diff --git a/.github/workflows/push-packages.yml b/.github/workflows/build.yml similarity index 59% rename from .github/workflows/push-packages.yml rename to .github/workflows/build.yml index 2f8ff1d8df6..085be6da793 100644 --- a/.github/workflows/push-packages.yml +++ b/.github/workflows/build.yml @@ -1,38 +1,39 @@ -name: Publish to GitHub Packages +name: Build and Test on: push jobs: - publish: + build: runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + java-version: [ 19, 20-ea ] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 with: - java-version: 11 + java-version: ${{ matrix.java-version }} distribution: temurin - name: Cache Maven artifacts - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Cache node - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: web-bundle/node key: ${{ runner.os }}-node-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os}}-node- - name: Cache node_modules - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: web-bundle/node_modules key: ${{ runner.os }}-node-${{ hashFiles('**/pom.xml', '**/package.json') }} restore-keys: | ${{ runner.os}}-node_modules- - - name: Build and publish package - run: | - mvn -B versions:set -DnewVersion=$GITHUB_SHA -DgenerateBackupPoms=false - mvn -B -DskipTests -Pskip-shaded-web-jar -Pskip-tools-jar source:jar deploy - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Build ${{ matrix.java-version }} + run: mvn -B clean test + diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml new file mode 100644 index 00000000000..83be4b2b42b --- /dev/null +++ b/.github/workflows/publish-github-packages.yml @@ -0,0 +1,49 @@ +name: Publish to GitHub Packages + +# ignore tags as the untagged commit has the same GITHUB_SHA and would conflict +on: + push: + branches: + - '**' + +jobs: + publish: + if: github.repository_owner == 'graphhopper' + runs-on: ubuntu-20.04 + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: temurin + - name: Cache Maven artifacts + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Cache node + uses: actions/cache@v3 + with: + path: web-bundle/node + key: ${{ runner.os }}-node-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os}}-node- + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: web-bundle/node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/pom.xml', '**/package.json') }} + restore-keys: | + ${{ runner.os}}-node_modules- + - name: Build and publish package ${{ matrix.java-version }} + # special 'wagon' option due to https://github.com/orgs/community/discussions/49001 + run: | + mvn -B versions:set -DnewVersion=$GITHUB_SHA -DgenerateBackupPoms=false + mvn -Dmaven.resolver.transport=wagon -B -DskipTests -Pskip-shaded-web-jar -Pskip-tools-jar source:jar deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-maven-central.yml b/.github/workflows/publish-maven-central.yml new file mode 100644 index 00000000000..74779486322 --- /dev/null +++ b/.github/workflows/publish-maven-central.yml @@ -0,0 +1,58 @@ +name: Publish to Maven Central + +on: + push: + tags: + - '**' + +jobs: + maven-central-publish: + if: github.repository_owner == 'graphhopper' + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: temurin + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + # feed directly as multi line value + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} + gpg-passphrase: GPG_PASSPHRASE + + - name: Cache Maven artifacts + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Cache node + uses: actions/cache@v3 + with: + path: web-bundle/node + key: ${{ runner.os }}-node-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os}}-node- + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: web-bundle/node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/pom.xml', '**/package.json') }} + restore-keys: | + ${{ runner.os}}-node_modules- + - name: clean install + run: | + mvn -Dkey=$API_KEY clean install -B + + - name: Build and publish package to maven central ${{ matrix.java-version }} + run: | + echo "release to maven central" + mvn -B versions:set -DnewVersion=$GITHUB_REF_NAME -DgenerateBackupPoms=false + mvn deploy -P release -DskipTests=true -Dpgp.secretkey=keyring:id=0E2FBADB -B + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.github/workflows/remove-old-artifacts.yml b/.github/workflows/remove-old-artifacts.yml index e41e91991eb..a52c80c4dc4 100644 --- a/.github/workflows/remove-old-artifacts.yml +++ b/.github/workflows/remove-old-artifacts.yml @@ -5,6 +5,7 @@ on: workflow_dispatch: jobs: remove-old-artifacts: + if: github.repository_owner == 'graphhopper' name: Remove old artifacts runs-on: ubuntu-latest timeout-minutes: 10 # stop the task if it takes longer diff --git a/.github/workflows/trigger-benchmarks.yml b/.github/workflows/trigger-benchmarks.yml new file mode 100644 index 00000000000..0aace67ba95 --- /dev/null +++ b/.github/workflows/trigger-benchmarks.yml @@ -0,0 +1,14 @@ +name: Trigger Benchmarks +on: push +jobs: + trigger_measurement: + if: github.repository_owner == 'graphhopper' + runs-on: ubuntu-22.04 + environment: benchmarks + steps: + - name: trigger + run: | + curl -X POST -H "Authorization: token ${{ secrets.BENCHMARKS_TOKEN }}" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${{ secrets.BENCHMARKS_REPO }}/dispatches" \ + -d '{"event_type":"measurement_core","client_payload":{"core_commit": "'$GITHUB_SHA'", "core_branch": "${{ github.ref_name }}" }}' diff --git a/.gitignore b/.gitignore index dc3d0209e7c..9e896cadab7 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,6 @@ android/.gradle local/ local.properties **/node_modules -*.zip .DS_Store /graph-cache package-lock.json \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index f65c8ef2d0a..00000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,36 +0,0 @@ -variables: - MAVEN_CLI_OPTS: "" - MAVEN_OPTS: "-Dmaven.repo.local=cache/.m2/repository" - -cache: - paths: - - cache/.m2/repository/ - -run_measurement: - stage: test - tags: - - openjdk11 - script: - # make this script exit if a command fails, a variable is missing etc. - - set -euo pipefail - - mvn -v - - java --version - - cat /proc/meminfo - - lscpu - - export BENCHMARK_DIR=/ext_data/ - - export BENCHMARK_RESULT_DIR=${BENCHMARK_DIR}results/$(date '+%d-%m-%Y-%s%N')/ - - export BENCHMARK_SMALL_MAP_NAME=bayern-190101.osm.pbf - - export BENCHMARK_SMALL_MAP_URL=http://download.geofabrik.de/europe/germany/${BENCHMARK_SMALL_MAP_NAME} - - export BENCHMARK_SMALL_MAP_PATH=${BENCHMARK_DIR}osm/${BENCHMARK_SMALL_MAP_NAME} - - export BENCHMARK_BIG_MAP_NAME=germany-190101.osm.pbf - - export BENCHMARK_BIG_MAP_URL=http://download.geofabrik.de/europe/${BENCHMARK_BIG_MAP_NAME} - - export BENCHMARK_BIG_MAP_PATH=${BENCHMARK_DIR}osm/${BENCHMARK_BIG_MAP_NAME} - - export USE_MEASUREMENT_TIME_AS_REF_TIME=${PERIODIC_BUILD:-false} - - mkdir -p ${BENCHMARK_DIR}osm - - mvn $MAVEN_CLI_OPTS package -DskipTests -pl tools -am - - chmod +x -R benchmark - - benchmark/download_map.sh $BENCHMARK_SMALL_MAP_URL $BENCHMARK_SMALL_MAP_PATH - - benchmark/download_map.sh $BENCHMARK_BIG_MAP_URL $BENCHMARK_BIG_MAP_PATH - - benchmark/benchmark.sh $BENCHMARK_DIR $BENCHMARK_RESULT_DIR $BENCHMARK_SMALL_MAP_PATH $BENCHMARK_BIG_MAP_PATH $USE_MEASUREMENT_TIME_AS_REF_TIME - - benchmark/post_benchmark.sh $BENCHMARK_RESULT_DIR $BENCHMARK_DB_USER $BENCHMARK_DB_PWD $BENCHMARK_DB_URL - - rm -rf $BENCHMARK_RESULT_DIR diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 998fc152d1f..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,63 +0,0 @@ -language: java -sudo: true -dist: xenial - -env: - global: - - GPG_EXECUTABLE=gpg - # TODO move this into the travis settings - - secure: "j6a61/qnfFcSjx5XxmxO2hqBOwtVx5HWrD1+4Atl7WG/pRKz9+jSga1Y7oDAFb2SIl8S65kDmPQB/vC8aHxUDj/Wizjxnxn1FhPqoe9yO6Ztft+984FKFyvj7s6tsBJKcehGec+chTOwZQpH4oI4rU6IlepDHnGLHiOd0Iviryg=" - - secure: "GiFr+v2lTQk/sTQB7CYjju1/mupS8LSJupmizLqY454utiZkabDMBOZQnF9ukpy7WhveB9hKQyEKf9iP2w7HSYEjgvogT26vZ5f2MeLnR4SWvqEtf/WBvvh+W+k/rb2f6YgitkB4Jlxn2izemBEDuKplGJphzGW41lf8XZ2IxVI=" - -matrix: - include: - - env: JDK='8/ga' # for backward compatibility - - env: JDK='17/ga'# latest LTS - - env: JDK='18/ga'# latest - - env: JDK='19/ea'# latest early access - -# avoid default dependency command for maven, 'true' means 'return true' and continue -install: true - -# store them into travis via https://dracoblue.net/dev/uploading-snapshots-and-releases-to-maven-central-with-travis/ -# gpg --export-secret-keys | base64 -w 0 -# gpg --export-ownertrust | base64 -w 0 -before_install: - - if [ ! -z "$GPG_SECRET_KEYS" ]; then echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import; fi - - if [ ! -z "$GPG_OWNERTRUST" ]; then echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust; fi - - wget -q "https://api.adoptium.net/v3/binary/latest/$JDK/linux/x64/jdk/hotspot/normal/eclipse" -O java.tar.gz - - tar xzf java.tar.gz - - export JAVA_HOME=$(ls -d $PWD/jdk*); echo $JAVA_HOME - -# Undo `_JAVA_OPTIONS` environment variable; see https://github.com/travis-ci/travis-ci/issues/8408 -before_script: - - _JAVA_OPTIONS= - - "mvn --version" - - "if [ -z \"$API_KEY\" ]; then API_KEY=78da6e9a-273e-43d1-bdda-8f24e007a1fa; fi" # change in GraphHopperWebIT too - -script: - - "mvn -Dkey=$API_KEY clean install -B" - -after_success: - # often spotbugs etc take long to be compatible with a future JDK version so skip them - - if [ "$SKIP_EXT_TESTS" != "true" ]; then - mvn checkstyle:check forbiddenapis:check; - fi - # if tagged deploy then release to maven central or deploy snapshot artifacts to sonatype - - if [ "$TRAVIS_JDK_VERSION" == "openjdk8" ] && [ "$TRAVIS_TAG" != "" ]; then - echo "release to maven central"; - mvn versions:set -DnewVersion=$TRAVIS_TAG -DgenerateBackupPoms=false; - mvn deploy -P release --settings core/files/settings.xml -DskipTests=true -B; - elif [ "$TRAVIS_JDK_VERSION" == "openjdk8" ] && [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then - mvn deploy --settings core/files/settings.xml -DskipTests=true -B; - else - echo "Not deploying artifacts for $TRAVIS_BRANCH"; - fi - -notifications: - email: - - $EMAIL - -cache: - directories: - - $HOME/.m2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 43a0f27111e..0dfb15cdada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ -### 6.0 [not yet released] -- don't allow cars or motorcycles to use ways tagged with service=emergency_access (#2484) +### 8.0 [not yet released] + +- renamed EdgeKVStorage to KVStorage as it is (temporarily) used for node tage too, see #2705 +- bike vehicles are now allowed to go in reverse direction of oneways, see custom_models/bike.json #196 + +### 7.0 [14 Mar 2023] + +- access node tags via List instead of Map: List> nodeTags = way.getTag("node_tags", emptyList()), see #2705 +- remove StringEncodedValue support from custom model due to insufficient usage/testing +- handle also node_tags in handleWayTags, when extending AbstractAccessParser call handleNodeTags, #2738 +- Format of 'areas' in CustomModel changed to 'FeatureCollection'. The old format is deprecated and will be removed in a later version, #2734 +- TagParser#handleWayTags no longer returns an IntsRef. We assume it never returned anything other than the input IntsRef. +- there is no longer a default value for the distanceInfluence parameter in custom models sent via client-hc. Previously it was 70. Not setting it explicitly now means the server-side value will be used. getDistanceInfluence can now be null. Server-side profiles with custom weighting now use distance_influence: 0 by default (previously it was 70). see #2716 +- there is a new, required 'import.osm.ignored_highways' configuration option that must be used to not increase the graph size and decrease performance for motorized-only routing compared to previous versions, #2702 +- new osm_way_id encoded value, #2701 +- the parameters vehicle, weighting, edge_based and turn_costs are no longer supported, use the profile parameter instead +- removed motorroad to road_class conversion, #2329 +- removed YAML support for custom models on the server-side. Only allow JSON with // comments. +- Bike2WeightTagParser was removed. Use the bike vehicle with a custom model, see custom_models/bike.json +- CurvatureWeighting was removed. Use a custom model with 'curvature' instead, see custom_models/curvature.json (#2665) +- internal keys for EdgeKVStorage changed to contain the street_ prefix like the path details too. Similarly, the + extra_info in the instructions of the API response, see #2661 +- subnetwork preparation can now be run in parallel to slightly speed up the base graph import (#2737) +- The block_area parameter was removed. Use custom model areas instead. + +### 6.0 [13 Sep 2022] + +- Car4WDTagParser was removed. Use the roads vehicle with a custom model, see custom_models/car4wd.json see #2651 +- When using a DecimalEncodedValue with useMaximumAsInfinity=true and a single bit of space make sure you always use + Double.POSITIVE_INFINITY to set the value, see #2646 +- renamed DouglasPeucker to RamerDouglasPeucker +- path details at via-points are no longer merged, see #2626 +- removed the FlagEncoder interface. for example encoder.getAccessEnc() is now encodingManager.getBooleanEncodedValue( + VehicleAccess.key("car")), #2611 +- backward incompatible change as instructions and the street_name path detail do no longer contain the ref #2598 +- StringIndex is now EdgeKVStorage and can store e.g. byte arrays. String values needs to be limited to 255 bytes before + storing them. See EdgeKVStorage.cutString and #2597. +- the Matrix client changed and users have to adapt the usage, see #2587 +- replaced car$access with car_access (and same for $average_speed and $priority) +- don't allow cars or motorcycles to use ways tagged with service=emergency_access (#2484) +- faster flexible routing, especially in conjunction with turn costs (#2571) +- negative OSM Ids are not supported any longer (#2652) +- new urban_density encoded value based on road density calculation (#2637) ### 5.0 [23 Mar 2022] @@ -105,7 +146,7 @@ - removed android demo, #1940 - added edge key path detail, #2073 - fixed bug for turn restrictions on bridges/tunnels, #2070 -- improved resolution of elevation profiles, 3D Douglas-Peucker and long edge sampling, #1953 +- improved resolution of elevation profiles, 3D Ramer-Douglas-Peucker and long edge sampling, #1953 ### 1.0 [22 May 2020] diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6133a0c6545..b6eae590ce6 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -4,11 +4,11 @@ Most of the contributors are mentioned at Github as [Members](https://github.com Here is an overview: + * agouge, discussion and API refactoring * AnahitaS, docs for Android, Android, Tomcat * andreaswolf, flag encoder versioning and more * andreylh, polygon for blocked area #1306 - * Anvoker, fixes like #1614 and helped with JUnit 5 migration #1632 - * agouge, discussion and API refactoring + * Anvoker, fixes like #1614 and helped with JUnit 5 migration #1632 * b3nn0, Android improvements * baumboi, path detail and landmark improvements * boldtrn, one of the core developers with motorcycle knowledge :) @@ -29,7 +29,7 @@ Here is an overview: * elibar, fix for alternative route calculation * fbonzon, several UI improvements like #615 * florent-morel, improvements regarding fords, #320 - * fredao, translations + * fredao, translations * gberaudo, improvements regarding elevation * GProbo, fixes like #2241 * HarelM, improvements regarding elevation @@ -48,6 +48,7 @@ Here is an overview: * kodonnell, adding support for CH and other algorithms (#60) and penalizing inner-link U-turns (#88) * legraina, improved docker for dockerhub * lmar, improved instructions + * lukasalexanderweber, helped to implement via-way restrictions #2689 and fixes like #2652 * matkoniecz, tweaking documentation * manueltimita, fixes like #1651 * mathstpierre, fixes like #1753 @@ -60,26 +61,28 @@ Here is an overview: * NopMap, massive improvements regarding OSM, parsing and encoding, route relations * ocampana, initial implementation for instructions * oflebbe, work on iOS port and issues like #2060 + * OlafFlebbeBosch, improvements like #2730 * oschlueter, fixes like #1185 * otbutz, added multiple EncodedValues * PGWelch, shapefile reader #874 + * rafaelstelles, fix deserializer web-api * rajanski, script to do routing via PostGIS * ratrun, route relations, GPX information, bike handling etc * rodneyodonnell, improved dead end removal (PrepareRoutingSubnetworks) and fords * rodo, more descriptions * rory, support milisecond gpx timestamps, see #4 + * samruston, improved point hint matching * seeebiii, motorcycle improvements * sguill, fixes like #1683 + * shunfan-shao, fix potential flaky tests * skienzl, imperial units for /navigate, related to #2071 * stefanholder, Stefan Holder, BMW AG, creating and integrating the hmm-lib (#49, #66, #69) and penalizing inner-link U-turns (#88, #91), refactored unfavoring of virtual edges #885 - * stevensnoeijen, fixes like #1568 + * stevensnoeijen, fixes like #1568 * Svantulden, improved documentation and nearest API * taulinger, hopefully more to come * thehereward, code cleanups like #620 * vvikas, ideas for many to many improvements and #616 * zstadler, multiple fixes and car4wd - * samruston, improved point hint matching - * shunfan-shao, fix potential flaky tests ## Translations diff --git a/NOTICE.md b/NOTICE.md index b2ff5f1b14c..6f4cbf8a318 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -2,9 +2,9 @@ GraphHopper licensed under the Apache license, Version 2.0 -Copyright 2012 - 2021 GraphHopper GmbH +Copyright 2012 - 2022 GraphHopper GmbH -The core module includes the following software: +The core module includes the following additional software: * slf4j.org - SLF4J distributed under the MIT license. * com.carrotsearch:hppc (Apache license) @@ -14,32 +14,36 @@ The core module includes the following software: * Apache Commons Lang - we copied the implementation of the Levenshtein Distance (Apache License) * Apache Commons Collections - we copied parts of the BinaryHeap (Apache License) * java-string-similarity - we copied the implementation of JaroWinkler (MIT license) - * com.fasterxml.jackson.core:jackson-annotations (Apache License) + * Jackson (Apache License) * org.locationtech:jts (EDL), see #1039 * AngleCalc.atan2 from Jim Shima, 1999 (public domain) - * list of Java keywords in EncodingManager from janino compiler (BSD-3-Clause license) + * janino compiler (BSD-3-Clause license) * protobuf - New BSD license * OSM-binary - LGPL license - * Osmosis - public domain, see osmosis-copying.txt under core/files + * Osmosis - public domain, see their github under package/copying.txt reader-gtfs: * some files from com.conveyal:gtfs-lib (BSD 2-clause license) * com.google.transit:gtfs-realtime-bindings (Apache license) - -reader-shp: - - * org.geotools:gt-shapefile (LGPL) + * com.google.guava:guava (Apache license) + * net.sourceforge.javacsv:javacsv (LGPL license) + * commons-io:commons-io (Apache license) + * org.mapdb:mapdb (Apache license) tools: * uses Apache Compress (Apache license) +client-hc: + + * okhttp (Apache license) + web: * org.eclipse.jetty:jetty-server (Apache License) - * com.fasterxml.jackson.core:jackson-databind (Apache license) - * com.google.inject (Apache license) + * Dropwizard and dependencies (Apache license) + * no.ecc:java-vector-tile (Apache license) * some images from mapbox https://www.mapbox.com/maki/, BSD License, see core/files ## Data @@ -48,7 +52,7 @@ web: |---------|-----------|---------|------| |OpenStreetMap data for the road network | [ODBL](https://www.openstreetmap.org/copyright) | yes | yes | SRTM elevation | [public domain](https://www2.jpl.nasa.gov/srtm/), [acknowledgement](https://lpdaac.usgs.gov/citing_our_data) | no | yes -| CGIAR elevation | [allowed usage for GraphHopper](https://graphhopper.com/public/license/CGIAR.txt) | no | no +| CGIAR elevation | [allowed usage for GraphHopper](https://gist.githubusercontent.com/karussell/4b54a289041ee48a16c00fd4e30e21b8/raw/45edf8ae85322cb20976baa30654093d0ca9bcd8/CGIAR.txt) | no | no | SRTMGL1 elevation | [acknowledgement](https://lpdaac.usgs.gov/citing_our_data) | no | no |OpenTopography mirror for SRTMGL1 | [acknowledgement OpenTopoGraphy](http://www.opentopography.org/citations) and [data source](http://opentopo.sdsc.edu/datasetMetadata?otCollectionID=OT.042013.4326.1) + SRTMGL1 | no | no | GMTED | [public domain, acknowledgment](https://lta.cr.usgs.gov/citation) | no | no diff --git a/README.md b/README.md index 740d43888b3..7c5f3567da8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # GraphHopper Routing Engine -[![Build Status](https://secure.travis-ci.org/graphhopper/graphhopper.png?branch=master)](http://travis-ci.org/graphhopper/graphhopper) +![Build Status](https://github.com/graphhopper/graphhopper/actions/workflows/build.yml/badge.svg?branch=master) GraphHopper is a fast and memory-efficient routing engine released under Apache License 2.0. It can be used as a Java library or standalone web server to calculate the distance, time, turn-by-turn instructions and many road attributes for a route between two or more points. Beyond this "A-to-B" routing it supports ["snap to road"](README.md#Map-Matching), [Isochrone calculation](README.md#Analysis), [mobile navigation](README.md#mobile-apps) and [more](README.md#Features). GraphHopper uses OpenStreetMap and GTFS data by default and it can import [other data sources too](README.md#OpenStreetMap-Support). @@ -22,14 +22,20 @@ We even have [good first issues](https://github.com/graphhopper/graphhopper/issu To get started you can try [GraphHopper Maps](README.md#graphhopper-maps), read through our documentation and install the GraphHopper Web Service locally. -* 5.x: [documentation](https://github.com/graphhopper/graphhopper/blob/5.x/docs/index.md) - , [web service jar](https://github.com/graphhopper/graphhopper/releases/download/5.1/graphhopper-web-5.1.jar) - , [announcement](https://www.graphhopper.com/blog/2022/03/23/graphhopper-routing-engine-5-0-released/) +* 7.x: [documentation](https://github.com/graphhopper/graphhopper/blob/7.x/docs/index.md) + , [web service jar](https://repo1.maven.org/maven2/com/graphhopper/graphhopper-web/7.0/graphhopper-web-7.0.jar) + , [announcement](https://www.graphhopper.com/blog/2023/03/14/graphhopper-routing-engine-7-0-released/) * unstable master: [documentation](https://github.com/graphhopper/graphhopper/blob/master/docs/index.md)
Click to see older releases * See our [changelog file](./CHANGELOG.md) for Java API Changes. +* 6.x: [documentation](https://github.com/graphhopper/graphhopper/blob/6.x/docs/index.md) + , [web service jar](https://repo1.maven.org/maven2/com/graphhopper/graphhopper-web/6.2/graphhopper-web-6.2.jar) + , [announcement](https://www.graphhopper.com/blog/2022/09/19/graphhopper-routing-engine-6-0-released/) +* 5.x: [documentation](https://github.com/graphhopper/graphhopper/blob/5.x/docs/index.md) + , [web service jar](https://github.com/graphhopper/graphhopper/releases/download/5.3/graphhopper-web-5.3.jar) + , [announcement](https://www.graphhopper.com/blog/2022/03/23/graphhopper-routing-engine-5-0-released/) * 4.x: [documentation](https://github.com/graphhopper/graphhopper/blob/4.x/docs/index.md) , [web service jar](https://github.com/graphhopper/graphhopper/releases/download/4.0/graphhopper-web-4.0.jar) , [announcement](https://www.graphhopper.com/blog/2021/09/29/graphhopper-routing-engine-4-0-released/) @@ -75,17 +81,17 @@ To get started you can try [GraphHopper Maps](README.md#graphhopper-maps), read ## Installation -To install the [GraphHopper Maps](https://graphhopper.com/maps/) UI and the web service locally you [need a JVM](https://adoptopenjdk.net/) (>= Java 8) and do: +To install the [GraphHopper Maps](https://graphhopper.com/maps/) UI and the web service locally you [need a JVM](https://adoptium.net) (>= Java 8) and do: ```bash -wget https://github.com/graphhopper/graphhopper/releases/download/5.1/graphhopper-web-5.1.jar https://raw.githubusercontent.com/graphhopper/graphhopper/5.x/config-example.yml http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf -java -Ddw.graphhopper.datareader.file=berlin-latest.osm.pbf -jar *.jar server config-example.yml +wget https://repo1.maven.org/maven2/com/graphhopper/graphhopper-web/7.0/graphhopper-web-7.0.jar https://raw.githubusercontent.com/graphhopper/graphhopper/7.x/config-example.yml http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf +java -D"dw.graphhopper.datareader.file=berlin-latest.osm.pbf" -jar graphhopper*.jar server config-example.yml ``` After a while you see a log message with 'Server - Started', then go to http://localhost:8989/ and you'll see a map of Berlin. You should be able to right click on the map to create a route. -For more details about the installation, see [here](./docs/web/quickstart.md). +See the [documentation](./docs/index.md) that contains e.g. [the elevation guide](./docs/core/elevation.md) and the [deployment guide](./docs/core/deploy.md). ### Docker @@ -96,10 +102,11 @@ The Docker images created by the community from the `master` branch can be found To see the road routing feature of GraphHopper in action please go to [GraphHopper Maps](https://graphhopper.com/maps). -[![GraphHopper Maps](https://karussell.files.wordpress.com/2014/12/graphhopper-maps-0-4-preview.png)](https://graphhopper.com/maps) +[![GraphHopper Maps](https://www.graphhopper.com/wp-content/uploads/2022/10/maps2-1024x661.png)](https://graphhopper.com/maps) + +GraphHopper Maps is an open source user interface, which you can find [here](https://github.com/graphhopper/graphhopper-maps). It can use this open source routing engine or the [GraphHopper Directions API](https://www.graphhopper.com), which provides the Routing API, a Route Optimization API (based on [jsprit](http://jsprit.github.io/)), a fast Matrix API and an address search (based on [photon](https://github.com/komoot/photon)). The photon project is also supported by the GraphHopper GmbH. Additionally to the GraphHopper Directions API, map tiles from various providers are used where the default is [Omniscale](http://omniscale.com/). -GraphHopper Maps uses the commercial offering the [GraphHopper Directions API](https://www.graphhopper.com) under the hood, which provides the Routing API (based on this routing engine), a Route Optimization API based on [jsprit](http://jsprit.github.io/), a fast Matrix API and an address search based on [photon](https://github.com/komoot/photon). The photon project is also supported by the GraphHopper GmbH. Additionally to the GraphHopper Directions API, map tiles from various providers are used -where the default is [Omniscale](http://omniscale.com/). All this is available for free, via encrypted connections and from German servers for a nice and private route planning experience! +All this is available for free, via encrypted connections and from German servers for a nice and private route planning experience! ## Public Transit @@ -229,7 +236,7 @@ Here is a list of the more detailed features: * Memory efficient data structures, algorithms and [the low and high level API](./docs/core/low-level-api.md) is tuned towards ease of use and efficiency * Provides a simple [web API](./docs/web/api-doc.md) including JavaScript and Java clients * Multiple weightings (fastest/shortest/custom/...) and pre-built routing profiles: car, bike, racing bike, mountain bike, foot, hike, motorcycle, wheelchair, ... - * [Customization of these profiles](./docs/core/profiles.md#custom-profiles) are possible to get truck and cargo bike support or individual improvements + * [Customization of these profiles](./docs/core/profiles.md#custom-profiles) are possible and e.g. get truck routing or support for cargo bikes and [many other changes](https://www.graphhopper.com/blog/2020/05/31/examples-for-customizable-routing/) * Does [map matching](./map-matching) * Supports public transit routing and [GTFS](./reader-gtfs/README.md). * Offers turn instructions in more than 42 languages, contribute or improve [here](./docs/core/translations.md) diff --git a/benchmark/benchmark.sh b/benchmark/benchmark.sh index 485c91c02bf..7f35dab7edc 100755 --- a/benchmark/benchmark.sh +++ b/benchmark/benchmark.sh @@ -1,10 +1,11 @@ #!/bin/bash # usage: -# benchmark/benchmark.sh +# benchmark/benchmark.sh # # where: -# = base directory used to store results -# = name of directory where results of this run are stored (inside ) +# = name of directory used to store graphs +# = name of directory where results of this run are stored +# = name of directory where the summary file is stored # = path to osm map the measurement is run on for slow measurements # = path to osm map the measurement is run on for fast measurements # = true/false, false by default, meaning the git commit time will be used as reference @@ -14,79 +15,81 @@ set -euo pipefail # print all commands set -o xtrace -defaultDataDir=measurements/ -defaultSingleResultsDir=measurements/results/$(date '+%d-%m-%Y-%s%N')/ +defaultGraphDir=measurements/ +defaultResultsDir=measurements/results/$(date '+%d-%m-%Y-%s%N')/ +defaultSummaryDir=measurements/ defaultSmallMap=core/files/andorra.osm.pbf defaultBigMap=core/files/andorra.osm.pbf defaultUseMeasurementTimeAsRefTime=false -# this is the directory where we read/write data from/to -DATA_DIR=${1:-$defaultDataDir} -RESULTS_DIR=${DATA_DIR}results/ -TMP_DIR=${DATA_DIR}tmp/ -SINGLE_RESULTS_DIR=${2:-$defaultSingleResultsDir} -SMALL_OSM_MAP=${3:-$defaultSmallMap} -BIG_OSM_MAP=${4:-$defaultBigMap} -USE_MEASUREMENT_TIME_AS_REF_TIME=${5:-$defaultUseMeasurementTimeAsRefTime} +GRAPH_DIR=${1:-$defaultGraphDir} +RESULTS_DIR=${2:-$defaultResultsDir} +SUMMARY_DIR=${3:-$defaultSummaryDir} +SMALL_OSM_MAP=${4:-$defaultSmallMap} +BIG_OSM_MAP=${5:-$defaultBigMap} +USE_MEASUREMENT_TIME_AS_REF_TIME=${6:-$defaultUseMeasurementTimeAsRefTime} # create directories -mkdir -p ${DATA_DIR} +mkdir -p ${GRAPH_DIR} mkdir -p ${RESULTS_DIR} -mkdir -p ${TMP_DIR} -mkdir -p ${SINGLE_RESULTS_DIR} +mkdir -p ${SUMMARY_DIR} # actually run the benchmarks: -echo "1 - small map: node- and edge-based CH + slow routing" +echo "1 - small map: node- and edge-based CH + landmarks (edge- & node-based for LM) + slow routing" java -cp tools/target/graphhopper-tools-*-jar-with-dependencies.jar \ -XX:+UseParallelGC -Xmx20g -Xms20g \ com.graphhopper.tools.Measurement \ datareader.file=${SMALL_OSM_MAP} \ datareader.date_range_parser_day=2019-11-01 \ measurement.name=small_map \ -measurement.folder=${SINGLE_RESULTS_DIR} \ +measurement.folder=${RESULTS_DIR} \ measurement.clean=true \ measurement.stop_on_error=true \ -measurement.summaryfile=${RESULTS_DIR}summary_small.dat \ +measurement.summaryfile=${SUMMARY_DIR}summary_small.dat \ measurement.repeats=1 \ measurement.run_slow_routing=true \ measurement.weighting=fastest \ measurement.ch.node=true \ measurement.ch.edge=true \ -measurement.lm=false \ -"graph.flag_encoders=car|turn_costs=true" \ -graph.location=${TMP_DIR}measurement-small-gh \ +measurement.lm=true \ +"measurement.lm.active_counts=[4,8,12]" \ +measurement.lm.edge_based=true \ +measurement.vehicle=car \ +import.osm.ignored_highways=footway,cycleway,path,pedestrian,bridleway \ +measurement.turn_costs=true \ +graph.location=${GRAPH_DIR}measurement-small-gh \ prepare.min_network_size=10000 \ measurement.json=true \ measurement.count=5000 \ -measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} \ -"measurement.block_area=49.394664,11.144428,49.348388,11.144943,49.355768,11.227169,49.411643,11.227512" +measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} -echo "2 - big map: node-based CH + landmarks (edge- & node-based for LM)" +echo "2 - big map: node-based CH + landmarks (edge- & node-based for LM) + slow routing" java -cp tools/target/graphhopper-tools-*-jar-with-dependencies.jar \ -XX:+UseParallelGC -Xmx20g -Xms20g \ com.graphhopper.tools.Measurement \ datareader.file=${BIG_OSM_MAP} \ datareader.date_range_parser_day=2019-11-01 \ measurement.name=big_map \ -measurement.folder=${SINGLE_RESULTS_DIR} \ +measurement.folder=${RESULTS_DIR} \ measurement.clean=true \ measurement.stop_on_error=true \ -measurement.summaryfile=${RESULTS_DIR}summary_big.dat \ +measurement.summaryfile=${SUMMARY_DIR}summary_big.dat \ measurement.repeats=1 \ -measurement.run_slow_routing=false \ +measurement.run_slow_routing=true \ measurement.weighting=fastest \ measurement.ch.node=true \ measurement.ch.edge=false \ measurement.lm=true \ -"measurement.lm.active_counts=[4,8,12,16]" \ +"measurement.lm.active_counts=[4,8,12]" \ measurement.lm.edge_based=true \ -"graph.flag_encoders=car|turn_costs=true" \ -graph.location=${TMP_DIR}measurement-big-gh \ +measurement.vehicle=car \ +import.osm.ignored_highways=footway,cycleway,path,pedestrian,bridleway \ +measurement.turn_costs=true \ +graph.location=${GRAPH_DIR}measurement-big-gh \ prepare.min_network_size=10000 \ measurement.json=true \ measurement.count=5000 \ -measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} \ -"measurement.block_area=49.394664,11.144428,49.348388,11.144943,49.355768,11.227169,49.411643,11.227512" +measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} echo "3 - big map with a custom model that is 'a little customized', i.e. similar to the standard fastest-car profile" echo "node-based CH + LM" @@ -96,27 +99,28 @@ com.graphhopper.tools.Measurement \ datareader.file=${BIG_OSM_MAP} \ datareader.date_range_parser_day=2019-11-01 \ measurement.name=big_map_little_custom \ -measurement.folder=${SINGLE_RESULTS_DIR} \ +measurement.folder=${RESULTS_DIR} \ measurement.clean=true \ measurement.stop_on_error=true \ -measurement.summaryfile=${RESULTS_DIR}summary_big_little_custom.dat \ +measurement.summaryfile=${SUMMARY_DIR}summary_big_little_custom.dat \ measurement.repeats=1 \ measurement.run_slow_routing=false \ measurement.weighting=custom \ -measurement.custom_model_file=benchmark/little_custom.yml \ +measurement.custom_model_file=benchmark/little_custom.json \ graph.encoded_values=max_width,max_height,toll,hazmat \ measurement.ch.node=true \ measurement.ch.edge=false \ measurement.lm=true \ "measurement.lm.active_counts=[8]" \ measurement.lm.edge_based=false \ -"graph.flag_encoders=car|turn_costs=true" \ -graph.location=${TMP_DIR}measurement-big-little-custom-gh \ +measurement.vehicle=car \ +import.osm.ignored_highways=footway,cycleway,path,pedestrian,bridleway \ +measurement.turn_costs=true \ +graph.location=${GRAPH_DIR}measurement-big-little-custom-gh \ prepare.min_network_size=10000 \ measurement.json=true \ measurement.count=5000 \ -measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} \ -"measurement.block_area=49.394664,11.144428,49.348388,11.144943,49.355768,11.227169,49.411643,11.227512" +measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} echo "4 - big map with a custom model that is 'very customized', i.e. has many custom weighting rules" echo "node-based CH + LM" @@ -126,27 +130,28 @@ com.graphhopper.tools.Measurement \ datareader.file=${BIG_OSM_MAP} \ datareader.date_range_parser_day=2019-11-01 \ measurement.name=big_map_very_custom \ -measurement.folder=${SINGLE_RESULTS_DIR} \ +measurement.folder=${RESULTS_DIR} \ measurement.clean=true \ measurement.stop_on_error=true \ -measurement.summaryfile=${RESULTS_DIR}summary_big_very_custom.dat \ +measurement.summaryfile=${SUMMARY_DIR}summary_big_very_custom.dat \ measurement.repeats=1 \ measurement.run_slow_routing=false \ measurement.weighting=custom \ -measurement.custom_model_file=benchmark/very_custom.yml \ +measurement.custom_model_file=benchmark/very_custom.json \ graph.encoded_values=max_width,max_height,toll,hazmat \ measurement.ch.node=true \ measurement.ch.edge=false \ measurement.lm=true \ "measurement.lm.active_counts=[8]" \ measurement.lm.edge_based=false \ -"graph.flag_encoders=car|turn_costs=true" \ -graph.location=${TMP_DIR}measurement-big-very-custom-gh \ +measurement.vehicle=car \ +import.osm.ignored_highways=footway,cycleway,path,pedestrian,bridleway \ +measurement.turn_costs=true \ +graph.location=${GRAPH_DIR}measurement-big-very-custom-gh \ prepare.min_network_size=10000 \ measurement.json=true \ measurement.count=5000 \ -measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} \ -"measurement.block_area=49.394664,11.144428,49.348388,11.144943,49.355768,11.227169,49.411643,11.227512" +measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} echo "5 - big map, outdoor: node-based CH + landmarks (edge- & node-based for LM)" java -cp tools/target/graphhopper-tools-*-jar-with-dependencies.jar \ @@ -155,22 +160,23 @@ com.graphhopper.tools.Measurement \ datareader.file=${BIG_OSM_MAP} \ datareader.date_range_parser_day=2019-11-01 \ measurement.name=big_map_outdoor \ -measurement.folder=${SINGLE_RESULTS_DIR} \ +measurement.folder=${RESULTS_DIR} \ measurement.clean=true \ measurement.stop_on_error=true \ -measurement.summaryfile=${RESULTS_DIR}summary_big_outdoor.dat \ +measurement.summaryfile=${SUMMARY_DIR}summary_big_outdoor.dat \ measurement.repeats=1 \ measurement.run_slow_routing=false \ measurement.weighting=fastest \ measurement.ch.node=true \ measurement.ch.edge=false \ measurement.lm=true \ -"measurement.lm.active_counts=[4,8,12,16]" \ +"measurement.lm.active_counts=[4,8,12]" \ measurement.lm.edge_based=false \ -"graph.flag_encoders=foot" \ -graph.location=${TMP_DIR}measurement-big-outdoor-gh \ +measurement.vehicle=foot \ +import.osm.ignored_highways= \ +measurement.turn_costs=false \ +graph.location=${GRAPH_DIR}measurement-big-outdoor-gh \ prepare.min_network_size=10000 \ measurement.json=true \ measurement.count=5000 \ -measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} \ -"measurement.block_area=49.394664,11.144428,49.348388,11.144943,49.355768,11.227169,49.411643,11.227512" \ No newline at end of file +measurement.use_measurement_time_as_ref_time=${USE_MEASUREMENT_TIME_AS_REF_TIME} \ No newline at end of file diff --git a/benchmark/little_custom.json b/benchmark/little_custom.json new file mode 100644 index 00000000000..1b548fecdd9 --- /dev/null +++ b/benchmark/little_custom.json @@ -0,0 +1,15 @@ +// this is a little customized model used to benchmark routing with a customized weighting that is similar to the +// standard car profile +{ + "priority": [ + { + "if": "road_access == PRIVATE", + "multiply_by": "0.1" + }, + { + "else_if": "road_access == DESTINATION", + "multiply_by": "0.1" + } + ], + "distance_influence": 0 +} diff --git a/benchmark/little_custom.yml b/benchmark/little_custom.yml deleted file mode 100644 index e6a5230e20c..00000000000 --- a/benchmark/little_custom.yml +++ /dev/null @@ -1,9 +0,0 @@ -# this is a little customized model used to benchmark routing with a customized weighting that is similar to the -# standard car profile -priority: - - if: road_access == PRIVATE - multiply_by: 0.1 - - else_if: road_access == DESTINATION - multiply_by: 0.1 - -distance_influence: 0 diff --git a/benchmark/post_benchmark.sh b/benchmark/post_benchmark.sh index ea712d038f5..4b2cb657044 100755 --- a/benchmark/post_benchmark.sh +++ b/benchmark/post_benchmark.sh @@ -7,7 +7,7 @@ # : = credentials for basic auth # = url the results are posted to -# make this script exit if a command faiils, a variable is missing etc. +# make this script exit if a command fails, a variable is missing etc. set -euo pipefail # use curl to post results (requires external variables to be set) diff --git a/benchmark/very_custom.json b/benchmark/very_custom.json new file mode 100644 index 00000000000..933cb2a2e05 --- /dev/null +++ b/benchmark/very_custom.json @@ -0,0 +1,43 @@ +// this is a heavily customized model used to benchmark routing with a custom weighting +{ + "speed": [ + { + "if": "road_class == MOTORWAY", + "multiply_by": "0.85" + }, + { + "else_if": "road_class == PRIMARY", + "multiply_by": "0.9" + }, + { + "if": "true", + "limit_to": "110" + } + ], + "priority": [ + { + "if": "max_height < 3.8", + "multiply_by": "0" + }, + { + "if": "max_width < 2.5", + "multiply_by": "0" + }, + { + "if": "road_class == PRIMARY", + "multiply_by": "0.5" + }, + { + "else_if": "road_class != MOTORWAY", + "multiply_by": "0.9" + }, + { + "if": "toll != NO", + "multiply_by": "0.5" + }, + { + "if": "hazmat == NO", + "multiply_by": "0" + } + ] +} \ No newline at end of file diff --git a/benchmark/very_custom.yml b/benchmark/very_custom.yml deleted file mode 100644 index 0234ad30c13..00000000000 --- a/benchmark/very_custom.yml +++ /dev/null @@ -1,24 +0,0 @@ -# this is a heavily customized model used to benchmark routing with a custom weighting -speed: - - if: road_class == MOTORWAY - multiply_by: 0.85 - - else_if: road_class == PRIMARY - multiply_by: 0.9 - - if: true - limit_to: 110 - -priority: - - if: max_height < 3.8 - multiply_by: 0 - - if: max_width < 2.5 - multiply_by: 0 - - - if: road_class == PRIMARY - multiply_by: 0.5 - - else_if: road_class != MOTORWAY - multiply_by: 0.9 - - - if: toll != NO - multiply_by: 0.5 - - if: hazmat == NO - multiply_by: 0 diff --git a/client-hc/pom.xml b/client-hc/pom.xml index aa3fbe57087..49782db81a8 100644 --- a/client-hc/pom.xml +++ b/client-hc/pom.xml @@ -40,7 +40,7 @@ com.squareup.okhttp3 okhttp - 3.14.9 + 4.9.3 org.slf4j @@ -52,4 +52,24 @@ test + + + + + + src/main/resources + true + + **/version + + + + src/main/resources + false + + **/version + + + + diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMRequest.java b/client-hc/src/main/java/com/graphhopper/api/GHMRequest.java index 8f33406c502..3868f9a7cf9 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GHMRequest.java +++ b/client-hc/src/main/java/com/graphhopper/api/GHMRequest.java @@ -1,90 +1,55 @@ package com.graphhopper.api; +import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonProperty; -import com.graphhopper.GHRequest; -import com.graphhopper.util.Helper; +import com.graphhopper.util.PMap; import com.graphhopper.util.shapes.GHPoint; -import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; import java.util.List; -import java.util.Set; /** * @author Peter Karich */ -public class GHMRequest extends GHRequest { - +public class GHMRequest { + private String profile; + private List points; private List fromPoints; private List toPoints; + + private List pointHints; private List fromPointHints; private List toPointHints; + + private List curbsides; private List fromCurbsides; private List toCurbsides; - private int called = 0; - boolean identicalLists = true; - private final Set outArrays = new HashSet<>(5); - private boolean failFast = true; - public GHMRequest() { - this(10); - } - - public GHMRequest(int size) { - super(0); - fromPoints = new ArrayList<>(size); - toPoints = new ArrayList<>(size); - fromPointHints = new ArrayList<>(size); - toPointHints = new ArrayList<>(size); - fromCurbsides = new ArrayList<>(size); - toCurbsides = new ArrayList<>(size); - } + private List snapPreventions; + private final PMap hints = new PMap(); + private List outArrays = Collections.EMPTY_LIST; + private boolean failFast = true; - /** - * Currently: weights, times, distances and paths possible. Where paths is - * the most expensive to calculate and limited to maximum 10*10 points (via - * API end point). - */ - public GHMRequest addOutArray(String type) { - outArrays.add(type); + public GHMRequest setProfile(String profile) { + this.profile = profile; return this; } - public Set getOutArrays() { - return outArrays; + public String getProfile() { + return profile; } - public GHMRequest addAllPoints(List points) { - for (GHPoint p : points) { - addPoint(p); - } + public GHMRequest setPoints(List points) { + this.points = points; return this; } - /** - * This methods adds the coordinate as 'from' and 'to' to the request. - */ - @Override - public GHMRequest addPoint(GHPoint point) { - fromPoints.add(point); - toPoints.add(point); - return this; - } - - @Override public List getPoints() { - throw new IllegalStateException("use getFromPoints or getToPoints"); - } - - public GHMRequest addFromPoint(GHPoint point) { - fromPoints.add(point); - identicalLists = false; - return this; + return points; } - public GHMRequest setFromPoints(List points) { - fromPoints = points; - identicalLists = false; + public GHMRequest setFromPoints(List fromPoints) { + this.fromPoints = fromPoints; return this; } @@ -92,15 +57,8 @@ public List getFromPoints() { return fromPoints; } - public GHMRequest addToPoint(GHPoint point) { - toPoints.add(point); - identicalLists = false; - return this; - } - - public GHMRequest setToPoints(List points) { - toPoints = points; - identicalLists = false; + public GHMRequest setToPoints(List toPoints) { + this.toPoints = toPoints; return this; } @@ -108,26 +66,17 @@ public List getToPoints() { return toPoints; } - @Override - public GHRequest setPointHints(List pointHints) { - setToPointHints(pointHints); - this.fromPointHints = this.toPointHints; + public GHMRequest setPointHints(List pointHints) { + this.pointHints = pointHints; return this; } - @Override public List getPointHints() { - throw new IllegalStateException("Use getFromPointHints or getToPointHints"); - } - - public GHRequest addFromPointHint(String pointHint) { - this.fromPointHints.add(pointHint); - return this; + return pointHints; } - public GHRequest setFromPointHints(List pointHints) { - // create new array as we modify pointHints in compactPointHints - this.fromPointHints = new ArrayList<>(pointHints); + public GHMRequest setFromPointHints(List fromPointHints) { + this.fromPointHints = fromPointHints; return this; } @@ -135,14 +84,8 @@ public List getFromPointHints() { return fromPointHints; } - public GHRequest addToPointHint(String pointHint) { - this.toPointHints.add(pointHint); - return this; - } - - public GHRequest setToPointHints(List pointHints) { - // create new array as we modify pointHints in compactPointHints - this.toPointHints = new ArrayList<>(pointHints); + public GHMRequest setToPointHints(List toPointHints) { + this.toPointHints = toPointHints; return this; } @@ -150,13 +93,17 @@ public List getToPointHints() { return toPointHints; } - public GHMRequest addFromCurbside(String curbside) { - fromCurbsides.add(curbside); + public GHMRequest setCurbsides(List curbsides) { + this.curbsides = curbsides; return this; } - public GHMRequest setFromCurbsides(List curbsides) { - fromCurbsides = curbsides; + public List getCurbsides() { + return curbsides; + } + + public GHMRequest setFromCurbsides(List fromCurbsides) { + this.fromCurbsides = fromCurbsides; return this; } @@ -164,30 +111,45 @@ public List getFromCurbsides() { return fromCurbsides; } - public GHMRequest addToCurbside(String curbside) { - toCurbsides.add(curbside); + public GHMRequest setToCurbsides(List toCurbsides) { + this.toCurbsides = toCurbsides; return this; } - public GHMRequest setToCurbsides(List curbsides) { - toCurbsides = curbsides; + public List getToCurbsides() { + return toCurbsides; + } + + public GHMRequest setSnapPreventions(List snapPreventions) { + this.snapPreventions = snapPreventions; return this; } - public List getToCurbsides() { - return toCurbsides; + public List getSnapPreventions() { + return snapPreventions; } - @Override - public GHRequest setCurbsides(List curbsides) { - fromCurbsides = curbsides; - toCurbsides = curbsides; + // a good trick to serialize unknown properties into the HintsMap + @JsonAnySetter + public GHMRequest putHint(String fieldName, Object value) { + hints.putObject(fieldName, value); return this; } - @Override - public List getCurbsides() { - throw new IllegalStateException("Use getFromCurbsides or getToCurbsides"); + public PMap getHints() { + return hints; + } + + /** + * Possible values are 'weights', 'times', 'distances' + */ + public GHMRequest setOutArrays(List outArrays) { + this.outArrays = outArrays; + return this; + } + + public List getOutArrays() { + return outArrays; } /** @@ -202,32 +164,4 @@ public boolean getFailFast() { return failFast; } - /** - * This method clears the point hint lists if they only contain empty values. Often point hints are added although the - * strings are empty. But because they could be used as placeholder we do not know earlier if they are meaningless. - */ - void compactPointHints() { - if (called > 0) - throw new IllegalStateException("cannot call more than once"); - called++; - boolean clear = true; - for (String hint : toPointHints) { - if (!Helper.isEmpty(hint)) { - clear = false; - break; - } - } - if (clear) - toPointHints.clear(); - - clear = true; - for (String hint : fromPointHints) { - if (!Helper.isEmpty(hint)) { - clear = false; - break; - } - } - if (clear) - fromPointHints.clear(); - } } diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java b/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java index f90e0375425..fe9e2297a52 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java +++ b/client-hc/src/main/java/com/graphhopper/api/GHMatrixAbstractRequester.java @@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit; import static com.graphhopper.api.GraphHopperMatrixWeb.*; +import static com.graphhopper.api.GraphHopperWeb.X_GH_CLIENT_VERSION; /** * @author Peter Karich @@ -79,17 +80,30 @@ public OkHttpClient getDownloader() { return downloader; } - protected Collection createOutArrayList(GHMRequest ghRequest) { - return ghRequest.getOutArrays().isEmpty() ? Collections.singletonList("weights") : ghRequest.getOutArrays(); - } + protected JsonNode createPostRequest(GHMRequest ghRequest) { + if (ghRequest.getHints().getObject("profile", null) != null) + throw new IllegalArgumentException("use setProfile instead of hint 'profile'"); + if (ghRequest.getProfile() == null) + throw new IllegalArgumentException("profile cannot be empty"); + if (ghRequest.getHints().getObject("fail_fast", null) != null) + throw new IllegalArgumentException("use setFailFast instead of hint 'fail_fast'"); - protected JsonNode createPostRequest(GHMRequest ghRequest, Collection outArraysList) { ObjectNode requestJson = objectMapper.createObjectNode(); - if (ghRequest.identicalLists) { - putPoints(requestJson, "points", ghRequest.getFromPoints()); - putStrings(requestJson, "point_hints", ghRequest.getFromPointHints()); - putStrings(requestJson, "curbsides", ghRequest.getFromCurbsides()); + if (ghRequest.getPoints() != null) { + if (ghRequest.getFromPoints() != null) + throw new IllegalArgumentException("if points are set do not use setFromPoints"); + if (ghRequest.getToPoints() != null) + throw new IllegalArgumentException("if points are set do not use setToPoints"); + + putPoints(requestJson, "points", ghRequest.getPoints()); + putStrings(requestJson, "point_hints", ghRequest.getPointHints()); + putStrings(requestJson, "curbsides", ghRequest.getCurbsides()); } else { + if (ghRequest.getFromPoints() == null) + throw new IllegalArgumentException("if points are not set you have to use setFromPoints but was null"); + if (ghRequest.getToPoints() == null) + throw new IllegalArgumentException("if points are not set you have to use setToPoints but was null"); + putPoints(requestJson, "from_points", ghRequest.getFromPoints()); putStrings(requestJson, "from_point_hints", ghRequest.getFromPointHints()); @@ -101,9 +115,9 @@ protected JsonNode createPostRequest(GHMRequest ghRequest, Collection ou } putStrings(requestJson, "snap_preventions", ghRequest.getSnapPreventions()); - putStrings(requestJson, "out_arrays", outArraysList); - // requestJson.put("elevation", ghRequest.getHints().getBool("elevation", false)); + putStrings(requestJson, "out_arrays", ghRequest.getOutArrays()); requestJson.put("fail_fast", ghRequest.getFailFast()); + requestJson.put("profile", ghRequest.getProfile()); Map hintsMap = ghRequest.getHints().toMap(); for (String hintKey : hintsMap.keySet()) { @@ -295,6 +309,7 @@ protected String buildURLNoHints(String path, GHMRequest ghRequest) { protected String postJson(String url, JsonNode data) throws IOException { String stringData = data.toString(); Request.Builder builder = new Request.Builder().url(url).post(RequestBody.create(MT_JSON, stringData)); + builder.header(X_GH_CLIENT_VERSION, Version.GH_VERSION_FROM_MAVEN); // force avoiding our GzipRequestInterceptor for smaller requests ~30 locations if (stringData.length() < maxUnzippedLength) builder.header("Content-Encoding", "identity"); @@ -309,7 +324,7 @@ protected String postJson(String url, JsonNode data) throws IOException { } private void putStrings(ObjectNode requestJson, String name, Collection stringList) { - if (stringList.isEmpty()) + if (stringList == null || stringList.isEmpty()) return; ArrayNode outList = objectMapper.createArrayNode(); for (String str : stringList) { diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMatrixBatchRequester.java b/client-hc/src/main/java/com/graphhopper/api/GHMatrixBatchRequester.java index 318cbe320ed..2833b682a2e 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GHMatrixBatchRequester.java +++ b/client-hc/src/main/java/com/graphhopper/api/GHMatrixBatchRequester.java @@ -28,9 +28,11 @@ import java.io.IOException; import java.net.SocketTimeoutException; -import java.util.Collection; import java.util.concurrent.TimeUnit; +import static com.graphhopper.api.GraphHopperWeb.X_GH_CLIENT_VERSION; +import static com.graphhopper.api.Version.GH_VERSION_FROM_MAVEN; + /** * @author Peter Karich */ @@ -71,15 +73,15 @@ public GHMatrixBatchRequester setSleepAfterGET(long sleepAfterGETMillis) { @Override public MatrixResponse route(GHMRequest ghRequest) { - Collection outArraysList = createOutArrayList(ghRequest); - JsonNode requestJson = createPostRequest(ghRequest, outArraysList); + JsonNode requestJson = createPostRequest(ghRequest); - boolean withTimes = outArraysList.contains("times"); - boolean withDistances = outArraysList.contains("distances"); - boolean withWeights = outArraysList.contains("weights"); + boolean withTimes = ghRequest.getOutArrays().contains("times"); + boolean withDistances = ghRequest.getOutArrays().contains("distances"); + boolean withWeights = ghRequest.getOutArrays().contains("weights"); final MatrixResponse matrixResponse = new MatrixResponse( - ghRequest.getFromPoints().size(), - ghRequest.getToPoints().size(), withTimes, withDistances, withWeights); + ghRequest.getPoints() == null ? ghRequest.getFromPoints().size() : ghRequest.getPoints().size(), + ghRequest.getPoints() == null ? ghRequest.getToPoints().size() : ghRequest.getPoints().size(), + withTimes, withDistances, withWeights); try { String postUrl = buildURLNoHints("/calculate", ghRequest); String postResponseStr = postJson(postUrl, requestJson); @@ -131,7 +133,7 @@ public MatrixResponse route(GHMRequest ghRequest) { if ("finished".equals(status)) { JsonNode solution = getResponseJson.get("solution"); - matrixResponse.addErrors(readUsableEntityError(outArraysList, solution)); + matrixResponse.addErrors(readUsableEntityError(ghRequest.getOutArrays(), solution)); if (!matrixResponse.hasErrors()) fillResponseFromJson(matrixResponse, solution, ghRequest.getFailFast()); @@ -154,7 +156,9 @@ public MatrixResponse route(GHMRequest ghRequest) { } protected String getJson(String url) throws IOException { - Request okRequest = new Request.Builder().url(url).build(); + Request okRequest = new Request.Builder().url(url) + .header(X_GH_CLIENT_VERSION, GH_VERSION_FROM_MAVEN) + .build(); ResponseBody body = null; try { body = getDownloader().newCall(okRequest).execute().body(); diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMatrixSyncRequester.java b/client-hc/src/main/java/com/graphhopper/api/GHMatrixSyncRequester.java index 5b037943e4d..08cddc96538 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GHMatrixSyncRequester.java +++ b/client-hc/src/main/java/com/graphhopper/api/GHMatrixSyncRequester.java @@ -5,7 +5,6 @@ import okhttp3.OkHttpClient; import java.io.IOException; -import java.util.Collection; import java.util.concurrent.TimeUnit; /** @@ -29,18 +28,18 @@ public GHMatrixSyncRequester(String serviceUrl, OkHttpClient client, boolean doR @Override public MatrixResponse route(GHMRequest ghRequest) { - Collection outArraysList = createOutArrayList(ghRequest); - JsonNode requestJson = createPostRequest(ghRequest, outArraysList); + JsonNode requestJson = createPostRequest(ghRequest); - boolean withTimes = outArraysList.contains("times"); - boolean withDistances = outArraysList.contains("distances"); - boolean withWeights = outArraysList.contains("weights"); + boolean withTimes = ghRequest.getOutArrays().contains("times"); + boolean withDistances = ghRequest.getOutArrays().contains("distances"); + boolean withWeights = ghRequest.getOutArrays().contains("weights"); final MatrixResponse matrixResponse = new MatrixResponse( - ghRequest.getFromPoints().size(), - ghRequest.getToPoints().size(), withTimes, withDistances, withWeights); + ghRequest.getPoints() == null ? ghRequest.getFromPoints().size() : ghRequest.getPoints().size(), + ghRequest.getPoints() == null ? ghRequest.getToPoints().size() : ghRequest.getPoints().size(), + withTimes, withDistances, withWeights); try { - String postUrl = buildURLNoHints("/", ghRequest); + String postUrl = buildURLNoHints("", ghRequest); JsonNode responseJson = fromStringToJSON(postUrl, postJson(postUrl, requestJson)); if (responseJson.has("message")) { matrixResponse.addErrors(ResponsePathDeserializer.readErrors(objectMapper, responseJson)); @@ -49,7 +48,7 @@ public MatrixResponse route(GHMRequest ghRequest) { matrixResponse.addErrors(ResponsePathDeserializer.readErrors(objectMapper, responseJson)); if (!matrixResponse.hasErrors()) - matrixResponse.addErrors(readUsableEntityError(outArraysList, responseJson)); + matrixResponse.addErrors(readUsableEntityError(ghRequest.getOutArrays(), responseJson)); if (!matrixResponse.hasErrors()) fillResponseFromJson(matrixResponse, responseJson, ghRequest.getFailFast()); diff --git a/client-hc/src/main/java/com/graphhopper/api/GraphHopperGeocoding.java b/client-hc/src/main/java/com/graphhopper/api/GraphHopperGeocoding.java index 26c535bab75..31d488cadb5 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GraphHopperGeocoding.java +++ b/client-hc/src/main/java/com/graphhopper/api/GraphHopperGeocoding.java @@ -30,6 +30,9 @@ import java.net.URLEncoder; import java.util.concurrent.TimeUnit; +import static com.graphhopper.api.GraphHopperWeb.X_GH_CLIENT_VERSION; +import static com.graphhopper.api.Version.GH_VERSION_FROM_MAVEN; + /** * Client implementation for the GraphHopper Directions API Geocoding. For details on how to use it, please consult * the documentation at: https://graphhopper.com/api/1/docs/geocoding/. @@ -76,7 +79,9 @@ public GraphHopperGeocoding(String serviceUrl) { public GHGeocodingResponse geocode(GHGeocodingRequest request) { String url = buildUrl(request); try { - Request okRequest = new Request.Builder().url(url).build(); + Request okRequest = new Request.Builder().url(url) + .header(X_GH_CLIENT_VERSION, GH_VERSION_FROM_MAVEN) + .build(); Response rsp = getClientForRequest(request).newCall(okRequest).execute(); ResponseBody rspBody = rsp.body(); if (!rsp.isSuccessful()) diff --git a/client-hc/src/main/java/com/graphhopper/api/GraphHopperMatrixWeb.java b/client-hc/src/main/java/com/graphhopper/api/GraphHopperMatrixWeb.java index 3a19a092647..90c4c0e222a 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GraphHopperMatrixWeb.java +++ b/client-hc/src/main/java/com/graphhopper/api/GraphHopperMatrixWeb.java @@ -38,9 +38,6 @@ public GraphHopperMatrixWeb setKey(String key) { public MatrixResponse route(GHMRequest request) { if (!Helper.isEmpty(key)) request.getHints().putObject(KEY, key); - if (!request.getPathDetails().isEmpty()) - throw new IllegalArgumentException("Path details are not supported for the Matrix API"); - request.compactPointHints(); return requester.route(request); } } diff --git a/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java b/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java index af4136c1b0a..f3c40ed6df6 100644 --- a/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java +++ b/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java @@ -28,6 +28,7 @@ import com.graphhopper.jackson.Jackson; import com.graphhopper.jackson.ResponsePathDeserializer; import com.graphhopper.util.Helper; +import com.graphhopper.util.PMap; import com.graphhopper.util.Parameters; import com.graphhopper.util.shapes.GHPoint; import okhttp3.OkHttpClient; @@ -41,6 +42,7 @@ import java.util.concurrent.TimeUnit; import static com.graphhopper.api.GraphHopperMatrixWeb.*; +import static com.graphhopper.api.Version.GH_VERSION_FROM_MAVEN; import static com.graphhopper.util.Helper.round6; import static com.graphhopper.util.Helper.toLowerCase; import static com.graphhopper.util.Parameters.Routing.CALC_POINTS; @@ -54,6 +56,7 @@ */ public class GraphHopperWeb { + public static final String X_GH_CLIENT_VERSION = "X-GH-Client-Version"; private final ObjectMapper objectMapper; private final String routeServiceUrl; private OkHttpClient downloader; @@ -200,12 +203,16 @@ public GHResponse route(GHRequest ghRequest) { return res; JsonNode paths = json.get("paths"); - for (JsonNode path : paths) { ResponsePath altRsp = ResponsePathDeserializer.createResponsePath(objectMapper, path, tmpElevation, tmpTurnDescription); res.add(altRsp); } + JsonNode b = json.get("hints"); + PMap hints = new PMap(); + b.fields().forEachRemaining(f -> hints.putObject(f.getKey(), Helper.toObject(f.getValue().asText()))); + res.setHints(hints); + return res; } catch (Exception ex) { @@ -242,6 +249,7 @@ Request createPostRequest(GHRequest ghRequest) { throw new RuntimeException("Could not write request body", e); } Request.Builder builder = new Request.Builder().url(url).post(RequestBody.create(MT_JSON, body)); + builder.header(X_GH_CLIENT_VERSION, GH_VERSION_FROM_MAVEN); // force avoiding our GzipRequestInterceptor for smaller requests ~30 locations if (body.length() < maxUnzippedLength) builder.header("Content-Encoding", "identity"); @@ -369,7 +377,9 @@ Request createGetRequest(GHRequest ghRequest) { } } - return new Request.Builder().url(url).build(); + return new Request.Builder().url(url) + .header(X_GH_CLIENT_VERSION, GH_VERSION_FROM_MAVEN) + .build(); } public String export(GHRequest ghRequest) { diff --git a/client-hc/src/main/java/com/graphhopper/api/Version.java b/client-hc/src/main/java/com/graphhopper/api/Version.java new file mode 100644 index 00000000000..d22f28466a5 --- /dev/null +++ b/client-hc/src/main/java/com/graphhopper/api/Version.java @@ -0,0 +1,37 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.api; + +import java.io.IOException; +import java.io.InputStreamReader; + +import static com.graphhopper.util.Helper.UTF_CS; +import static com.graphhopper.util.Helper.readFile; + +public class Version { + public static final String GH_VERSION_FROM_MAVEN; + + static { + try { + GH_VERSION_FROM_MAVEN = readFile(new InputStreamReader(Version.class.getResourceAsStream("version"), UTF_CS)).get(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/client-hc/src/main/resources/com/graphhopper/api/version b/client-hc/src/main/resources/com/graphhopper/api/version new file mode 100644 index 00000000000..f2ab45c3b0e --- /dev/null +++ b/client-hc/src/main/resources/com/graphhopper/api/version @@ -0,0 +1 @@ +${project.version} \ No newline at end of file diff --git a/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java b/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java index 85dc064988e..9553c07f071 100644 --- a/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java +++ b/client-hc/src/test/java/com/graphhopper/api/AbstractGHMatrixWebTester.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.util.Arrays; import java.util.Collections; import static org.junit.jupiter.api.Assertions.*; @@ -27,10 +28,14 @@ public abstract class AbstractGHMatrixWebTester { public static GHMRequest createRequest() { GHMRequest req = new GHMRequest(); - req.addPoint(new GHPoint(51.534377, -0.087891)); - req.addPoint(new GHPoint(51.467697, -0.090637)); - req.addPoint(new GHPoint(51.521241, -0.171833)); - req.addPoint(new GHPoint(51.473685, -0.211487)); + req.setPoints(Arrays.asList( + new GHPoint(51.534377, -0.087891), + new GHPoint(51.467697, -0.090637), + new GHPoint(51.521241, -0.171833), + new GHPoint(51.473685, -0.211487) + )); + req.setOutArrays(Arrays.asList("weights")); + req.setProfile("car"); return req; } @@ -70,12 +75,13 @@ public void testReadingMatrixConnectionsNotFound_noFailFast() throws IOException GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); GHMRequest req = new GHMRequest(); - req.addPoint(new GHPoint(0, 1)); - req.addPoint(new GHPoint(2, 3)); - req.addOutArray("weights"); - req.addOutArray("distances"); - req.addOutArray("times"); + req.setPoints(Arrays.asList( + new GHPoint(0, 1), + new GHPoint(2, 3) + )); + req.setOutArrays(Arrays.asList("weights", "distances", "times")); req.setFailFast(false); + req.setProfile("car"); MatrixResponse rsp = matrixWeb.route(req); assertFalse(rsp.hasErrors()); @@ -99,12 +105,13 @@ public void testReadingMatrixPointsNotFound_noFailFast() throws IOException { GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); GHMRequest req = new GHMRequest(); - req.addPoint(new GHPoint(0, 1)); - req.addPoint(new GHPoint(2, 3)); - req.addPoint(new GHPoint(4, 5)); - req.addOutArray("weights"); - req.addOutArray("distances"); - req.addOutArray("times"); + req.setPoints(Arrays.asList( + new GHPoint(0, 1), + new GHPoint(2, 3), + new GHPoint(4, 5) + )); + req.setProfile("car"); + req.setOutArrays(Arrays.asList("weights", "distances", "times")); req.setFailFast(false); MatrixResponse rsp = matrixWeb.route(req); @@ -164,9 +171,7 @@ public void testReadingWeights_TimesAndDistances() throws IOException { GraphHopperMatrixWeb matrixWeb = createMatrixClient(ghMatrix); GHMRequest req = createRequest(); - req.addOutArray("weights"); - req.addOutArray("distances"); - req.addOutArray("times"); + req.setOutArrays(Arrays.asList("weights", "distances", "times")); MatrixResponse rsp = matrixWeb.route(req); assertFalse(rsp.hasErrors()); @@ -183,19 +188,38 @@ public void testReadingWeights_TimesAndDistances() throws IOException { } @Test - public void noVehicleWhenNotSpecified() { + public void noProfileWhenNotSpecified() { GHMatrixBatchRequester requester = new GHMatrixBatchRequester("url"); - JsonNode json = requester.createPostRequest(new GHMRequest(5), Collections.singletonList("weights")); - assertEquals("{\"out_arrays\":[\"weights\"],\"fail_fast\":true}", json.toString()); + JsonNode json = requester.createPostRequest(new GHMRequest().setOutArrays(Collections.singletonList("weights")). + setPoints(Arrays.asList(new GHPoint(11, 12))).setProfile("car")); + assertEquals("{\"points\":[[12.0,11.0]],\"out_arrays\":[\"weights\"],\"fail_fast\":true,\"profile\":\"car\"}", json.toString()); + } + + @Test + public void hasProfile() { + GHMatrixAbstractRequester requester = createRequester("url"); + GHMRequest ghmRequest = new GHMRequest(); + ghmRequest.setOutArrays(Collections.singletonList("weights")); + ghmRequest.setPoints(Arrays.asList(new GHPoint(11, 12))); + ghmRequest.setProfile("bike"); + JsonNode json = requester.createPostRequest(ghmRequest); + assertEquals("{\"points\":[[12.0,11.0]],\"out_arrays\":[\"weights\"],\"fail_fast\":true,\"profile\":\"bike\"}", json.toString()); } @Test public void hasHintsWhenSpecified() { GHMatrixAbstractRequester requester = createRequester("url"); - GHMRequest ghmRequest = new GHMRequest(5); - ghmRequest.putHint("vehicle", "my_car").putHint("profile", "my_profile"); - JsonNode json = requester.createPostRequest(ghmRequest, Collections.singletonList("weights")); - assertEquals("{\"out_arrays\":[\"weights\"],\"fail_fast\":true,\"vehicle\":\"my_car\",\"profile\":\"my_profile\"}", json.toString()); + GHMRequest ghmRequest = new GHMRequest(); + ghmRequest.setProfile("car"); + ghmRequest.putHint("some_property", "value"); + ghmRequest.setOutArrays(Collections.singletonList("weights")); + ghmRequest.setPoints(Arrays.asList(new GHPoint(11, 12))); + JsonNode json = requester.createPostRequest(ghmRequest); + assertEquals("{\"points\":[[12.0,11.0]],\"out_arrays\":[\"weights\"],\"fail_fast\":true,\"profile\":\"car\",\"some_property\":\"value\"}", json.toString()); + + ghmRequest.putHint("profile", "car"); + Exception ex = assertThrows(IllegalArgumentException.class, () -> requester.createPostRequest(ghmRequest)); + assertTrue(ex.getMessage().contains("use setProfile"), ex.getMessage()); } public static String readFile(Reader simpleReader) throws IOException { diff --git a/client-hc/src/test/java/com/graphhopper/api/Examples.java b/client-hc/src/test/java/com/graphhopper/api/Examples.java index be26b50199e..887742d070f 100644 --- a/client-hc/src/test/java/com/graphhopper/api/Examples.java +++ b/client-hc/src/test/java/com/graphhopper/api/Examples.java @@ -36,9 +36,9 @@ public void routing() { GHRequest req = new GHRequest(). addPoint(new GHPoint(49.6724, 11.3494)). addPoint(new GHPoint(49.6550, 11.4180)); - // Set vehicle like car, bike, foot, ... - req.putHint("vehicle", "bike"); - // Optionally enable/disable elevation in output PointList, currently bike and foot support elevation, default is false + // Set profile like car, bike, foot, ... + req.setProfile("bike"); + // Optionally enable/disable elevation in output PointList, default is false req.putHint("elevation", false); // Optionally enable/disable turn instruction information, defaults is true req.putHint("instructions", true); @@ -75,6 +75,10 @@ public void routing() { InstructionList il = res.getInstructions(); for (Instruction i : il) { // System.out.println(i.getName()); + + // to get the translated turn instructions you call: + // System.out.println(i.getTurnDescription(null)); + // Note, that you can control the language only in via the request setLocale method and cannot change it only the client side } // get path details List pathDetails = res.getPathDetails().get(Parameters.Details.STREET_NAME); @@ -92,23 +96,29 @@ public void matrix() { matrixClient.setKey(apiKey); GHMRequest ghmRequest = new GHMRequest(); - ghmRequest.addOutArray("distances"); - ghmRequest.addOutArray("times"); - ghmRequest.putHint("vehicle", "car"); - - // init points for a symmetric matrix - // List allPoints = Arrays.asList(new GHPoint(49.6724, 11.3494), new GHPoint(49.6550, 11.4180)); - // ghmRequest.addAllPoints(allPoints); + ghmRequest.setOutArrays(Arrays.asList("distances", "times")); + ghmRequest.setProfile("car"); + + // Option 1: init points for a symmetric matrix + List allPoints = Arrays.asList(new GHPoint(49.6724, 11.3494), new GHPoint(49.6550, 11.4180)); + ghmRequest.setPoints(allPoints); + MatrixResponse responseSymm = matrixClient.route(ghmRequest); + if (responseSymm.hasErrors()) + throw new RuntimeException(responseSymm.getErrors().toString()); + // get time from first to second point: + // System.out.println(response.getTime(0, 1)); + // Option 2: for an asymmetric matrix do: + ghmRequest = new GHMRequest(); + ghmRequest.setOutArrays(Arrays.asList("distances", "times")); + ghmRequest.setProfile("car"); + ghmRequest.setFromPoints(Arrays.asList(new GHPoint(49.6724, 11.3494))); // or init e.g. a one-to-many matrix: - ghmRequest.addFromPoint(new GHPoint(49.6724, 11.3494)); - for (GHPoint to : Arrays.asList(new GHPoint(49.6724, 11.3494), new GHPoint(49.6550, 11.4180))) { - ghmRequest.addToPoint(to); - } + ghmRequest.setToPoints(Arrays.asList(new GHPoint(49.6724, 11.3494), new GHPoint(49.6550, 11.4180))); - MatrixResponse response = matrixClient.route(ghmRequest); - if (response.hasErrors()) - throw new RuntimeException(response.getErrors().toString()); + MatrixResponse responseAsymm = matrixClient.route(ghmRequest); + if (responseAsymm.hasErrors()) + throw new RuntimeException(responseAsymm.getErrors().toString()); // get time from first to second point: // System.out.println(response.getTime(0, 1)); diff --git a/client-hc/src/test/java/com/graphhopper/api/GHMRequestTest.java b/client-hc/src/test/java/com/graphhopper/api/GHMRequestTest.java deleted file mode 100644 index 1c374bd593d..00000000000 --- a/client-hc/src/test/java/com/graphhopper/api/GHMRequestTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.graphhopper.api; - -import com.graphhopper.util.shapes.GHPoint; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class GHMRequestTest { - @Test - public void testCompact() { - GHMRequest request = new GHMRequest(); - for (int i = 0; i < 3; i++) { - request.addFromPoint(new GHPoint()); - request.addFromPointHint(""); - } - - request.addToPoint(new GHPoint()); - request.addToPointHint(""); - - request.compactPointHints(); - assertTrue(request.getToPointHints().isEmpty()); - assertTrue(request.getFromPointHints().isEmpty()); - } - - @Test - public void testCompact2() { - GHMRequest request = new GHMRequest(); - for (int i = 0; i < 3; i++) { - request.addFromPoint(new GHPoint()); - request.addFromPointHint(""); - } - - request.addToPoint(new GHPoint()); - request.addToPointHint("x"); - - request.compactPointHints(); - assertFalse(request.getToPointHints().isEmpty()); - assertTrue(request.getFromPointHints().isEmpty()); - } -} \ No newline at end of file diff --git a/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java b/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java index 115f7bcccc9..067883d953d 100644 --- a/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java +++ b/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java @@ -9,6 +9,7 @@ import com.graphhopper.json.Statement; import com.graphhopper.util.CustomModel; import com.graphhopper.util.JsonFeature; +import com.graphhopper.util.JsonFeatureCollection; import com.graphhopper.util.shapes.GHPoint; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -20,8 +21,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; /** * This class unit tests the class. For integration tests against a real server see RouteResourceClientHCTest. @@ -33,14 +33,14 @@ public class GraphHopperWebTest { public void testGetClientForRequest(boolean usePost) { GraphHopperWeb gh = new GraphHopperWeb(null).setPostRequest(usePost); GHRequest req = new GHRequest(new GHPoint(42.509225, 1.534728), new GHPoint(42.512602, 1.551558)). - putHint("vehicle", "car"); + setProfile("car"); req.putHint(GraphHopperWeb.TIMEOUT, 5); assertEquals(5, gh.getClientForRequest(req).connectTimeoutMillis()); } @Test - public void vehicleIncludedAsGiven() { + public void profileIncludedAsGiven() { GraphHopperWeb hopper = new GraphHopperWeb("https://localhost:8000/route"); // no vehicle -> no vehicle assertEquals("https://localhost:8000/route?profile=&type=json&instructions=true&points_encoded=true" + @@ -48,25 +48,25 @@ public void vehicleIncludedAsGiven() { hopper.createGetRequest(new GHRequest()).url().toString()); // vehicle given -> vehicle used in url - assertEquals("https://localhost:8000/route?profile=my_profile&type=json&instructions=true&points_encoded=true" + - "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false&vehicle=my_car", - hopper.createGetRequest(new GHRequest().putHint("vehicle", "my_car").setProfile("my_profile")).url().toString()); + assertEquals("https://localhost:8000/route?profile=my_car&type=json&instructions=true&points_encoded=true" + + "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false", + hopper.createGetRequest(new GHRequest().setProfile("my_car")).url().toString()); } @Test public void headings() { GraphHopperWeb hopper = new GraphHopperWeb("http://localhost:8080/route"); GHRequest req = new GHRequest(new GHPoint(42.509225, 1.534728), new GHPoint(42.512602, 1.551558)). - setHeadings(Arrays.asList(10.0, -90.0)). + setHeadings(Arrays.asList(10.0, 90.0)). setProfile("car"); assertEquals("http://localhost:8080/route?profile=car&point=42.509225,1.534728&point=42.512602,1.551558&type=json&instructions=true&points_encoded=true" + - "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false&heading=10.0&heading=-90.0", hopper.createGetRequest(req).url().toString()); + "&calc_points=true&algorithm=&locale=en_US&elevation=false&optimize=false&heading=10.0&heading=90.0", hopper.createGetRequest(req).url().toString()); } @Test public void customModel() throws JsonProcessingException { GraphHopperWeb client = new GraphHopperWeb("http://localhost:8080/route"); - LinkedHashMap areas = new LinkedHashMap<>(); + JsonFeatureCollection areas = new JsonFeatureCollection(); Coordinate[] area_1_coordinates = new Coordinate[]{ new Coordinate(48.019324184801185, 11.28021240234375), new Coordinate(48.019324184801185, 11.53564453125), @@ -81,21 +81,21 @@ public void customModel() throws JsonProcessingException { new Coordinate(48.281365151571755, 11.53289794921875), new Coordinate(48.15509285476017, 11.53289794921875), }; - areas.put("area_1", new JsonFeature("area_1", + areas.getFeatures().add(new JsonFeature("area_1", "Feature", null, new GeometryFactory().createPolygon(area_1_coordinates), new HashMap<>())); - areas.put("area_2", new JsonFeature("area_2", + areas.getFeatures().add(new JsonFeature("area_2", "Feature", null, new GeometryFactory().createPolygon(area_2_coordinates), new HashMap<>())); CustomModel customModel = new CustomModel() - .addToSpeed(Statement.If("road_class == MOTORWAY", Statement.Op.LIMIT, 80)) - .addToPriority(Statement.If("surface == DIRT", Statement.Op.MULTIPLY, 0.7)) - .addToPriority(Statement.If("surface == SAND", Statement.Op.MULTIPLY, 0.6)) - .setDistanceInfluence(69) + .addToSpeed(Statement.If("road_class == MOTORWAY", Statement.Op.LIMIT, "80")) + .addToPriority(Statement.If("surface == DIRT", Statement.Op.MULTIPLY, "0.7")) + .addToPriority(Statement.If("surface == SAND", Statement.Op.MULTIPLY, "0.6")) + .setDistanceInfluence(69d) .setHeadingPenalty(22) .setAreas(areas); GHRequest req = new GHRequest(new GHPoint(42.509225, 1.534728), new GHPoint(42.512602, 1.551558)) @@ -109,10 +109,14 @@ public void customModel() throws JsonProcessingException { JsonNode customModelJson = postRequest.get("custom_model"); ObjectMapper objectMapper = Jackson.newObjectMapper(); JsonNode expected = objectMapper.readTree("{\"distance_influence\":69.0,\"heading_penalty\":22.0,\"internal\":false,\"areas\":{" + - "\"area_1\":{\"id\":\"area_1\",\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[48.019324184801185,11.28021240234375],[48.019324184801185,11.53564453125],[48.11843396091691,11.53564453125],[48.11843396091691,11.28021240234375],[48.019324184801185,11.28021240234375]]]},\"properties\":{}}," + - "\"area_2\":{\"id\":\"area_2\",\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[48.15509285476017,11.53289794921875],[48.15509285476017,11.8212890625],[48.281365151571755,11.8212890625],[48.281365151571755,11.53289794921875],[48.15509285476017,11.53289794921875]]]},\"properties\":{}}}," + - "\"speed\":[{\"if\":\"road_class == MOTORWAY\",\"limit_to\":80.0}]," + - "\"priority\":[{\"if\":\"surface == DIRT\",\"multiply_by\":0.7},{\"if\":\"surface == SAND\",\"multiply_by\":0.6}]}"); + "\"type\":\"FeatureCollection\",\"features\":["+ + "{\"id\":\"area_1\",\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[48.019324184801185,11.28021240234375],[48.019324184801185,11.53564453125],[48.11843396091691,11.53564453125],[48.11843396091691,11.28021240234375],[48.019324184801185,11.28021240234375]]]},\"properties\":{}}," + + "{\"id\":\"area_2\",\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[48.15509285476017,11.53289794921875],[48.15509285476017,11.8212890625],[48.281365151571755,11.8212890625],[48.281365151571755,11.53289794921875],[48.15509285476017,11.53289794921875]]]},\"properties\":{}}]}," + + "\"speed\":[{\"if\":\"road_class == MOTORWAY\",\"limit_to\":\"80\"}]," + + "\"priority\":[{\"if\":\"surface == DIRT\",\"multiply_by\":\"0.7\"},{\"if\":\"surface == SAND\",\"multiply_by\":\"0.6\"}]}"); assertEquals(expected, objectMapper.valueToTree(customModelJson)); + + CustomModel cm = objectMapper.readValue("{\"distance_influence\":null}", CustomModel.class); + assertNull(cm.getDistanceInfluence()); } } \ No newline at end of file diff --git a/config-example.yml b/config-example.yml index fe466747cbe..8286cc560ac 100644 --- a/config-example.yml +++ b/config-example.yml @@ -5,58 +5,49 @@ graphhopper: # Local folder used by graphhopper to store its data graph.location: graph-cache - ##### Vehicles ##### - - - # More options: foot,hike,bike,bike2,mtb,racingbike,motorcycle,car4wd,wheelchair (comma separated) - # bike2 takes elevation data into account (like up-hill is slower than down-hill) and requires enabling graph.elevation.provider below. - # graph.flag_encoders: car - - # Enable turn restrictions for car or motorcycle. - # graph.flag_encoders: car|turn_costs=true - - # Add additional information to every edge. Used for path details (#1548), better instructions (#1844) and tunnel/bridge interpolation (#798). - # Default values are: road_class,road_class_link,road_environment,max_speed,road_access (since #1805) - # More are: surface,smoothness,max_width,max_height,max_weight,max_axle_load,max_length,hazmat,hazmat_tunnel,hazmat_water,toll,track_type, - # mtb_rating, hike_rating,horse_rating,lanes - # graph.encoded_values: surface,toll,track_type ##### Routing Profiles #### - # Routing can be done for the following list of profiles. Note that it is required to specify all the profiles you - # would like to use here. The fields of each profile are as follows: + # Routing can be done only for profiles listed below. For more information about profiles and custom profiles have a + # look into the documentation at docs/core/profiles.md or the examples under web/src/test/java/com/graphhopper/application/resources/ + # or the CustomWeighting class for the raw details. + # + # In general a profile consists of the following # - name (required): a unique string identifier for the profile - # - vehicle (required): refers to the `graph.flag_encoders` used for this profile - # - weighting (required): the weighting used for this profile, e.g. fastest,shortest or short_fastest + # - vehicle (required): refers to the `graph.vehicles` used for this profile + # - weighting (required): the weighting used for this profile like custom,fastest,shortest or short_fastest # - turn_costs (true/false, default: false): whether or not turn restrictions should be applied for this profile. - # this will only work if the `graph.flag_encoders` for the given `vehicle` is configured with `|turn_costs=true`. # # Depending on the above fields there are other properties that can be used, e.g. # - distance_factor: 0.1 (can be used to fine tune the time/distance trade-off of short_fastest weighting) # - u_turn_costs: 60 (time-penalty for doing a u-turn in seconds (only possible when `turn_costs: true`)). # Note that since the u-turn costs are given in seconds the weighting you use should also calculate the weight # in seconds, so for example it does not work with shortest weighting. - # - custom_model_file: when you specified "weighting: custom" you need to set a yaml or json file inside your custom_model_folder + # - custom_model_file: when you specified "weighting: custom" you need to set a json file inside your custom_models.directory # or working directory that defines the custom_model. If you want an empty model you can also set "custom_model_file: empty". # You can also use th e`custom_model` field instead and specify your custom model in the profile directly. # - # For more information about profiles and especially custom profiles have a look into the documentation - # at docs/core/profiles.md or the examples under web/src/test/java/com/graphhopper/application/resources/ or - # the CustomWeighting class for the raw details. - # # To prevent long running routing queries you should usually enable either speed or hybrid mode for all the given - # profiles (see below). Otherwise you should at least limit the number of `routing.max_visited_nodes`. + # profiles (see below). Or at least limit the number of `routing.max_visited_nodes`. + profiles: - name: car vehicle: car - weighting: fastest - - # - name: car_with_turn_costs - # vehicle: car - # weighting: short_fastest - # distance_factor: 0.1 - # turn_costs: true - # u_turn_costs: 60 + weighting: custom + custom_model: + distance_influence: 70 +# turn_costs: true +# u_turn_costs: 60 + +# - name: bike +# # to use the bike vehicle make sure to not ignore cycleways etc., see import.osm.ignored_highways below +# vehicle: bike +# weighting: custom +# # the custom model in bike.json is defined to avoid hills +# custom_model_file: bike.json + + # specify the folder where to find the custom model files + custom_models.directory: custom_models # Speed mode: # Its possible to speed up routing by doing a special graph preparation (Contraction Hierarchies, CH). This requires @@ -66,7 +57,6 @@ graphhopper: # usage) and the routing will also be slower than with `turn_costs: false`. profiles_ch: - profile: car - # - profile: car_with_turn_costs # Hybrid mode: # Similar to speed mode, the hybrid mode (Landmarks, LM) also speeds up routing by doing calculating auxiliary data @@ -79,24 +69,58 @@ graphhopper: # for the preparation (`my_other_profile`) profiles_lm: [] - ##### Elevation ##### + #### Vehicles #### + + # The vehicle defines the base for how the routing of a profile behaves. It can be fine tuned using the options: + # name=mycustomvehicle,block_private=true,turn_costs=true,transportation_mode=MOTOR_VEHICLE (only for the roads vehicle) + # Still, it is recommended to avoid changing the vehicle settings and change the custom model instead. + # graph.vehicles: car|block_fords=true,turn_costs=true,bike|turn_costs=true + # Other standard vehicles: foot,bike,mtb,racingbike,motorcycle,wheelchair + + + #### Encoded Values #### + + # Add additional information to every edge. Used for path details (#1548) and custom models (docs/core/custom-models.md) + # Default values are: road_class,road_class_link,road_environment,max_speed,road_access + # More are: surface,smoothness,max_width,max_height,max_weight,hgv,max_axle_load,max_length,hazmat,hazmat_tunnel,hazmat_water, + # lanes,osm_way_id,toll,track_type,mtb_rating,hike_rating,horse_rating + # graph.encoded_values: surface,toll,track_type + + #### Speed, hybrid and flexible mode #### + + # To make CH preparation faster for multiple profiles you can increase the default threads if you have enough RAM. + # Change this setting only if you know what you are doing and if the default worked for you. + # prepare.ch.threads: 1 + + # To tune the performance vs. memory usage for the hybrid mode use + # prepare.lm.landmarks: 16 + + # Make landmark preparation parallel if you have enough RAM. Change this only if you know what you are doing and if + # the default worked for you. + # prepare.lm.threads: 1 + + + #### Elevation #### # To populate your graph with elevation data use SRTM, default is noop (no elevation). Read more about it in docs/core/elevation.md # graph.elevation.provider: srtm - # default location for cache is /tmp/srtm # graph.elevation.cache_dir: ./srtmprovider/ - # If you have a slow disk or plenty of RAM change the default MMAP to: # graph.elevation.dataaccess: RAM_STORE - # To enable bilinear interpolation when sampling elevation at points (default uses nearest neighbor): # graph.elevation.interpolate: bilinear + # Reduce ascend/descend per edge without changing the maximum slope: + # graph.elevation.edge_smoothing: ramer + # removes elevation fluctuations up to max_elevation (in meter) and replaces the elevation with a value based on the average slope + # graph.elevation.edge_smoothing.ramer.max_elevation: 5 + # A potentially bigger reduction of ascend/descend is possible, but maximum slope will often increase (do not use when average_slope and maximum_slope shall be used in a custom_model) + # graph.elevation.edge_smoothing: moving_average # To increase elevation profile resolution, use the following two parameters to tune the extra resolution you need # against the additional storage space used for edge geometries. You should enable bilinear interpolation when using @@ -108,29 +132,31 @@ graphhopper: # graph.elevation.way_point_max_distance: 10 - #### Speed, hybrid and flexible mode #### - + #### Urban density (built-up areas) #### - # To make CH preparation faster for multiple profiles you can increase the default threads if you have enough RAM. - # Change this setting only if you know what you are doing and if the default worked for you. - # prepare.ch.threads: 1 + # This feature allows classifying roads into 'rural', 'residential' and 'city' areas (encoded value 'urban_density') + # Use 1 or more threads to enable the feature + # graph.urban_density.threads: 8 + # Use higher/lower sensitivities if too little/many roads fall into the according categories. + # Using smaller radii will speed up the classification, but only change these values if you know what you are doing. + # If you do not need the (rather slow) city classification set city_radius to zero. + # graph.urban_density.residential_radius: 300 + # graph.urban_density.residential_sensitivity: 60 + # graph.urban_density.city_radius: 2000 + # graph.urban_density.city_sensitivity: 30 - # To tune the performance vs. memory usage for the hybrid mode use - # prepare.lm.landmarks: 16 - # Make landmark preparation parallel if you have enough RAM. Change this only if you know what you are doing and if - # the default worked for you. - # prepare.lm.threads: 1 + #### Subnetworks #### # In many cases the road network consists of independent components without any routes going in between. In # the most simple case you can imagine an island without a bridge or ferry connection. The following parameter # allows setting a minimum size (number of edges) for such detached components. This can be used to reduce the number # of cases where a connection between locations might not be found. prepare.min_network_size: 200 + prepare.subnetworks.threads: 1 - ##### Routing ##### - + #### Routing #### # You can define the maximum visited nodes when routing. This may result in not found connections if there is no # connection between two points within the given visited nodes. The default is Integer.MAX_VALUE. Useful for flexibility mode @@ -144,26 +170,36 @@ graphhopper: routing.non_ch.max_waypoint_distance: 1000000 - ##### Storage ##### + #### Storage #### + # Excludes certain types of highways during the OSM import to speed up the process and reduce the size of the graph. + # A typical application is excluding 'footway','cycleway','path' and maybe 'pedestrian' and 'track' highways for + # motorized vehicles. This leads to a smaller and less dense graph, because there are fewer ways (obviously), + # but also because there are fewer crossings between highways (=junctions). + # Another typical example is excluding 'motorway', 'trunk' and maybe 'primary' highways for bicycle or pedestrian routing. + import.osm.ignored_highways: footway,cycleway,path,pedestrian,steps # typically useful for motorized-only routing + # import.osm.ignored_highways: motorway,trunk # typically useful for non-motorized routing # configure the memory access, use RAM_STORE for well equipped servers (default and recommended) graph.dataaccess.default_type: RAM_STORE - # will write way names in the preferred language (language code as defined in ISO 639-1 or ISO 639-2): # datareader.preferred_language: en - # Sort the graph after import to make requests roughly ~10% faster. Note that this requires significantly more RAM on import. # graph.do_sort: true - ##### Country Rules ##### + + #### Custom Areas #### + # GraphHopper reads GeoJSON polygon files including their properties from this directory and makes them available - # to all tag parsers and flag encoders. Country borders (see countries.geojson) are always included automatically. + # to all tag parsers, vehicles and custom models. All GeoJSON Features require to have the "id" property. + # Country borders are included automatically (see countries.geojson). # custom_areas.directory: path/to/custom_areas - ##### Country Rules ##### + + #### Country Rules #### + # GraphHopper applies country-specific routing rules during import (not enabled by default). # You need to redo the import for changes to take effect. # country_rules.enabled: true diff --git a/core/files/N41E001.hgt.zip b/core/files/N41E001.hgt.zip new file mode 100644 index 00000000000..79c19112ac8 Binary files /dev/null and b/core/files/N41E001.hgt.zip differ diff --git a/core/files/N42E001.hgt.zip b/core/files/N42E001.hgt.zip new file mode 100755 index 00000000000..30eba1d23a1 Binary files /dev/null and b/core/files/N42E001.hgt.zip differ diff --git a/core/files/N53E009.hgt.zip b/core/files/N53E009.hgt.zip new file mode 100644 index 00000000000..ab90b9ffb31 Binary files /dev/null and b/core/files/N53E009.hgt.zip differ diff --git a/core/files/bautzen.osm b/core/files/bautzen.osm new file mode 100644 index 00000000000..282ba16b18b --- /dev/null +++ b/core/files/bautzen.osm @@ -0,0 +1,2691 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/files/hohe-warte.osm.gz b/core/files/hohe-warte.osm.gz new file mode 100644 index 00000000000..a772c593d3c Binary files /dev/null and b/core/files/hohe-warte.osm.gz differ diff --git a/core/files/licenses/D3-LICENSE.txt b/core/files/licenses/D3-LICENSE.txt deleted file mode 100644 index fb7d95d70ba..00000000000 --- a/core/files/licenses/D3-LICENSE.txt +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2014, Michael Bostock -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* The name Michael Bostock may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/core/files/licenses/MAPBOX-LICENSE.txt b/core/files/licenses/MAPBOX-LICENSE.txt deleted file mode 100644 index 793dca9fb74..00000000000 --- a/core/files/licenses/MAPBOX-LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -BSD License - -Copyright© 2012, MapBox, LLC. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - -* Neither the name of the Development Seed, Inc. nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/core/files/licenses/autocomplete-license.txt b/core/files/licenses/autocomplete-license.txt deleted file mode 100644 index 11b3ff11a79..00000000000 --- a/core/files/licenses/autocomplete-license.txt +++ /dev/null @@ -1,21 +0,0 @@ -Copyright 2012 DevBridge and other contributors -http://www.devbridge.com/projects/autocomplete/jquery/ - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/files/licenses/copying-osmosis.txt b/core/files/licenses/copying-osmosis.txt deleted file mode 100644 index 4e6d150af98..00000000000 --- a/core/files/licenses/copying-osmosis.txt +++ /dev/null @@ -1,16 +0,0 @@ -Osmosis consists of all files in this archive with the exception of the -third party libraries in the lib sub-directory, and the osmosis-osm-binary -library which is a re-packaged version of a third-party library. - -Osmosis is placed into the public domain and where this is not legally -possible everybody is granted a perpetual, irrevocable license to use -this work for any purpose whatsoever. - -DISCLAIMERS -By making Osmosis publicly available, it is hoped that users will find the -software useful. However: -* Osmosis comes without any warranty, to the extent permitted by applicable -law. -* Unless required by applicable law, no liability will be accepted by -the authors and distributors of this software for any damages caused -as a result of its use. diff --git a/core/files/settings.xml b/core/files/settings.xml deleted file mode 100644 index 5d27c5eb745..00000000000 --- a/core/files/settings.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - ossrh - ${env.CI_DEPLOY_USERNAME} - ${env.CI_DEPLOY_PASSWORD} - - - - - ossrh - - true - - - ${env.GPG_EXECUTABLE} - ${env.GPG_PASSPHRASE} - - - - diff --git a/core/files/test_simple_turncosts.osm.xml b/core/files/test_simple_turncosts.osm.xml index b98915926b3..220e19e78d5 100644 --- a/core/files/test_simple_turncosts.osm.xml +++ b/core/files/test_simple_turncosts.osm.xml @@ -11,6 +11,12 @@ + + + + + + diff --git a/core/files/update-translations.sh b/core/files/update-translations.sh index 6fc32c2ceeb..487ba417158 100755 --- a/core/files/update-translations.sh +++ b/core/files/update-translations.sh @@ -3,7 +3,7 @@ cd $HOME/.. destination=src/main/resources/com/graphhopper/util/ -translations="en_US SKIP SKIP ar ast bg bn_BN ca cs_CZ da_DK de_DE el eo es fa fil fi fr_FR fr_CH gl he hr_HR hsb hu_HU in_ID it ja ko lt_LT ne nl pl_PL pt_BR pt_PT ro ru sk sl_SI sr_RS sv_SE tr uk vi_VN zh_CN zh_HK zh_TW" +translations="en_US SKIP SKIP ar ast az bg bn_BN ca cs_CZ da_DK de_DE el eo es fa fil fi fr_FR fr_CH gl he hr_HR hsb hu_HU in_ID it ja ko lt_LT nb_NO ne nl pl_PL pt_BR pt_PT ro ru sk sl_SI sr_RS sv_SE tr uk vi_VN zh_CN zh_HK zh_TW" file=$1 # You can execute the following diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java index c38c7b0b582..81177fe9b66 100644 --- a/core/src/main/java/com/graphhopper/GraphHopper.java +++ b/core/src/main/java/com/graphhopper/GraphHopper.java @@ -24,6 +24,7 @@ import com.graphhopper.config.Profile; import com.graphhopper.reader.dem.*; import com.graphhopper.reader.osm.OSMReader; +import com.graphhopper.reader.osm.RestrictionTagParser; import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.*; import com.graphhopper.routing.ch.CHPreparationHandler; @@ -40,8 +41,7 @@ import com.graphhopper.routing.subnetwork.PrepareRoutingSubnetworks.PrepareJob; import com.graphhopper.routing.util.*; import com.graphhopper.routing.util.countryrules.CountryRuleFactory; -import com.graphhopper.routing.util.parsers.DefaultTagParserFactory; -import com.graphhopper.routing.util.parsers.TagParserFactory; +import com.graphhopper.routing.util.parsers.*; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomProfile; import com.graphhopper.routing.weighting.custom.CustomWeighting; @@ -89,8 +89,10 @@ public class GraphHopper { // for custom areas: private String customAreasDirectory = ""; // for graph: - protected GraphHopperStorage ghStorage; - private TagParserManager tagParserManager; + protected BaseGraph baseGraph; + private StorableProperties properties; + private EncodingManager encodingManager; + private OSMParsers osmParsers; private int defaultSegmentSize = -1; private String ghLocation = ""; private DAType dataAccessDefaultType = DAType.RAM_STORE; @@ -107,8 +109,15 @@ public class GraphHopper { protected LocationIndex locationIndex; private int preciseIndexResolution = 300; private int maxRegionSearch = 4; - // for prepare + // subnetworks private int minNetworkSize = 200; + private int subnetworksThreads = 1; + // residential areas + private double residentialAreaRadius = 300; + private double residentialAreaSensitivity = 60; + private double cityAreaRadius = 2000; + private double cityAreaSensitivity = 30; + private int urbanDensityCalculationThreads = 0; // preparation handlers private final LMPreparationHandler lmPreparationHandler = new LMPreparationHandler(); @@ -119,14 +128,15 @@ public class GraphHopper { // for data reader private String osmFile; private ElevationProvider eleProvider = ElevationProvider.NOOP; - private FlagEncoderFactory flagEncoderFactory = new DefaultFlagEncoderFactory(); + private VehicleEncodedValuesFactory vehicleEncodedValuesFactory = new DefaultVehicleEncodedValuesFactory(); + private VehicleTagParserFactory vehicleTagParserFactory = new DefaultVehicleTagParserFactory(); private EncodedValueFactory encodedValueFactory = new DefaultEncodedValueFactory(); private TagParserFactory tagParserFactory = new DefaultTagParserFactory(); protected PathDetailsBuilderFactory pathBuilderFactory = new PathDetailsBuilderFactory(); private String dateRangeParserString = ""; private String encodedValuesString = ""; - private String flagEncodersString = ""; + private String vehiclesString = ""; public GraphHopper setEncodedValuesString(String encodedValuesString) { @@ -134,19 +144,21 @@ public GraphHopper setEncodedValuesString(String encodedValuesString) { return this; } - public GraphHopper setFlagEncodersString(String flagEncodersString) { - this.flagEncodersString = flagEncodersString; + public GraphHopper setVehiclesString(String vehiclesString) { + this.vehiclesString = vehiclesString; return this; } - public TagParserManager getTagParserManager() { - if (tagParserManager == null) - throw new IllegalStateException("TagParserManager not yet built"); - return tagParserManager; + public EncodingManager getEncodingManager() { + if (encodingManager == null) + throw new IllegalStateException("EncodingManager not yet built"); + return encodingManager; } - public EncodingManager getEncodingManager() { - return getTagParserManager().getEncodingManager(); + public OSMParsers getOSMParsers() { + if (osmParsers == null) + throw new IllegalStateException("OSMParsers not yet built"); + return osmParsers; } public ElevationProvider getElevationProvider() { @@ -188,6 +200,32 @@ public GraphHopper setMinNetworkSize(int minNetworkSize) { return this; } + /** + * Configures the urban density classification. Each edge will be classified as 'rural','residential' or 'city', {@link UrbanDensity} + * + * @param residentialAreaRadius in meters. The higher this value the longer the calculation will take and the bigger the area for + * which the road density used to identify residential areas is calculated. + * @param residentialAreaSensitivity Use this to find a trade-off between too many roads being classified as residential (too high + * values) and not enough roads being classified as residential (too small values) + * @param cityAreaRadius in meters. The higher this value the longer the calculation will take and the bigger the area for + * which the road density used to identify city areas is calculated. Set this to zero + * to skip the city classification. + * @param cityAreaSensitivity Use this to find a trade-off between too many roads being classified as city (too high values) + * and not enough roads being classified as city (too small values) + * @param threads the number of threads used for the calculation. If this is zero the urban density + * calculation is skipped entirely + */ + public GraphHopper setUrbanDensityCalculation(double residentialAreaRadius, double residentialAreaSensitivity, + double cityAreaRadius, double cityAreaSensitivity, int threads) { + ensureNotLoaded(); + this.residentialAreaRadius = residentialAreaRadius; + this.residentialAreaSensitivity = residentialAreaSensitivity; + this.cityAreaRadius = cityAreaRadius; + this.cityAreaSensitivity = cityAreaSensitivity; + this.urbanDensityCalculationThreads = threads; + return this; + } + /** * Only valid option for in-memory graph and if you e.g. want to disable store on flush for unit * tests. Specify storeOnFlush to true if you want that existing data will be loaded FROM disc @@ -238,8 +276,8 @@ public GraphHopper setProfiles(Profile... profiles) { public GraphHopper setProfiles(List profiles) { if (!profilesByName.isEmpty()) throw new IllegalArgumentException("Cannot initialize profiles multiple times"); - if (tagParserManager != null) - throw new IllegalArgumentException("Cannot set profiles after TagParserManager was built"); + if (encodingManager != null) + throw new IllegalArgumentException("Cannot set profiles after EncodingManager was built"); for (Profile profile : profiles) { Profile previous = this.profilesByName.put(profile.getName(), profile); if (previous != null) @@ -312,18 +350,22 @@ public GraphHopper setOSMFile(String osmFile) { * * @throws IllegalStateException if graph is not instantiated. */ - public GraphHopperStorage getGraphHopperStorage() { - if (ghStorage == null) + public BaseGraph getBaseGraph() { + if (baseGraph == null) throw new IllegalStateException("GraphHopper storage not initialized"); - return ghStorage; + return baseGraph; } - public void setGraphHopperStorage(GraphHopperStorage ghStorage) { - this.ghStorage = ghStorage; + public void setBaseGraph(BaseGraph baseGraph) { + this.baseGraph = baseGraph; setFullyLoaded(); } + public StorableProperties getProperties() { + return properties; + } + /** * @return a mapping between profile names and according CH preparations. The map will be empty before loading * or import. @@ -382,8 +424,8 @@ public TranslationMap getTranslationMap() { return trMap; } - public GraphHopper setFlagEncoderFactory(FlagEncoderFactory factory) { - this.flagEncoderFactory = factory; + public GraphHopper setVehicleEncodedValuesFactory(VehicleEncodedValuesFactory factory) { + this.vehicleEncodedValuesFactory = factory; return this; } @@ -396,6 +438,15 @@ public GraphHopper setEncodedValueFactory(EncodedValueFactory factory) { return this; } + public VehicleTagParserFactory getVehicleTagParserFactory() { + return this.vehicleTagParserFactory; + } + + public GraphHopper setVehicleTagParserFactory(VehicleTagParserFactory factory) { + this.vehicleTagParserFactory = factory; + return this; + } + public TagParserFactory getTagParserFactory() { return this.tagParserFactory; } @@ -429,6 +480,13 @@ public CountryRuleFactory getCountryRuleFactory() { /** * Reads the configuration from a {@link GraphHopperConfig} object which can be manually filled, or more typically * is read from `config.yml`. + *

+ * Important note: Calling this method overwrites the configuration done in some of the setter methods of this class, + * so generally it is advised to either use this method to configure GraphHopper or the different setter methods, + * but not both. Unfortunately, this still does not cover all cases and sometimes you have to use both, but then you + * should make sure there are no conflicts. If you need both it might also help to call the init before calling the + * setters, because this way the init method won't apply defaults to configuration options you already chose using + * the setters. */ public GraphHopper init(GraphHopperConfig ghConfig) { ensureNotLoaded(); @@ -478,13 +536,16 @@ public GraphHopper init(GraphHopperConfig ghConfig) { if (!ghConfig.getString("spatial_rules.max_bbox", "").isEmpty()) throw new IllegalArgumentException("spatial_rules.max_bbox has been deprecated. There is no replacement, all custom areas will be considered."); - if (tagParserManager != null) - throw new IllegalStateException("Cannot call init twice. TagParserManager was already initialized."); setProfiles(ghConfig.getProfiles()); - String flagEncodersStr = ghConfig.getString("graph.flag_encoders", flagEncodersString); - String encodedValueStr = ghConfig.getString("graph.encoded_values", encodedValuesString); - String dateRangeParserStr = ghConfig.getString("datareader.date_range_parser_day", dateRangeParserString); - tagParserManager = buildTagParserManager(flagEncodersStr, encodedValueStr, dateRangeParserStr, profilesByName.values()); + + if (ghConfig.has("graph.vehicles") && ghConfig.has("graph.flag_encoders")) + throw new IllegalArgumentException("Remove graph.flag_encoders as it cannot be used in parallel with graph.vehicles"); + if (ghConfig.has("graph.flag_encoders")) + logger.warn("The option graph.flag_encoders is deprecated and will be removed. Replace with graph.vehicles"); + vehiclesString = ghConfig.getString("graph.vehicles", ghConfig.getString("graph.flag_encoders", vehiclesString)); + + encodedValuesString = ghConfig.getString("graph.encoded_values", encodedValuesString); + dateRangeParserString = ghConfig.getString("datareader.date_range_parser_day", dateRangeParserString); if (ghConfig.getString("graph.locktype", "native").equals("simple")) lockFactory = new SimpleFSLockFactory(); @@ -492,7 +553,10 @@ public GraphHopper init(GraphHopperConfig ghConfig) { lockFactory = new NativeFSLockFactory(); // elevation - osmReaderConfig.setSmoothElevation(ghConfig.getBool("graph.elevation.smoothing", osmReaderConfig.isSmoothElevation())); + if (ghConfig.has("graph.elevation.smoothing")) + throw new IllegalArgumentException("Use 'graph.elevation.edge_smoothing: moving_average' or the new 'graph.elevation.edge_smoothing: ramer'. See #2634."); + osmReaderConfig.setElevationSmoothing(ghConfig.getString("graph.elevation.edge_smoothing", osmReaderConfig.getElevationSmoothing())); + osmReaderConfig.setElevationSmoothingRamerMax(ghConfig.getInt("graph.elevation.edge_smoothing.ramer.max_elevation", osmReaderConfig.getElevationSmoothingRamerMax())); osmReaderConfig.setLongEdgeSamplingDistance(ghConfig.getDouble("graph.elevation.long_edge_sampling_distance", osmReaderConfig.getLongEdgeSamplingDistance())); osmReaderConfig.setElevationMaxWayPointDistance(ghConfig.getDouble("graph.elevation.way_point_max_distance", osmReaderConfig.getElevationMaxWayPointDistance())); routerConfig.setElevationWayPointMaxDistance(ghConfig.getDouble("graph.elevation.way_point_max_distance", routerConfig.getElevationWayPointMaxDistance())); @@ -504,12 +568,25 @@ public GraphHopper init(GraphHopperConfig ghConfig) { // optimizable prepare minNetworkSize = ghConfig.getInt("prepare.min_network_size", minNetworkSize); + subnetworksThreads = ghConfig.getInt("prepare.subnetworks.threads", subnetworksThreads); // prepare CH&LM chPreparationHandler.init(ghConfig); lmPreparationHandler.init(ghConfig); // osm import + // We do a few checks for import.osm.ignored_highways to prevent configuration errors when migrating from an older + // GH version. + if (!ghConfig.has("import.osm.ignored_highways")) + throw new IllegalArgumentException("Missing 'import.osm.ignored_highways'. Not using this parameter can decrease performance, see config-example.yml for more details"); + String ignoredHighwaysString = ghConfig.getString("import.osm.ignored_highways", ""); + if ((ignoredHighwaysString.contains("footway") || ignoredHighwaysString.contains("path")) && ghConfig.getProfiles().stream().map(Profile::getName).anyMatch(p -> p.contains("foot") || p.contains("hike") || p.contains("wheelchair"))) + throw new IllegalArgumentException("You should not use import.osm.ignored_highways=footway or =path in conjunction with pedestrian profiles. This is probably an error in your configuration."); + if ((ignoredHighwaysString.contains("cycleway") || ignoredHighwaysString.contains("path")) && ghConfig.getProfiles().stream().map(Profile::getName).anyMatch(p -> p.contains("mtb") || p.contains("bike"))) + throw new IllegalArgumentException("You should not use import.osm.ignored_highways=cycleway or =path in conjunction with bicycle profiles. This is probably an error in your configuration"); + + osmReaderConfig.setIgnoredHighways(Arrays.stream(ghConfig.getString("import.osm.ignored_highways", String.join(",", osmReaderConfig.getIgnoredHighways())) + .split(",")).map(String::trim).collect(Collectors.toList())); osmReaderConfig.setParseWayNames(ghConfig.getBool("datareader.instructions", osmReaderConfig.isParseWayNames())); osmReaderConfig.setPreferredLanguage(ghConfig.getString("datareader.preferred_language", osmReaderConfig.getPreferredLanguage())); osmReaderConfig.setMaxWayPointDistance(ghConfig.getDouble(Routing.INIT_WAY_POINT_MAX_DISTANCE, osmReaderConfig.getMaxWayPointDistance())); @@ -519,6 +596,13 @@ public GraphHopper init(GraphHopperConfig ghConfig) { preciseIndexResolution = ghConfig.getInt("index.high_resolution", preciseIndexResolution); maxRegionSearch = ghConfig.getInt("index.max_region_search", maxRegionSearch); + // urban density calculation + residentialAreaRadius = ghConfig.getDouble("graph.urban_density.residential_radius", residentialAreaRadius); + residentialAreaSensitivity = ghConfig.getDouble("graph.urban_density.residential_sensitivity", residentialAreaSensitivity); + cityAreaRadius = ghConfig.getDouble("graph.urban_density.city_radius", cityAreaRadius); + cityAreaSensitivity = ghConfig.getDouble("graph.urban_density.city_sensitivity", cityAreaSensitivity); + urbanDensityCalculationThreads = ghConfig.getInt("graph.urban_density.threads", urbanDensityCalculationThreads); + // routing routerConfig.setMaxVisitedNodes(ghConfig.getInt(Routing.INIT_MAX_VISITED_NODES, routerConfig.getMaxVisitedNodes())); routerConfig.setMaxRoundTripRetries(ghConfig.getInt(RoundTrip.INIT_MAX_RETRIES, routerConfig.getMaxRoundTripRetries())); @@ -533,34 +617,116 @@ public GraphHopper init(GraphHopperConfig ghConfig) { return this; } - private TagParserManager buildTagParserManager(String flagEncodersStr, String encodedValueStr, String dateRangeParserStr, Collection profiles) { - TagParserManager.Builder emBuilder = new TagParserManager.Builder(); - emBuilder.setDateRangeParser(DateRangeParser.createInstance(dateRangeParserStr)); - Map flagEncoderMap = new LinkedHashMap<>(); - for (String encoderStr : flagEncodersStr.split(",")) { - String key = encoderStr.split("\\|")[0]; - if (!key.isEmpty()) { - if (flagEncoderMap.containsKey(key)) - throw new IllegalArgumentException("FlagEncoder " + key + " needs to be unique"); - flagEncoderMap.put(key, encoderStr); - } - } - Map implicitFlagEncoderMap = new LinkedHashMap<>(); - for (Profile profile : profiles) { - emBuilder.add(Subnetwork.create(profile.getName())); - if (!flagEncoderMap.containsKey(profile.getVehicle()) - // overwrite key in implicit map if turn cost support required - && (!implicitFlagEncoderMap.containsKey(profile.getVehicle()) || profile.isTurnCosts())) - implicitFlagEncoderMap.put(profile.getVehicle(), profile.getVehicle() + (profile.isTurnCosts() ? "|turn_costs=true" : "")); + protected EncodingManager buildEncodingManager(Map vehiclesByName, List encodedValueStrings, + boolean withUrbanDensity, Collection profiles) { + EncodingManager.Builder emBuilder = new EncodingManager.Builder(); + vehiclesByName.forEach((name, vehicleStr) -> emBuilder.add(vehicleEncodedValuesFactory.createVehicleEncodedValues(name, new PMap(vehicleStr)))); + profiles.forEach(profile -> emBuilder.add(Subnetwork.create(profile.getName()))); + if (withUrbanDensity) + emBuilder.add(UrbanDensity.create()); + encodedValueStrings.forEach(s -> emBuilder.add(encodedValueFactory.create(s, new PMap()))); + return emBuilder.build(); + } + + protected OSMParsers buildOSMParsers(Map vehiclesByName, List encodedValueStrings, + List ignoredHighways, String dateRangeParserString) { + OSMParsers osmParsers = new OSMParsers(); + ignoredHighways.forEach(osmParsers::addIgnoredHighway); + for (String s : encodedValueStrings) { + TagParser tagParser = tagParserFactory.create(encodingManager, s, new PMap()); + if (tagParser != null) + osmParsers.addWayTagParser(tagParser); } - flagEncoderMap.putAll(implicitFlagEncoderMap); - flagEncoderMap.values().forEach(s -> emBuilder.addIfAbsent(flagEncoderFactory, s)); - for (String tpStr : encodedValueStr.split(",")) { - if (!tpStr.isEmpty()) emBuilder.addIfAbsent(encodedValueFactory, tagParserFactory, tpStr); + // this needs to be in sync with the default EVs added in EncodingManager.Builder#build. ideally I would like to remove + // all these defaults and just use the config as the single source of truth + if (!encodedValueStrings.contains(Roundabout.KEY)) + osmParsers.addWayTagParser(new OSMRoundaboutParser(encodingManager.getBooleanEncodedValue(Roundabout.KEY))); + if (!encodedValueStrings.contains(RoadClass.KEY)) + osmParsers.addWayTagParser(new OSMRoadClassParser(encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class))); + if (!encodedValueStrings.contains(RoadClassLink.KEY)) + osmParsers.addWayTagParser(new OSMRoadClassLinkParser(encodingManager.getBooleanEncodedValue(RoadClassLink.KEY))); + if (!encodedValueStrings.contains(RoadEnvironment.KEY)) + osmParsers.addWayTagParser(new OSMRoadEnvironmentParser(encodingManager.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class))); + if (!encodedValueStrings.contains(MaxSpeed.KEY)) + osmParsers.addWayTagParser(new OSMMaxSpeedParser(encodingManager.getDecimalEncodedValue(MaxSpeed.KEY))); + if (!encodedValueStrings.contains(RoadAccess.KEY)) + osmParsers.addWayTagParser(new OSMRoadAccessParser(encodingManager.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR))); + if (encodingManager.hasEncodedValue(AverageSlope.KEY) || encodingManager.hasEncodedValue(MaxSlope.KEY)) { + if (!encodingManager.hasEncodedValue(AverageSlope.KEY) || !encodingManager.hasEncodedValue(MaxSlope.KEY)) + throw new IllegalArgumentException("Enable both, average_slope and max_slope"); + osmParsers.addWayTagParser(new SlopeCalculator(encodingManager.getDecimalEncodedValue(MaxSlope.KEY), + encodingManager.getDecimalEncodedValue(AverageSlope.KEY))); } + if (encodingManager.hasEncodedValue(Curvature.KEY)) + osmParsers.addWayTagParser(new CurvatureCalculator(encodingManager.getDecimalEncodedValue(Curvature.KEY))); + + DateRangeParser dateRangeParser = DateRangeParser.createInstance(dateRangeParserString); + Set added = new HashSet<>(); + vehiclesByName.forEach((name, vehicleStr) -> { + VehicleTagParsers vehicleTagParsers = vehicleTagParserFactory.createParsers(encodingManager, name, + new PMap(vehicleStr).putObject("date_range_parser", dateRangeParser)); + if (vehicleTagParsers == null) + return; + vehicleTagParsers.getTagParsers().forEach(tagParser -> { + if (tagParser == null) return; + if (tagParser instanceof BikeCommonAccessParser) { + if (encodingManager.hasEncodedValue(BikeNetwork.KEY) && added.add(BikeNetwork.KEY)) + osmParsers.addRelationTagParser(relConfig -> new OSMBikeNetworkTagParser(encodingManager.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class), relConfig)); + if (encodingManager.hasEncodedValue(Smoothness.KEY) && added.add(Smoothness.KEY)) + osmParsers.addWayTagParser(new OSMSmoothnessParser(encodingManager.getEnumEncodedValue(Smoothness.KEY, Smoothness.class))); + } else if (tagParser instanceof FootAccessParser) { + if (encodingManager.hasEncodedValue(FootNetwork.KEY) && added.add(FootNetwork.KEY)) + osmParsers.addRelationTagParser(relConfig -> new OSMFootNetworkTagParser(encodingManager.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class), relConfig)); + } + String turnCostKey = TurnCost.key(new PMap(vehicleStr).getString("name", name)); + if (encodingManager.hasEncodedValue(turnCostKey) + // need to make sure we do not add the same restriction parsers multiple times + && osmParsers.getRestrictionTagParsers().stream().noneMatch(r -> r.getTurnCostEnc().getName().equals(turnCostKey))) { + List restrictions = tagParser instanceof AbstractAccessParser + ? ((AbstractAccessParser) tagParser).getRestrictions() + : OSMRoadAccessParser.toOSMRestrictions(TransportationMode.valueOf(new PMap(vehicleStr).getString("transportation_mode", "VEHICLE"))); + osmParsers.addRestrictionTagParser(new RestrictionTagParser(restrictions, encodingManager.getDecimalEncodedValue(turnCostKey))); + } + }); + vehicleTagParsers.getTagParsers().forEach(tagParser -> { + if (tagParser == null) return; + osmParsers.addWayTagParser(tagParser); + + if (tagParser instanceof BikeCommonAccessParser && encodingManager.hasEncodedValue(GetOffBike.KEY) && added.add(GetOffBike.KEY)) + osmParsers.addWayTagParser(new OSMGetOffBikeParser(encodingManager.getBooleanEncodedValue(GetOffBike.KEY), ((BikeCommonAccessParser) tagParser).getAccessEnc())); + }); + }); + return osmParsers; + } + + public static List getEncodedValueStrings(String encodedValuesStr) { + return Arrays.stream(encodedValuesStr.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + } - return emBuilder.build(); + public static Map getVehiclesByName(String vehiclesStr, Collection profiles) { + Map vehiclesMap = new LinkedHashMap<>(); + for (String encoderStr : vehiclesStr.split(",")) { + String name = encoderStr.split("\\|")[0].trim(); + if (name.isEmpty()) + continue; + if (vehiclesMap.containsKey(name)) + throw new IllegalArgumentException("Duplicate vehicle: " + name + " in: " + encoderStr); + vehiclesMap.put(name, encoderStr); + } + Map vehiclesFromProfiles = new LinkedHashMap<>(); + for (Profile profile : profiles) { + // if a profile uses a vehicle with turn costs make sure we add that vehicle with turn costs + String vehicle = profile.getVehicle().trim(); + if (!vehiclesFromProfiles.containsKey(vehicle) || profile.isTurnCosts()) + vehiclesFromProfiles.put(vehicle, vehicle + (profile.isTurnCosts() ? "|turn_costs=true" : "")); + } + // vehicles from profiles are only taken into account when they were not given explicitly + vehiclesFromProfiles.forEach(vehiclesMap::putIfAbsent); + return vehiclesMap; } private static ElevationProvider createElevationProvider(GraphHopperConfig ghConfig) { @@ -574,7 +740,9 @@ private static ElevationProvider createElevationProvider(GraphHopperConfig ghCon throw new IllegalArgumentException("use graph.elevation.cache_dir not cachedir in configuration"); ElevationProvider elevationProvider = ElevationProvider.NOOP; - if (eleProviderStr.equalsIgnoreCase("srtm")) { + if (eleProviderStr.equalsIgnoreCase("hgt")) { + elevationProvider = new HGTProvider(cacheDirStr); + } else if (eleProviderStr.equalsIgnoreCase("srtm")) { elevationProvider = new SRTMProvider(cacheDirStr); } else if (eleProviderStr.equalsIgnoreCase("cgiar")) { elevationProvider = new CGIARProvider(cacheDirStr); @@ -616,8 +784,26 @@ private static ElevationProvider createElevationProvider(GraphHopperConfig ghCon private void printInfo() { logger.info("version " + Constants.VERSION + "|" + Constants.BUILD_DATE + " (" + Constants.getVersions() + ")"); - if (ghStorage != null) - logger.info("graph " + ghStorage.toString() + ", details:" + ghStorage.toDetailsString()); + if (baseGraph != null) + logger.info("graph " + getBaseGraphString() + ", details:" + baseGraph.toDetailsString()); + } + + private String getBaseGraphString() { + return encodingManager + + "|" + baseGraph.getDirectory().getDefaultType() + + "|" + baseGraph.getNodeAccess().getDimension() + "D" + + "|" + (baseGraph.getTurnCostStorage() != null ? baseGraph.getTurnCostStorage() : "no_turn_cost") + + "|" + getVersionsString(); + } + + private String getVersionsString() { + return "nodes:" + Constants.VERSION_NODE + + ",edges:" + Constants.VERSION_EDGE + + ",geometry:" + Constants.VERSION_GEOMETRY + + ",location_index:" + Constants.VERSION_LOCATION_IDX + + ",string_index:" + Constants.VERSION_KV_STORAGE + + ",nodesCH:" + Constants.VERSION_NODE_CH + + ",shortcuts:" + Constants.VERSION_SHORTCUT; } /** @@ -652,12 +838,26 @@ public void importAndClose() { /** * Creates the graph from OSM data. */ - private void process(boolean closeEarly) { + protected void process(boolean closeEarly) { + GHDirectory directory = new GHDirectory(ghLocation, dataAccessDefaultType); + directory.configure(dataAccessConfig); + boolean withUrbanDensity = urbanDensityCalculationThreads > 0; + Map vehiclesByName = getVehiclesByName(vehiclesString, profilesByName.values()); + List encodedValueStrings = getEncodedValueStrings(encodedValuesString); + encodingManager = buildEncodingManager(vehiclesByName, encodedValueStrings, withUrbanDensity, profilesByName.values()); + osmParsers = buildOSMParsers(vehiclesByName, encodedValueStrings, osmReaderConfig.getIgnoredHighways(), dateRangeParserString); + baseGraph = new BaseGraph.Builder(getEncodingManager()) + .setDir(directory) + .set3D(hasElevation()) + .withTurnCosts(encodingManager.needsTurnCostsSupport()) + .setSegmentSize(defaultSegmentSize) + .build(); + properties = new StorableProperties(directory); + checkProfilesConsistency(); + GHLock lock = null; try { - if (ghStorage == null) - throw new IllegalStateException("GraphHopperStorage must be initialized before starting the import"); - if (ghStorage.getDirectory().getDefaultType().isStoring()) { + if (directory.getDefaultType().isStoring()) { lockFactory.setLockDir(new File(ghLocation)); lock = lockFactory.create(fileLockName, true); if (!lock.tryLock()) @@ -676,15 +876,30 @@ private void process(boolean closeEarly) { } protected void postImport() { + // Important note: To deal with via-way turn restrictions we introduce artificial edges in OSMReader (#2689). + // These are simply copies of real edges. Any further modifications of the graph edges must take care of keeping + // the artificial edges in sync with their real counterparts. So if an edge attribute shall be changed this change + // must also be applied to the corresponding artificial edge. if (sortGraph) { - GraphHopperStorage newGraph = GHUtility.newStorage(ghStorage); - GHUtility.sortDFS(ghStorage, newGraph); + BaseGraph newGraph = GHUtility.newGraph(baseGraph); + GHUtility.sortDFS(baseGraph, newGraph); logger.info("graph sorted (" + getMemInfo() + ")"); - ghStorage = newGraph; + baseGraph = newGraph; } if (hasElevation()) interpolateBridgesTunnelsAndFerries(); + + if (encodingManager.hasEncodedValue(UrbanDensity.KEY)) { + EnumEncodedValue urbanDensityEnc = encodingManager.getEnumEncodedValue(UrbanDensity.KEY, UrbanDensity.class); + if (!encodingManager.hasEncodedValue(RoadClass.KEY)) + throw new IllegalArgumentException("Urban density calculation requires " + RoadClass.KEY); + if (!encodingManager.hasEncodedValue(RoadClassLink.KEY)) + throw new IllegalArgumentException("Urban density calculation requires " + RoadClassLink.KEY); + EnumEncodedValue roadClassEnc = encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); + BooleanEncodedValue roadClassLinkEnc = encodingManager.getBooleanEncodedValue(RoadClassLink.KEY); + UrbanDensityCalculator.calcUrbanDensity(baseGraph, urbanDensityEnc, roadClassEnc, roadClassLinkEnc, residentialAreaRadius, residentialAreaSensitivity, cityAreaRadius, cityAreaSensitivity, urbanDensityCalculationThreads); + } } protected void importOSM() { @@ -699,24 +914,46 @@ protected void importOSM() { logger.info("Creating custom area index, reading custom areas from: '" + customAreasDirectory + "'"); customAreas.addAll(readCustomAreas()); } + CustomArea area = GHUtility.getFirstDuplicateArea(customAreas, Country.ISO_ALPHA3); + if (area != null) + throw new IllegalArgumentException("area used duplicate '" + Country.ISO_ALPHA3 + "' see properties: " + area.getProperties()); AreaIndex areaIndex = new AreaIndex<>(customAreas); + if (countryRuleFactory == null || countryRuleFactory.getCountryToRuleMap().isEmpty()) { + logger.info("No country rules available"); + } else { + logger.info("Applying rules for the following countries: {}", countryRuleFactory.getCountryToRuleMap().keySet()); + } logger.info("start creating graph from " + osmFile); - OSMReader reader = new OSMReader(ghStorage.getBaseGraph(), tagParserManager, osmReaderConfig).setFile(_getOSMFile()). + OSMReader reader = new OSMReader(baseGraph.getBaseGraph(), osmParsers, osmReaderConfig).setFile(_getOSMFile()). setAreaIndex(areaIndex). setElevationProvider(eleProvider). setCountryRuleFactory(countryRuleFactory); - logger.info("using " + ghStorage.toString() + ", memory:" + getMemInfo()); - ghStorage.create(100); + logger.info("using " + getBaseGraphString() + ", memory:" + getMemInfo()); + + createBaseGraphAndProperties(); + try { reader.readGraph(); } catch (IOException ex) { throw new RuntimeException("Cannot read file " + getOSMFile(), ex); } DateFormat f = createFormatter(); - ghStorage.getProperties().put("datareader.import.date", f.format(new Date())); + properties.put("datareader.import.date", f.format(new Date())); if (reader.getDataDate() != null) - ghStorage.getProperties().put("datareader.data.date", f.format(reader.getDataDate())); + properties.put("datareader.data.date", f.format(reader.getDataDate())); + + writeEncodingManagerToProperties(); + } + + protected void createBaseGraphAndProperties() { + baseGraph.getDirectory().create(); + baseGraph.create(100); + properties.create(100); + } + + protected void writeEncodingManagerToProperties() { + EncodingManager.putEncodingManagerIntoProperties(encodingManager, properties); } private List readCustomAreas() { @@ -771,48 +1008,47 @@ public boolean load() { } } + // todo: this does not really belong here, we abuse the load method to derive the dataAccessDefaultType setting from others if (!allowWrites && dataAccessDefaultType.isMMap()) dataAccessDefaultType = DAType.MMAP_RO; - if (tagParserManager == null) - // we did not call init(), so we build the tag parser manager based on the changes made to emBuilder - // and the current profiles. - // just like when calling init, users have to make sure they use the same setup for import and load - tagParserManager = buildTagParserManager(flagEncodersString, encodedValuesString, dateRangeParserString, profilesByName.values()); - - GHDirectory directory = new GHDirectory(ghLocation, dataAccessDefaultType); - directory.configure(dataAccessConfig); - ghStorage = new GraphBuilder(getEncodingManager()) - .setDir(directory) - .set3D(hasElevation()) - .withTurnCosts(tagParserManager.needsTurnCostsSupport()) - .setSegmentSize(defaultSegmentSize) - .build(); - checkProfilesConsistency(); if (!new File(ghLocation).exists()) + // there is just nothing to load return false; + GHDirectory directory = new GHDirectory(ghLocation, dataAccessDefaultType); + directory.configure(dataAccessConfig); GHLock lock = null; try { // create locks only if writes are allowed, if they are not allowed a lock cannot be created // (e.g. on a read only filesystem locks would fail) - if (ghStorage.getDirectory().getDefaultType().isStoring() && isAllowWrites()) { + if (directory.getDefaultType().isStoring() && isAllowWrites()) { lockFactory.setLockDir(new File(ghLocation)); lock = lockFactory.create(fileLockName, false); if (!lock.tryLock()) throw new RuntimeException("To avoid reading partial data we need to obtain the read lock but it failed. In " + ghLocation, lock.getObtainFailedReason()); } - - if (!ghStorage.loadExisting()) + properties = new StorableProperties(directory); + if (!properties.loadExisting()) + // the -gh folder exists, but there is no properties file. it might be just empty, so let's act as if + // the import did not run yet or is not complete for some reason return false; - - String storedProfiles = ghStorage.getProperties().get("profiles"); + encodingManager = EncodingManager.fromProperties(properties); + baseGraph = new BaseGraph.Builder(encodingManager) + .setDir(directory) + .set3D(hasElevation()) + .withTurnCosts(encodingManager.needsTurnCostsSupport()) + .setSegmentSize(defaultSegmentSize) + .build(); + baseGraph.loadExisting(); + String storedProfiles = properties.get("profiles"); String configuredProfiles = getProfilesString(); if (!storedProfiles.equals(configuredProfiles)) throw new IllegalStateException("Profiles do not match:" + "\nGraphhopper config: " + configuredProfiles + "\nGraph: " + storedProfiles - + "\nChange configuration to match the graph or delete " + ghStorage.getDirectory().getLocation()); + + "\nChange configuration to match the graph or delete " + baseGraph.getDirectory().getLocation()); + checkProfilesConsistency(); postProcessing(false); directory.loadMMap(); @@ -833,14 +1069,16 @@ private void checkProfilesConsistency() { throw new IllegalArgumentException("There has to be at least one profile"); EncodingManager encodingManager = getEncodingManager(); for (Profile profile : profilesByName.values()) { - if (!encodingManager.hasEncoder(profile.getVehicle())) { - throw new IllegalArgumentException("Unknown vehicle '" + profile.getVehicle() + "' in profile: " + profile + ". Make sure all vehicles used in 'profiles' exist in 'graph.flag_encoders'"); - } - FlagEncoder encoder = encodingManager.getEncoder(profile.getVehicle()); - if (profile.isTurnCosts() && !encoder.supportsTurnCosts()) { + if (!encodingManager.getVehicles().contains(profile.getVehicle())) + throw new IllegalArgumentException("Unknown vehicle '" + profile.getVehicle() + "' in profile: " + profile + ". " + + "Available vehicles: " + String.join(",", encodingManager.getVehicles())); + DecimalEncodedValue turnCostEnc = encodingManager.hasEncodedValue(TurnCost.key(profile.getVehicle())) + ? encodingManager.getDecimalEncodedValue(TurnCost.key(profile.getVehicle())) + : null; + if (profile.isTurnCosts() && turnCostEnc == null) { throw new IllegalArgumentException("The profile '" + profile.getName() + "' was configured with " + "'turn_costs=true', but the corresponding vehicle '" + profile.getVehicle() + "' does not support turn costs." + - "\nYou need to add `|turn_costs=true` to the vehicle in `graph.flag_encoders`"); + "\nYou need to add `|turn_costs=true` to the vehicle in `graph.vehicles`"); } try { createWeighting(profile, new PMap()); @@ -943,9 +1181,9 @@ protected void postProcessing(boolean closeEarly) { if (closeEarly) { boolean includesCustomProfiles = profilesByName.values().stream().anyMatch(p -> p instanceof CustomProfile); if (!includesCustomProfiles) - // when there are custom profiles we must not close way geometry or StringIndex, because + // when there are custom profiles we must not close way geometry or KVStorage, because // they might be needed to evaluate the custom weightings for the following preparations - ghStorage.flushAndCloseGeometryAndNameStorage(); + baseGraph.flushAndCloseGeometryAndNameStorage(); } if (lmPreparationHandler.isEnabled()) @@ -963,18 +1201,18 @@ protected void importPublicTransit() { } void interpolateBridgesTunnelsAndFerries() { - if (ghStorage.getEncodingManager().hasEncodedValue(RoadEnvironment.KEY)) { - EnumEncodedValue roadEnvEnc = ghStorage.getEncodingManager().getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class); + if (encodingManager.hasEncodedValue(RoadEnvironment.KEY)) { + EnumEncodedValue roadEnvEnc = encodingManager.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class); StopWatch sw = new StopWatch().start(); - new EdgeElevationInterpolator(ghStorage.getBaseGraph(), roadEnvEnc, RoadEnvironment.TUNNEL).execute(); + new EdgeElevationInterpolator(baseGraph.getBaseGraph(), roadEnvEnc, RoadEnvironment.TUNNEL).execute(); float tunnel = sw.stop().getSeconds(); sw = new StopWatch().start(); - new EdgeElevationInterpolator(ghStorage.getBaseGraph(), roadEnvEnc, RoadEnvironment.BRIDGE).execute(); + new EdgeElevationInterpolator(baseGraph.getBaseGraph(), roadEnvEnc, RoadEnvironment.BRIDGE).execute(); float bridge = sw.stop().getSeconds(); // The SkadiProvider contains bathymetric data. For ferries this can result in bigger elevation changes // See #2098 for mor information sw = new StopWatch().start(); - new EdgeElevationInterpolator(ghStorage.getBaseGraph(), roadEnvEnc, RoadEnvironment.FERRY).execute(); + new EdgeElevationInterpolator(baseGraph.getBaseGraph(), roadEnvEnc, RoadEnvironment.FERRY).execute(); logger.info("Bridge interpolation " + (int) bridge + "s, " + "tunnel interpolation " + (int) tunnel + "s, ferry interpolation " + (int) sw.stop().getSeconds() + "s"); } } @@ -988,7 +1226,7 @@ public final Weighting createWeighting(Profile profile, PMap hints, boolean disa } protected WeightingFactory createWeightingFactory() { - return new DefaultWeightingFactory(ghStorage.getBaseGraph(), getEncodingManager()); + return new DefaultWeightingFactory(baseGraph.getBaseGraph(), getEncodingManager()); } public GHResponse route(GHRequest request) { @@ -996,27 +1234,27 @@ public GHResponse route(GHRequest request) { } protected Router createRouter() { - if (ghStorage == null || !fullyLoaded) + if (baseGraph == null || !fullyLoaded) throw new IllegalStateException("Do a successful call to load or importOrLoad before routing"); - if (ghStorage.isClosed()) + if (baseGraph.isClosed()) throw new IllegalStateException("You need to create a new GraphHopper instance as it is already closed"); if (locationIndex == null) throw new IllegalStateException("Location index not initialized"); - return doCreateRouter(ghStorage, locationIndex, profilesByName, pathBuilderFactory, + return doCreateRouter(baseGraph, encodingManager, locationIndex, profilesByName, pathBuilderFactory, trMap, routerConfig, createWeightingFactory(), chGraphs, landmarks); } - protected Router doCreateRouter(GraphHopperStorage ghStorage, LocationIndex locationIndex, Map profilesByName, + protected Router doCreateRouter(BaseGraph baseGraph, EncodingManager encodingManager, LocationIndex locationIndex, Map profilesByName, PathDetailsBuilderFactory pathBuilderFactory, TranslationMap trMap, RouterConfig routerConfig, WeightingFactory weightingFactory, Map chGraphs, Map landmarks) { - return new Router(ghStorage.getBaseGraph(), ghStorage.getEncodingManager(), locationIndex, profilesByName, pathBuilderFactory, + return new Router(baseGraph, encodingManager, locationIndex, profilesByName, pathBuilderFactory, trMap, routerConfig, weightingFactory, chGraphs, landmarks ); } protected LocationIndex createLocationIndex(Directory dir) { - LocationIndexTree tmpIndex = new LocationIndexTree(ghStorage, dir); + LocationIndexTree tmpIndex = new LocationIndexTree(baseGraph, dir); tmpIndex.setResolution(preciseIndexResolution); tmpIndex.setMaxRegionSearch(maxRegionSearch); if (!tmpIndex.loadExisting()) { @@ -1034,23 +1272,23 @@ protected void initLocationIndex() { if (locationIndex != null) throw new IllegalStateException("Cannot initialize locationIndex twice!"); - locationIndex = createLocationIndex(ghStorage.getDirectory()); + locationIndex = createLocationIndex(baseGraph.getDirectory()); } private String getCHProfileVersion(String profile) { - return ghStorage.getProperties().get("graph.profiles.ch." + profile + ".version"); + return properties.get("graph.profiles.ch." + profile + ".version"); } private void setCHProfileVersion(String profile, int version) { - ghStorage.getProperties().put("graph.profiles.ch." + profile + ".version", version); + properties.put("graph.profiles.ch." + profile + ".version", version); } private String getLMProfileVersion(String profile) { - return ghStorage.getProperties().get("graph.profiles.lm." + profile + ".version"); + return properties.get("graph.profiles.lm." + profile + ".version"); } private void setLMProfileVersion(String profile, int version) { - ghStorage.getProperties().put("graph.profiles.lm." + profile + ".version", version); + properties.put("graph.profiles.lm." + profile + ".version", version); } protected void loadOrPrepareCH(boolean closeEarly) { @@ -1061,7 +1299,7 @@ protected void loadOrPrepareCH(boolean closeEarly) { // we load ch graphs that already exist and prepare the other ones List chConfigs = createCHConfigs(chPreparationHandler.getCHProfiles()); - Map loaded = chPreparationHandler.load(ghStorage.getBaseGraph(), chConfigs); + Map loaded = chPreparationHandler.load(baseGraph.getBaseGraph(), chConfigs); List configsToPrepare = chConfigs.stream().filter(c -> !loaded.containsKey(c.getName())).collect(Collectors.toList()); Map prepared = prepareCH(closeEarly, configsToPrepare); @@ -1073,7 +1311,7 @@ protected void loadOrPrepareCH(boolean closeEarly) { else if (prepared.containsKey(profile.getProfile())) { setCHProfileVersion(profile.getProfile(), profilesByName.get(profile.getProfile()).getVersion()); PrepareContractionHierarchies.Result res = prepared.get(profile.getProfile()); - chGraphs.put(profile.getProfile(), RoutingCHGraphImpl.fromGraph(ghStorage.getBaseGraph(), res.getCHStorage(), res.getCHConfig())); + chGraphs.put(profile.getProfile(), RoutingCHGraphImpl.fromGraph(baseGraph.getBaseGraph(), res.getCHStorage(), res.getCHConfig())); } else if (loaded.containsKey(profile.getProfile())) { chGraphs.put(profile.getProfile(), loaded.get(profile.getProfile())); } else @@ -1084,8 +1322,9 @@ else if (prepared.containsKey(profile.getProfile())) { protected Map prepareCH(boolean closeEarly, List configsToPrepare) { if (!configsToPrepare.isEmpty()) ensureWriteAccess(); - ghStorage.freeze(); - return chPreparationHandler.prepare(ghStorage, configsToPrepare, closeEarly); + if (!baseGraph.isFrozen()) + baseGraph.freeze(); + return chPreparationHandler.prepare(baseGraph, properties, configsToPrepare, closeEarly); } /** @@ -1099,7 +1338,7 @@ protected void loadOrPrepareLM(boolean closeEarly) { // we load landmark storages that already exist and prepare the other ones List lmConfigs = createLMConfigs(lmPreparationHandler.getLMProfiles()); - List loaded = lmPreparationHandler.load(lmConfigs, ghStorage.getBaseGraph(), ghStorage.getEncodingManager()); + List loaded = lmPreparationHandler.load(lmConfigs, baseGraph, encodingManager); List loadedConfigs = loaded.stream().map(LandmarkStorage::getLMConfig).collect(Collectors.toList()); List configsToPrepare = lmConfigs.stream().filter(c -> !loadedConfigs.contains(c)).collect(Collectors.toList()); List prepared = prepareLM(closeEarly, configsToPrepare); @@ -1124,19 +1363,21 @@ else if (preparedLMS.isPresent()) { protected List prepareLM(boolean closeEarly, List configsToPrepare) { if (!configsToPrepare.isEmpty()) ensureWriteAccess(); - ghStorage.freeze(); - return lmPreparationHandler.prepare(configsToPrepare, ghStorage, locationIndex, closeEarly); + if (!baseGraph.isFrozen()) + baseGraph.freeze(); + return lmPreparationHandler.prepare(configsToPrepare, baseGraph, encodingManager, properties, locationIndex, closeEarly); } /** * Internal method to clean up the graph. */ protected void cleanUp() { - PrepareRoutingSubnetworks preparation = new PrepareRoutingSubnetworks(ghStorage.getBaseGraph(), buildSubnetworkRemovalJobs()); + PrepareRoutingSubnetworks preparation = new PrepareRoutingSubnetworks(baseGraph.getBaseGraph(), buildSubnetworkRemovalJobs()); preparation.setMinNetworkSize(minNetworkSize); + preparation.setThreads(subnetworksThreads); preparation.doWork(); - ghStorage.getProperties().put("profiles", getProfilesString()); - logger.info("nodes: " + Helper.nf(ghStorage.getNodes()) + ", edges: " + Helper.nf(ghStorage.getEdges())); + properties.put("profiles", getProfilesString()); + logger.info("nodes: " + Helper.nf(baseGraph.getNodes()) + ", edges: " + Helper.nf(baseGraph.getEdges())); } private List buildSubnetworkRemovalJobs() { @@ -1144,15 +1385,16 @@ private List buildSubnetworkRemovalJobs() { for (Profile profile : profilesByName.values()) { // if turn costs are enabled use u-turn costs of zero as we only want to make sure the graph is fully connected assuming finite u-turn costs Weighting weighting = createWeighting(profile, new PMap().putObject(Parameters.Routing.U_TURN_COSTS, 0)); - jobs.add(new PrepareJob(tagParserManager.getBooleanEncodedValue(Subnetwork.key(profile.getName())), weighting)); + jobs.add(new PrepareJob(encodingManager.getBooleanEncodedValue(Subnetwork.key(profile.getName())), weighting)); } return jobs; } protected void flush() { - logger.info("flushing graph " + ghStorage.toString() + ", details:" + ghStorage.toDetailsString() + ", " + logger.info("flushing graph " + getBaseGraphString() + ", details:" + baseGraph.toDetailsString() + ", " + getMemInfo() + ")"); - ghStorage.flush(); + baseGraph.flush(); + properties.flush(); logger.info("flushed graph " + getMemInfo() + ")"); setFullyLoaded(); } @@ -1162,8 +1404,10 @@ protected void flush() { * remove the files created in graphhopperLocation you have to call clean(). */ public void close() { - if (ghStorage != null) - ghStorage.close(); + if (baseGraph != null) + baseGraph.close(); + if (properties != null) + properties.close(); chGraphs.values().forEach(RoutingCHGraph::close); landmarks.values().forEach(LandmarkStorage::close); @@ -1221,21 +1465,21 @@ public GHMatrixResponse matrix(GHMatrixRequest request) { } private RouterMatrix createMatrixRouter() { - if (ghStorage == null || !fullyLoaded) + if (baseGraph == null || !fullyLoaded) throw new IllegalStateException("Do a successful call to load or importOrLoad before routing"); - if (ghStorage.isClosed()) + if (baseGraph.isClosed()) throw new IllegalStateException("You need to create a new GraphHopper instance as it is already closed"); if (locationIndex == null) throw new IllegalStateException("Location index not initialized"); - return doCreateMatrixRouter(ghStorage, locationIndex, profilesByName, pathBuilderFactory, + return doCreateMatrixRouter(baseGraph, locationIndex, profilesByName, pathBuilderFactory, trMap, routerConfig, createWeightingFactory(), chGraphs, landmarks); } - private RouterMatrix doCreateMatrixRouter(GraphHopperStorage ghStorage, LocationIndex locationIndex, Map profilesByName, + private RouterMatrix doCreateMatrixRouter(BaseGraph baseGraph, LocationIndex locationIndex, Map profilesByName, PathDetailsBuilderFactory pathBuilderFactory, TranslationMap trMap, RouterConfig routerConfig, WeightingFactory weightingFactory, Map chGraphs, Map landmarks) { - return new RouterMatrix(ghStorage.getBaseGraph(), ghStorage.getEncodingManager(), locationIndex, profilesByName, pathBuilderFactory, + return new RouterMatrix(baseGraph, getEncodingManager(), locationIndex, profilesByName, pathBuilderFactory, trMap, routerConfig, weightingFactory, chGraphs, landmarks ); } diff --git a/core/src/main/java/com/graphhopper/isochrone/algorithm/ShortestPathTree.java b/core/src/main/java/com/graphhopper/isochrone/algorithm/ShortestPathTree.java index 69234fad877..7f51479d171 100644 --- a/core/src/main/java/com/graphhopper/isochrone/algorithm/ShortestPathTree.java +++ b/core/src/main/java/com/graphhopper/isochrone/algorithm/ShortestPathTree.java @@ -131,7 +131,7 @@ public void search(int from, final Consumer consumer) { if (traversalMode == TraversalMode.NODE_BASED) { fromMap.put(from, currentLabel); } - while (!finished()) { + while (!queueByWeighting.isEmpty()) { currentLabel = queueByWeighting.poll(); if (currentLabel.deleted) continue; @@ -191,16 +191,6 @@ private double getExploreValue(IsoLabel label) { return label.distance; } - @Override - protected boolean finished() { - return queueByWeighting.isEmpty(); - } - - @Override - protected Path extractPath() { - throw new UnsupportedOperationException(); - } - @Override public String getName() { return "reachability"; diff --git a/core/src/main/java/com/graphhopper/reader/OSMTurnRelation.java b/core/src/main/java/com/graphhopper/reader/OSMTurnRelation.java deleted file mode 100644 index 1f34ea6ec21..00000000000 --- a/core/src/main/java/com/graphhopper/reader/OSMTurnRelation.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.reader; - -import java.util.*; - -/** - * Helper object which gives node cost entries for a given OSM-relation of type "restriction" - * - * @author Karl Hübner - */ -public class OSMTurnRelation { - private final long fromOsmWayId; - private final long viaOsmNodeId; - private final long toOsmWayId; - private final Type restriction; - // vehicleTypeRestricted contains the dedicated vehicle type - // example: restriction:bus = no_left_turn => vehicleTypeRestricted = "bus"; - private String vehicleTypeRestricted; - private List vehicleTypesExcept; - - public OSMTurnRelation(long fromWayID, long viaNodeID, long toWayID, Type restrictionType) { - this.fromOsmWayId = fromWayID; - this.viaOsmNodeId = viaNodeID; - this.toOsmWayId = toWayID; - this.restriction = restrictionType; - this.vehicleTypeRestricted = ""; - this.vehicleTypesExcept = new ArrayList<>(); - } - - public long getOsmIdFrom() { - return fromOsmWayId; - } - - public long getOsmIdTo() { - return toOsmWayId; - } - - public long getViaOsmNodeId() { - return viaOsmNodeId; - } - - public Type getRestriction() { - return restriction; - } - - public String getVehicleTypeRestricted() { - return vehicleTypeRestricted; - } - - public void setVehicleTypeRestricted(String vehicleTypeRestricted) { - this.vehicleTypeRestricted = vehicleTypeRestricted; - } - - public List getVehicleTypesExcept() { - return vehicleTypesExcept; - } - - public void setVehicleTypesExcept(List vehicleTypesExcept) { - this.vehicleTypesExcept = vehicleTypesExcept; - } - - /** - * For a conditional turn restriction, test each vehicle type to verify if it is concerned. - * For a normal turn restriction (non conditional), the restriction is necessary considered. - */ - public boolean isVehicleTypeConcernedByTurnRestriction(Collection vehicleTypes) { - // if the restriction explicitly does not apply for one of the vehicles we do not accept it - if (!Collections.disjoint(vehicleTypes, vehicleTypesExcept)) { - return false; - } - return vehicleTypeRestricted.isEmpty() || vehicleTypes.contains(vehicleTypeRestricted); - } - - @Override - public String toString() { - return "*-(" + fromOsmWayId + ")->" + viaOsmNodeId + "-(" + toOsmWayId + ")->*"; - } - - public enum Type { - UNSUPPORTED, NOT, ONLY; - - private static final Map tags = new HashMap<>(); - - static { - tags.put("no_left_turn", NOT); - tags.put("no_right_turn", NOT); - tags.put("no_straight_on", NOT); - tags.put("no_u_turn", NOT); - tags.put("no_entry", NOT); - tags.put("only_right_turn", ONLY); - tags.put("only_left_turn", ONLY); - tags.put("only_straight_on", ONLY); - } - - public static Type getRestrictionType(String tag) { - Type result = null; - if (tag != null) { - result = tags.get(tag); - } - return (result != null) ? result : UNSUPPORTED; - } - } - -} diff --git a/core/src/main/java/com/graphhopper/reader/ReaderElement.java b/core/src/main/java/com/graphhopper/reader/ReaderElement.java index ab4895ed2c6..9007af9cba6 100644 --- a/core/src/main/java/com/graphhopper/reader/ReaderElement.java +++ b/core/src/main/java/com/graphhopper/reader/ReaderElement.java @@ -28,19 +28,25 @@ * @author Peter */ public abstract class ReaderElement { - public static final int NODE = 0; - public static final int WAY = 1; - public static final int RELATION = 2; - public static final int FILEHEADER = 3; - private final int type; + public enum Type { + NODE, + WAY, + RELATION, + FILEHEADER; + } + private final long id; + private final Type type; private final Map properties; - protected ReaderElement(long id, int type) { - this(id, type, new HashMap<>(4)); + protected ReaderElement(long id, Type type) { + this(id, type, new LinkedHashMap<>(4)); } - protected ReaderElement(long id, int type, Map properties) { + protected ReaderElement(long id, Type type, Map properties) { + if (id < 0) { + throw new IllegalArgumentException("Invalid OSM " + type + " Id: " + id + "; Ids must not be negative"); + } this.id = id; this.type = type; this.properties = properties; @@ -68,10 +74,10 @@ public Map getTags() { return properties; } - public void setTags(Map newTags) { + public void setTags(Map newTags) { properties.clear(); if (newTags != null) - for (Entry e : newTags.entrySet()) { + for (Entry e : newTags.entrySet()) { setTag(e.getKey(), e.getValue()); } } @@ -180,11 +186,11 @@ public void clearTags() { properties.clear(); } - public int getType() { + public Type getType() { return type; } - public boolean isType(int type) { + public boolean isType(Type type) { return this.type == type; } diff --git a/core/src/main/java/com/graphhopper/reader/ReaderNode.java b/core/src/main/java/com/graphhopper/reader/ReaderNode.java index 9a89b25e64d..acd96bc8a62 100644 --- a/core/src/main/java/com/graphhopper/reader/ReaderNode.java +++ b/core/src/main/java/com/graphhopper/reader/ReaderNode.java @@ -30,13 +30,13 @@ public class ReaderNode extends ReaderElement { private final double lon; public ReaderNode(long id, double lat, double lon) { - super(id, NODE); + super(id, Type.NODE); this.lat = lat; this.lon = lon; } public ReaderNode(long id, double lat, double lon, Map tags) { - super(id, NODE, tags); + super(id, Type.NODE, tags); this.lat = lat; this.lon = lon; } diff --git a/core/src/main/java/com/graphhopper/reader/ReaderRelation.java b/core/src/main/java/com/graphhopper/reader/ReaderRelation.java index 4011fbb530a..1d782d84c68 100644 --- a/core/src/main/java/com/graphhopper/reader/ReaderRelation.java +++ b/core/src/main/java/com/graphhopper/reader/ReaderRelation.java @@ -32,7 +32,7 @@ public class ReaderRelation extends ReaderElement { protected List members; public ReaderRelation(long id) { - super(id, RELATION, new HashMap<>(2)); + super(id, Type.RELATION, new HashMap<>(2)); } @Override @@ -50,7 +50,7 @@ public List getMembers() { public boolean isMetaRelation() { if (members != null) for (Member member : members) { - if (member.getType() == RELATION) { + if (member.getType() == Type.RELATION) { return true; } } @@ -67,14 +67,11 @@ public void add(Member member) { * Container class for relation members */ public static class Member { - public static final int NODE = 0; - public static final int WAY = 1; - public static final int RELATION = 2; - private final int type; + private final Type type; private final long ref; private final String role; - public Member(int type, long ref, String role) { + public Member(Type type, long ref, String role) { this.type = type; this.ref = ref; this.role = role; @@ -85,7 +82,7 @@ public String toString() { return "Member " + type + ":" + ref; } - public int getType() { + public Type getType() { return type; } diff --git a/core/src/main/java/com/graphhopper/reader/ReaderWay.java b/core/src/main/java/com/graphhopper/reader/ReaderWay.java index 9cb15a3d5ab..5255ff63951 100644 --- a/core/src/main/java/com/graphhopper/reader/ReaderWay.java +++ b/core/src/main/java/com/graphhopper/reader/ReaderWay.java @@ -29,7 +29,7 @@ public class ReaderWay extends ReaderElement { protected final LongArrayList nodes = new LongArrayList(5); public ReaderWay(long id) { - super(id, WAY); + super(id, Type.WAY); } public LongArrayList getNodes() { diff --git a/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java b/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java index 9e066730d35..83fcced9283 100644 --- a/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java +++ b/core/src/main/java/com/graphhopper/reader/dem/AbstractSRTMElevationProvider.java @@ -21,11 +21,11 @@ import com.graphhopper.storage.DataAccess; import com.graphhopper.util.BitUtil; import com.graphhopper.util.Downloader; +import com.graphhopper.util.Helper; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.net.SocketTimeoutException; /** @@ -97,7 +97,7 @@ public double getEle(double lat, double lon) { int minLon = down(lon); String fileName = getFileName(lat, lon); - if (fileName == null) + if (fileName == null || (Helper.isEmpty(baseUrl) && !new File(fileName).exists())) return 0; DataAccess heights = getDirectory().create("dem" + intKey); @@ -141,7 +141,10 @@ public double getEle(double lat, double lon) { private void updateHeightsFromFile(double lat, double lon, DataAccess heights) throws FileNotFoundException { try { - byte[] bytes = getByteArrayFromFile(lat, lon); + String zippedURL = baseUrl + getDownloadURL(lat, lon); + File zipFile = new File(cacheDir, new File(zippedURL).getName()); + if (!zipFile.exists()) downloadToFile(zipFile, zippedURL); + byte[] bytes = readFile(zipFile); heights.create(bytes.length); for (int bytePos = 0; bytePos < bytes.length; bytePos += 2) { // we need big endianess to read the SRTM files @@ -162,29 +165,22 @@ private void updateHeightsFromFile(double lat, double lon, DataAccess heights) t } } - private byte[] getByteArrayFromFile(double lat, double lon) throws InterruptedException, IOException { - String zippedURL = baseUrl + getDownloadURL(lat, lon); - File file = new File(cacheDir, new File(zippedURL).getName()); - InputStream is; - // get zip file if not already in cacheDir - if (!file.exists()) - for (int i = 0; i < 3; i++) { - try { - downloader.downloadFile(zippedURL, file.getAbsolutePath()); - break; - } catch (SocketTimeoutException ex) { - // just try again after a little nap - Thread.sleep(2000); - } catch (FileNotFoundException ex) { - if (zippedURL.contains(".hgt.zip")) { - zippedURL = zippedURL.replace(".hgt.zip", "hgt.zip"); - } else { - throw ex; - } + private void downloadToFile(File file, String zippedURL) throws InterruptedException, IOException { + for (int i = 0; i < 3; i++) { + try { + downloader.downloadFile(zippedURL, file.getAbsolutePath()); + break; + } catch (SocketTimeoutException ex) { + // just try again after a little nap + Thread.sleep(2000); + } catch (FileNotFoundException ex) { + if (zippedURL.contains(".hgt.zip")) { + zippedURL = zippedURL.replace(".hgt.zip", "hgt.zip"); + } else { + throw ex; } } - - return readFile(file); + } } protected String getPaddedLonString(int lonInt) { diff --git a/core/src/main/java/com/graphhopper/reader/dem/AbstractTiffElevationProvider.java b/core/src/main/java/com/graphhopper/reader/dem/AbstractTiffElevationProvider.java index 8bdc8c84973..5a4ab8e3f68 100644 --- a/core/src/main/java/com/graphhopper/reader/dem/AbstractTiffElevationProvider.java +++ b/core/src/main/java/com/graphhopper/reader/dem/AbstractTiffElevationProvider.java @@ -20,6 +20,7 @@ import com.graphhopper.storage.DataAccess; import com.graphhopper.util.Downloader; +import javax.net.ssl.SSLException; import java.awt.image.Raster; import java.io.File; import java.io.IOException; @@ -127,22 +128,24 @@ public double getEle(double lat, double lon) { } if (!loadExisting) { - String zippedURL = getDownloadURL(lat, lon); - File file = new File(cacheDir, new File(getFileNameOfLocalFile(lat, lon)).getName()); - - try { - downloadFile(file, zippedURL); - } catch (IOException e) { - demProvider.setSeaLevel(true); - // use small size on disc and in-memory - heights.create(10).flush(); - return 0; - } + File zipFile = new File(cacheDir, new File(getFileNameOfLocalFile(lat, lon)).getName()); + if (!zipFile.exists()) + try { + String zippedURL = getDownloadURL(lat, lon); + downloadToFile(zipFile, zippedURL); + } catch (SSLException ex) { + throw new IllegalStateException("SSL problem with elevation provider " + getClass().getSimpleName(), ex); + } catch (IOException ex) { + demProvider.setSeaLevel(true); + // use small size on disc and in-memory + heights.create(10).flush(); + return 0; + } // short == 2 bytes - heights.create(2 * WIDTH * HEIGHT); + heights.create(2L * WIDTH * HEIGHT); - Raster raster = generateRasterFromFile(file, name + ".tif"); + Raster raster = readFile(zipFile, name + ".tif"); fillDataAccessWithElevationData(raster, heights, WIDTH); } // loadExisting @@ -154,12 +157,12 @@ public double getEle(double lat, double lon) { return demProvider.getHeight(lat, lon); } - abstract Raster generateRasterFromFile(File file, String tifName); + abstract Raster readFile(File file, String tifName); /** * Download a file at the provided url and save it as the given downloadFile if the downloadFile does not exist. */ - private void downloadFile(File downloadFile, String url) throws IOException { + private void downloadToFile(File downloadFile, String url) throws IOException { if (!downloadFile.exists()) { int max = 3; for (int trial = 0; trial < max; trial++) { @@ -190,7 +193,7 @@ private void fillDataAccessWithElevationData(Raster raster, DataAccess heights, if (val < -1000 || val > 12000) val = Short.MIN_VALUE; - heights.setShort(2 * (y * dataAccessWidth + x), val); + heights.setShort(2 * ((long) y * dataAccessWidth + x), val); } } heights.flush(); diff --git a/core/src/main/java/com/graphhopper/reader/dem/CGIARProvider.java b/core/src/main/java/com/graphhopper/reader/dem/CGIARProvider.java index 22f25470213..30392947bc4 100644 --- a/core/src/main/java/com/graphhopper/reader/dem/CGIARProvider.java +++ b/core/src/main/java/com/graphhopper/reader/dem/CGIARProvider.java @@ -94,7 +94,7 @@ public static void main(String[] args) { } @Override - Raster generateRasterFromFile(File file, String tifName) { + Raster readFile(File file, String tifName) { SeekableStream ss = null; try { InputStream is = new FileInputStream(file); diff --git a/core/src/main/java/com/graphhopper/reader/dem/EdgeElevationSmoothing.java b/core/src/main/java/com/graphhopper/reader/dem/EdgeElevationSmoothing.java new file mode 100644 index 00000000000..924402126a6 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/dem/EdgeElevationSmoothing.java @@ -0,0 +1,126 @@ +package com.graphhopper.reader.dem; + +import com.graphhopper.util.DistanceCalcEarth; +import com.graphhopper.util.DistancePlaneProjection; +import com.graphhopper.util.PointList; + +/** + * The ElevationData is read from rectangular tiles. Especially when going along a cliff, + * valley, or pass, it can happen that a small part of the road contains incorrect elevation data. + * This is because the elevation data is coarse and sometimes contains errors. + * + * @author Robin Boldt + */ +public class EdgeElevationSmoothing { + + // If the point is farther then this, we stop averaging + private final static int MAX_SEARCH_DISTANCE = 150; + + /** + * This method smooths the elevation data of a PointList by calculating the average elevation over + * multiple points of that PointList. + */ + public static void smoothMovingAverage(PointList geometry) { + for (int i = 1; i < geometry.size() - 1; i++) { + + int start = i; + for (int j = i - 1; j >= 0; j--) { + if (MAX_SEARCH_DISTANCE > DistancePlaneProjection.DIST_PLANE.calcDist(geometry.getLat(i), geometry.getLon(i), geometry.getLat(j), geometry.getLon(j))) { + start = j; + } else { + break; + } + } + + int end = i; + for (int j = i + 1; j < geometry.size(); j++) { + if (MAX_SEARCH_DISTANCE > DistancePlaneProjection.DIST_PLANE.calcDist(geometry.getLat(i), geometry.getLon(i), geometry.getLat(j), geometry.getLon(j))) { + // +1 because the end is exclusive + end = j + 1; + } else { + break; + } + } + + // In this case we cannot find any points within the max search distance, so we simply skip this point + if (start == end) + continue; + + double sum = 0; + for (int j = start; j < end; j++) { + // We skip points that are too far away, important for motorways + if (MAX_SEARCH_DISTANCE > DistancePlaneProjection.DIST_PLANE.calcDist(geometry.getLat(i), geometry.getLon(i), geometry.getLat(j), geometry.getLon(j))) { + sum += geometry.getEle(j); + } + } + double smoothed = sum / (end - start); + geometry.setElevation(i, smoothed); + } + } + + /** + * This method removes elevation fluctuations up to maxElevationDelta. Compared to the smoothMovingAverage function + * this method has the advantage that the maximum slope of a PointList never increases (max(abs(slope_i))). + * The disadvantage is that the appearance might be still more spiky (at tower nodes) as a result when a bigger + * positive slope changes to a bigger negative slope. + *

+ * The underlying algorithm is an adapted Ramer-Douglas-Peucker algorithm (see #2634) with a maximum elevation change and: + * 1. only elevation changes are considered and any lat,lon difference is ignored + * 2. instead of removing the point the elevation will be calculated from the average slope of the first and last + * point of the specified pointList + */ + public static void smoothRamer(PointList pointList, double maxElevationDelta) { + internSmoothRamer(pointList, 0, pointList.size() - 1, maxElevationDelta); + } + + static void internSmoothRamer(PointList pointList, int fromIndex, int lastIndex, double maxElevationDelta) { + if (lastIndex - fromIndex < 2) + return; + + double prevLat = pointList.getLat(fromIndex); + double prevLon = pointList.getLon(fromIndex); + double dist2D = DistanceCalcEarth.DIST_EARTH.calcDist(prevLat, prevLon, pointList.getLat(lastIndex), pointList.getLon(lastIndex)); + + // in rare cases the first point can be identical to the last for e.g. areas (or for things like man_made=pier which are not explicitly excluded from adding edges) + double averageSlope = dist2D == 0 ? 0 : (pointList.getEle(lastIndex) - pointList.getEle(fromIndex)) / dist2D; + double prevAverageSlopeEle = pointList.getEle(fromIndex); + double maxEleDelta = -1; + int indexWithMaxDelta = -1; + for (int i = fromIndex + 1; i < lastIndex; i++) { + double lat = pointList.getLat(i); + double lon = pointList.getLon(i); + double ele = pointList.getEle(i); + double tmpDist2D = DistanceCalcEarth.DIST_EARTH.calcDist(prevLat, prevLon, lat, lon); + double eleFromAverageSlope = averageSlope * tmpDist2D + prevAverageSlopeEle; + double tmpEleDelta = Math.abs(ele - eleFromAverageSlope); + if (maxEleDelta < tmpEleDelta) { + indexWithMaxDelta = i; + maxEleDelta = tmpEleDelta; + } + prevAverageSlopeEle = eleFromAverageSlope; + prevLat = lat; + prevLon = lon; + } + + // the maximum elevation change limit filters away especially the smaller high frequent elevation changes, + // which is likely the "noise" that we want to remove. + if (indexWithMaxDelta < 0 || maxElevationDelta > maxEleDelta) { + prevLat = pointList.getLat(fromIndex); + prevLon = pointList.getLon(fromIndex); + prevAverageSlopeEle = pointList.getEle(fromIndex); + for (int i = fromIndex + 1; i < lastIndex; i++) { + double lat = pointList.getLat(i); + double lon = pointList.getLon(i); + double tmpDist2D = DistanceCalcEarth.DIST_EARTH.calcDist(prevLat, prevLon, lat, lon); + double eleFromAverageSlope = averageSlope * tmpDist2D + prevAverageSlopeEle; + pointList.setElevation(i, eleFromAverageSlope); + prevAverageSlopeEle = eleFromAverageSlope; + prevLat = lat; + prevLon = lon; + } + } else { + internSmoothRamer(pointList, fromIndex, indexWithMaxDelta, maxElevationDelta); + internSmoothRamer(pointList, indexWithMaxDelta, lastIndex, maxElevationDelta); + } + } +} diff --git a/core/src/main/java/com/graphhopper/reader/dem/GMTEDProvider.java b/core/src/main/java/com/graphhopper/reader/dem/GMTEDProvider.java index 0b46ca0e3aa..e1ed2fb20ed 100644 --- a/core/src/main/java/com/graphhopper/reader/dem/GMTEDProvider.java +++ b/core/src/main/java/com/graphhopper/reader/dem/GMTEDProvider.java @@ -139,7 +139,7 @@ public static void main(String[] args) { } @Override - Raster generateRasterFromFile(File file, String tifName) { + Raster readFile(File file, String tifName) { SeekableStream ss = null; try { InputStream is = new FileInputStream(file); diff --git a/core/src/main/java/com/graphhopper/reader/dem/GraphElevationSmoothing.java b/core/src/main/java/com/graphhopper/reader/dem/GraphElevationSmoothing.java deleted file mode 100644 index 45c4db2ca45..00000000000 --- a/core/src/main/java/com/graphhopper/reader/dem/GraphElevationSmoothing.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.graphhopper.reader.dem; - -import com.graphhopper.util.DistancePlaneProjection; -import com.graphhopper.util.PointList; - -/** - * This class smooths the elevation data of a PointList by calculating the average elevation over - * multiple points of that PointList. - *

- * The ElevationData is read from rectangular tiles. Especially when going along a cliff, - * valley, or pass, it can happen that a small part of the road contains incorrect elevation data. - * This is because the elevation data is coarse and sometimes contains errors. - *

- * This can lead to incorrect ascend, descend, and distance calculation of a route. - * - * @author Robin Boldt - */ -public class GraphElevationSmoothing { - - // If the point is farther then this, we stop averaging - private final static int MAX_SEARCH_DISTANCE = 150; - - public static PointList smoothElevation(PointList geometry) { - for (int i = 1; i < geometry.size() - 1; i++) { - - int start = i; - for (int j = i-1; j >= 0 ; j--) { - if (MAX_SEARCH_DISTANCE > DistancePlaneProjection.DIST_PLANE.calcDist(geometry.getLat(i), geometry.getLon(i), geometry.getLat(j), geometry.getLon(j))) { - start = j; - }else{ - break; - } - } - - int end = i; - for (int j = i+1; j < geometry.size(); j++) { - if (MAX_SEARCH_DISTANCE > DistancePlaneProjection.DIST_PLANE.calcDist(geometry.getLat(i), geometry.getLon(i), geometry.getLat(j), geometry.getLon(j))) { - // +1 because the end is exclusive - end = j+1; - }else{ - break; - } - } - - // In this case we cannot find any points within the max search distance, so we simply skip this point - if(start == end) - continue; - - double sum = 0; - for (int j = start; j < end; j++) { - // We skip points that are too far away, important for motorways - if (MAX_SEARCH_DISTANCE > DistancePlaneProjection.DIST_PLANE.calcDist(geometry.getLat(i), geometry.getLon(i), geometry.getLat(j), geometry.getLon(j))) { - sum += geometry.getEle(j); - } - } - double smoothed = sum / (end-start); - geometry.setElevation(i, smoothed); - } - return geometry; - } - -} diff --git a/core/src/main/java/com/graphhopper/reader/dem/HGTProvider.java b/core/src/main/java/com/graphhopper/reader/dem/HGTProvider.java new file mode 100644 index 00000000000..80e53cf5be2 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/dem/HGTProvider.java @@ -0,0 +1,60 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.dem; + +import com.graphhopper.util.Helper; + +import java.io.*; +import java.nio.file.Files; +import java.util.zip.ZipInputStream; + +public class HGTProvider extends AbstractSRTMElevationProvider { + public HGTProvider(String dir) { + super("", dir, "", Integer.MIN_VALUE, Integer.MAX_VALUE, 3601); + } + + @Override + byte[] readFile(File file) throws IOException { + InputStream is = Files.newInputStream(file.toPath()); + ZipInputStream zis = new ZipInputStream(is); + zis.getNextEntry(); + BufferedInputStream buff = new BufferedInputStream(zis); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + byte[] buffer = new byte[0xFFFF]; + int len; + while ((len = buff.read(buffer)) > 0) { + os.write(buffer, 0, len); + } + os.flush(); + Helper.close(buff); + return os.toByteArray(); + } + + @Override + String getFileName(double lat, double lon) { + int latInt = (int) Math.floor(lat); + int lonInt = (int) Math.floor(lon); + return cacheDir + "/" + (lat > 0 ? "N" : "S") + getPaddedLatString(latInt) + (lon > 0 ? "E" : "W") + getPaddedLonString(lonInt) + ".hgt.zip"; + } + + @Override + String getDownloadURL(double lat, double lon) { + return getFileName(lat, lon); + } +} diff --git a/core/src/main/java/com/graphhopper/reader/dem/HeightTile.java b/core/src/main/java/com/graphhopper/reader/dem/HeightTile.java index 114469d9378..61b0a2723cd 100644 --- a/core/src/main/java/com/graphhopper/reader/dem/HeightTile.java +++ b/core/src/main/java/com/graphhopper/reader/dem/HeightTile.java @@ -24,7 +24,6 @@ import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.util.concurrent.atomic.AtomicInteger; /** * One rectangle of height data from Shuttle Radar Topography Mission. @@ -81,7 +80,7 @@ void setHeights(DataAccess da) { private short getHeightSample(int x, int y) { // always keep in mind factor 2 because of short value - return heights.getShort(2 * (y * width + x)); + return heights.getShort(2L * ((long) y * width + x)); } private boolean isValidElevation(double elevation) { diff --git a/core/src/main/java/com/graphhopper/reader/dem/SRTMGL1Provider.java b/core/src/main/java/com/graphhopper/reader/dem/SRTMGL1Provider.java index e448c1ffa6e..091227b181b 100644 --- a/core/src/main/java/com/graphhopper/reader/dem/SRTMGL1Provider.java +++ b/core/src/main/java/com/graphhopper/reader/dem/SRTMGL1Provider.java @@ -31,7 +31,7 @@ *

* When using this data we have to acknowledge: * This material is based on data services provided by the OpenTopography Facility with support from the - * National Science Foundation under NSF Award Numbers 1226353 & 1225810 + * National Science Foundation under NSF Award Numbers 1226353 & 1225810 * National Geospatial-Intelligence Agency (NGA) and the National Aeronautics and Space Administration (NASA), 2013, * SRTMGL1: NASA Shuttle Radar Topography Mission Global 1 arc second V003. [Version]. NASA EOSDIS Land Processes DAAC, * USGS Earth Resources Observation and Science (EROS) Center, Sioux Falls, South Dakota (https://lpdaac.usgs.gov), diff --git a/core/src/main/java/com/graphhopper/reader/osm/GraphRestriction.java b/core/src/main/java/com/graphhopper/reader/osm/GraphRestriction.java new file mode 100644 index 00000000000..81f25262029 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/GraphRestriction.java @@ -0,0 +1,101 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.carrotsearch.hppc.IntArrayList; + +/** + * Basically an OSM restriction, but in 'graph-representation', i.e. it is expressed in terms of graph node/edge IDs + * instead of OSM way IDs. There can be via-node-restrictions (with a single via-node) and via-way/edge-restrictions + * (with one or more via-edges). There can also be multiple from- or to-edges to represent OSM restrictions like + * no_entry or no_exit that use multiple from- or to-members. + *

+ * We store a list of via-nodes even for via-way restrictions. It stores the nodes connecting the via-ways, + * see {@link WayToEdgeConverter.EdgeResult}. For via-node restrictions the list simply contains the single via node. + *

+ * This class only contains the 'topology' of the restriction. The {@link RestrictionType} is handled separately, + * because opposite to the type the topology does not depend on the vehicle type. + */ +public class GraphRestriction { + private final boolean isViaWayRestriction; + private final IntArrayList viaNodes; + private final IntArrayList fromEdges; + private final IntArrayList viaEdges; + private final IntArrayList toEdges; + + public static GraphRestriction node(int fromEdge, int viaNode, int toEdge) { + return node(IntArrayList.from(fromEdge), viaNode, IntArrayList.from(toEdge)); + } + + public static GraphRestriction node(IntArrayList fromEdges, int viaNode, IntArrayList toEdges) { + return new GraphRestriction(false, IntArrayList.from(viaNode), fromEdges, null, toEdges); + } + + public static GraphRestriction way(int fromEdge, int viaEdge, int toEdge, IntArrayList viaNodes) { + return way(fromEdge, IntArrayList.from(viaEdge), toEdge, viaNodes); + } + + public static GraphRestriction way(int fromEdge, IntArrayList viaEdges, int toEdge, IntArrayList viaNodes) { + return way(IntArrayList.from(fromEdge), viaEdges, IntArrayList.from(toEdge), viaNodes); + } + + public static GraphRestriction way(IntArrayList fromEdges, IntArrayList viaEdges, IntArrayList toEdges, IntArrayList viaNodes) { + return new GraphRestriction(true, viaNodes, fromEdges, viaEdges, toEdges); + } + + private GraphRestriction(boolean isViaWayRestriction, IntArrayList viaNodes, IntArrayList fromEdges, IntArrayList viaEdges, IntArrayList toEdges) { + if (fromEdges.size() > 1 && toEdges.size() > 1) + throw new IllegalArgumentException("fromEdges and toEdges cannot be size > 1 at the same time"); + if (fromEdges.isEmpty() || toEdges.isEmpty()) + throw new IllegalArgumentException("fromEdges and toEdges must not be empty"); + if (!isViaWayRestriction && viaNodes.size() != 1) + throw new IllegalArgumentException("for node restrictions there must be exactly one via node"); + if (!isViaWayRestriction && viaEdges != null) + throw new IllegalArgumentException("for node restrictions the viaEdges must be null"); + if (isViaWayRestriction && viaEdges.isEmpty()) + throw new IllegalArgumentException("for way restrictions there must at least one via edge"); + if (isViaWayRestriction && viaNodes.size() != viaEdges.size() + 1) + throw new IllegalArgumentException("for way restrictions there must be one via node more than there are via edges"); + this.isViaWayRestriction = isViaWayRestriction; + this.viaNodes = viaNodes; + this.fromEdges = fromEdges; + this.viaEdges = viaEdges; + this.toEdges = toEdges; + } + + public boolean isViaWayRestriction() { + return isViaWayRestriction; + } + + public IntArrayList getViaNodes() { + return viaNodes; + } + + public IntArrayList getFromEdges() { + return fromEdges; + } + + public IntArrayList getViaEdges() { + return viaEdges; + } + + public IntArrayList getToEdges() { + return toEdges; + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMFileHeader.java b/core/src/main/java/com/graphhopper/reader/osm/OSMFileHeader.java index d88b4cbf174..85a0677319b 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/OSMFileHeader.java +++ b/core/src/main/java/com/graphhopper/reader/osm/OSMFileHeader.java @@ -31,7 +31,7 @@ */ public class OSMFileHeader extends ReaderElement { public OSMFileHeader() { - super(0, FILEHEADER); + super(0, Type.FILEHEADER); } /** diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMInputFile.java b/core/src/main/java/com/graphhopper/reader/osm/OSMInputFile.java index 296d39f07ec..1741ee5cdaf 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/OSMInputFile.java +++ b/core/src/main/java/com/graphhopper/reader/osm/OSMInputFile.java @@ -55,6 +55,7 @@ public class OSMInputFile implements Sink, OSMInput { private Thread pbfReaderThread; private boolean hasIncomingData; private int workerThreads = -1; + private SkipOptions skipOptions = SkipOptions.none(); private OSMFileHeader fileheader; public OSMInputFile(File file) throws IOException { @@ -73,13 +74,22 @@ public OSMInputFile open() throws XMLStreamException { } /** - * Currently on for pbf format. Default is number of cores. + * Currently only for pbf format. Default is number of cores. */ public OSMInputFile setWorkerThreads(int threads) { workerThreads = threads; return this; } + /** + * Use this to prevent the creation of OSM nodes, ways and/or relations to speed up the file reading process. + * This will only affect the reading of pbf files. + */ + public OSMInputFile setSkipOptions(SkipOptions skipOptions) { + this.skipOptions = skipOptions; + return this; + } + @SuppressWarnings("unchecked") private InputStream decode(File file) throws IOException { final String name = file.getName(); @@ -247,7 +257,7 @@ private void openPBFReader(InputStream stream) { if (workerThreads <= 0) workerThreads = 1; - pbfReader = new PbfReader(stream, this, workerThreads); + pbfReader = new PbfReader(stream, this, workerThreads, skipOptions); pbfReaderThread = new Thread(pbfReader, "PBF Reader"); pbfReaderThread.start(); } diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMNodeData.java b/core/src/main/java/com/graphhopper/reader/osm/OSMNodeData.java index b00129d9fa3..3c442c01e84 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/OSMNodeData.java +++ b/core/src/main/java/com/graphhopper/reader/osm/OSMNodeData.java @@ -18,22 +18,23 @@ package com.graphhopper.reader.osm; +import com.carrotsearch.hppc.LongScatterSet; +import com.carrotsearch.hppc.LongSet; import com.graphhopper.coll.GHLongIntBTree; import com.graphhopper.coll.LongIntMap; -import com.graphhopper.reader.PillarInfo; import com.graphhopper.reader.ReaderNode; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.Directory; import com.graphhopper.util.PointAccess; import com.graphhopper.util.PointList; import com.graphhopper.util.shapes.GHPoint3D; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.HashMap; import java.util.Map; +import java.util.function.DoubleSupplier; import java.util.function.IntUnaryOperator; - -import static java.util.Collections.emptyMap; +import java.util.stream.Collectors; /** * This class stores OSM node data while reading an OSM file in {@link WaySegmentParser}. It is not trivial to do this @@ -67,12 +68,13 @@ class OSMNodeData { private final PillarInfo pillarNodes; private final PointAccess towerNodes; - // this map stores an index for each OSM node we keep the node tags of. a value of -1 means there is no entry - // yet and a value of -2 means there was an entry but it was removed again + // this map stores an index for each OSM node we keep the node tags of. a value of -1 means there is no entry yet. private final LongIntMap nodeTagIndicesByOsmNodeIds; // stores node tags - private final List> nodeTags; + private final KVStorage nodeKVStorage; + // collect all nodes that should be split and a barrier edge should be created between them. + private final LongSet nodesToBeSplit; private int nextTowerId = 0; private int nextPillarId = 0; @@ -80,15 +82,16 @@ class OSMNodeData { private long nextArtificialOSMNodeId = -Long.MAX_VALUE; public OSMNodeData(PointAccess nodeAccess, Directory directory) { - // we use GHLongIntBTree, because it is based on a tree, not an array, so it can store as many entries as there - // are longs. this also makes it memory efficient, because there is no need to pre-allocate memory for empty - // entries. + // We use GHLongIntBTree, because it is based on a tree, not an array, so it can store as many entries as there + // are longs. This also makes it memory efficient, because there is no need to pre-allocate memory for empty + // entries, and it also avoids allocating a new array and copying into it when increasing the size. idsByOsmNodeIds = new GHLongIntBTree(200); towerNodes = nodeAccess; pillarNodes = new PillarInfo(towerNodes.is3D(), directory); nodeTagIndicesByOsmNodeIds = new GHLongIntBTree(200); - nodeTags = new ArrayList<>(); + nodesToBeSplit = new LongScatterSet(); + nodeKVStorage = new KVStorage(directory, false); } public boolean is3D() { @@ -132,11 +135,15 @@ public long getNodeCount() { return idsByOsmNodeIds.getSize(); } + public long getTaggedNodeCount() { + return nodeTagIndicesByOsmNodeIds.getSize(); + } + /** * @return the number of nodes for which we store tags */ - public long getTaggedNodeCount() { - return nodeTags.size(); + public long getNodeTagCapacity() { + return nodeKVStorage.getCapacity(); } /** @@ -145,14 +152,14 @@ public long getTaggedNodeCount() { * * @return the node type this OSM node was associated with before this method was called */ - public int addCoordinatesIfMapped(long osmNodeId, double lat, double lon, double ele) { + public int addCoordinatesIfMapped(long osmNodeId, double lat, double lon, DoubleSupplier getEle) { int nodeType = idsByOsmNodeIds.get(osmNodeId); if (nodeType == EMPTY_NODE) return nodeType; else if (nodeType == JUNCTION_NODE || nodeType == CONNECTION_NODE) - addTowerNode(osmNodeId, lat, lon, ele); + addTowerNode(osmNodeId, lat, lon, getEle.getAsDouble()); else if (nodeType == INTERMEDIATE_NODE || nodeType == END_NODE) - addPillarNode(osmNodeId, lat, lon, ele); + addPillarNode(osmNodeId, lat, lon, getEle.getAsDouble()); else throw new IllegalStateException("Unknown node type: " + nodeType + ", or coordinates already set. Possibly duplicate OSM node ID: " + osmNodeId); return nodeType; @@ -187,7 +194,7 @@ SegmentNode addCopyOfNode(SegmentNode node) { if (idsByOsmNodeIds.put(newOsmId, INTERMEDIATE_NODE) != EMPTY_NODE) throw new IllegalStateException("Artificial osm node id already exists: " + newOsmId); int id = addPillarNode(newOsmId, point.getLat(), point.getLon(), point.getEle()); - return new SegmentNode(newOsmId, id); + return new SegmentNode(newOsmId, id, node.tags); } int convertPillarToTowerNode(int id, long osmNodeId) { @@ -241,11 +248,13 @@ public void addCoordinatesToPointList(int id, PointList pointList) { public void setTags(ReaderNode node) { int tagIndex = nodeTagIndicesByOsmNodeIds.get(node.getId()); - if (tagIndex == -2) - throw new IllegalStateException("Cannot add tags after they were removed"); - else if (tagIndex == -1) { - nodeTagIndicesByOsmNodeIds.put(node.getId(), nodeTags.size()); - nodeTags.add(node.getTags()); + if (tagIndex == -1) { + long pointer = nodeKVStorage.add(node.getTags().entrySet().stream().map(m -> new KVStorage.KeyValue(m.getKey(), + m.getValue() instanceof String ? KVStorage.cutString((String) m.getValue()) : m.getValue())). + collect(Collectors.toList())); + if (pointer > Integer.MAX_VALUE) + throw new IllegalStateException("Too many key value pairs are stored in node tags, was " + pointer); + nodeTagIndicesByOsmNodeIds.put(node.getId(), (int) pointer); } else { throw new IllegalStateException("Cannot add tags twice, duplicate node OSM ID: " + node.getId()); } @@ -255,16 +264,12 @@ public Map getTags(long osmNodeId) { int tagIndex = nodeTagIndicesByOsmNodeIds.get(osmNodeId); if (tagIndex < 0) return Collections.emptyMap(); - return nodeTags.get(tagIndex); - } - - public void removeTags(long osmNodeId) { - int prev = nodeTagIndicesByOsmNodeIds.put(osmNodeId, -2); - nodeTags.set(prev, emptyMap()); + return nodeKVStorage.getMap(tagIndex); } public void release() { pillarNodes.clear(); + nodeKVStorage.clear(); } public int towerNodeToId(int towerId) { @@ -282,4 +287,18 @@ public int pillarNodeToId(int pillarId) { public int idToPillarNode(int id) { return id - 3; } + + public boolean setSplitNode(long osmNodeId) { + return nodesToBeSplit.add(osmNodeId); + } + + public void unsetSplitNode(long osmNodeId) { + int removed = nodesToBeSplit.removeAll(osmNodeId); + if (removed == 0) + throw new IllegalStateException("Node " + osmNodeId + " was not a split node"); + } + + public boolean isSplitNode(long osmNodeId) { + return nodesToBeSplit.contains(osmNodeId); + } } diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java index 5d7bbeefc99..5ca65dd7b46 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java +++ b/core/src/main/java/com/graphhopper/reader/osm/OSMReader.java @@ -17,23 +17,29 @@ */ package com.graphhopper.reader.osm; -import com.carrotsearch.hppc.IntLongMap; +import com.carrotsearch.hppc.IntIntMap; import com.carrotsearch.hppc.LongArrayList; -import com.graphhopper.coll.GHIntLongHashMap; -import com.graphhopper.coll.GHLongHashSet; +import com.carrotsearch.hppc.LongHashSet; +import com.carrotsearch.hppc.LongSet; +import com.carrotsearch.hppc.cursors.LongCursor; import com.graphhopper.coll.GHLongLongHashMap; -import com.graphhopper.reader.*; +import com.graphhopper.reader.ReaderElement; +import com.graphhopper.reader.ReaderNode; +import com.graphhopper.reader.ReaderRelation; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.dem.EdgeElevationSmoothing; import com.graphhopper.reader.dem.EdgeSampling; import com.graphhopper.reader.dem.ElevationProvider; -import com.graphhopper.reader.dem.GraphElevationSmoothing; import com.graphhopper.routing.OSMReaderConfig; import com.graphhopper.routing.ev.Country; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.util.AreaIndex; import com.graphhopper.routing.util.CustomArea; -import com.graphhopper.routing.util.TagParserManager; +import com.graphhopper.routing.util.OSMParsers; import com.graphhopper.routing.util.countryrules.CountryRule; import com.graphhopper.routing.util.countryrules.CountryRuleFactory; -import com.graphhopper.routing.util.parsers.TurnCostParser; +import com.graphhopper.routing.util.parsers.RestrictionSetter; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.IntsRef; import com.graphhopper.storage.NodeAccess; @@ -49,7 +55,9 @@ import java.util.*; import java.util.function.LongToIntFunction; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import static com.graphhopper.search.KVStorage.KeyValue.*; import static com.graphhopper.util.Helper.nf; import static java.util.Collections.emptyList; @@ -69,38 +77,42 @@ public class OSMReader { private final OSMReaderConfig config; private final BaseGraph baseGraph; + private final EdgeIntAccess edgeIntAccess; private final NodeAccess nodeAccess; private final TurnCostStorage turnCostStorage; - private final TagParserManager tagParserManager; + private final OSMParsers osmParsers; private final DistanceCalc distCalc = DistanceCalcEarth.DIST_EARTH; + private final RestrictionSetter restrictionSetter; private ElevationProvider eleProvider = ElevationProvider.NOOP; private AreaIndex areaIndex; private CountryRuleFactory countryRuleFactory = null; private File osmFile; - private final DouglasPeucker simplifyAlgo = new DouglasPeucker(); + private final RamerDouglasPeucker simplifyAlgo = new RamerDouglasPeucker(); private final IntsRef tempRelFlags; private Date osmDataDate; private long zeroCounter = 0; private GHLongLongHashMap osmWayIdToRelationFlagsMap = new GHLongLongHashMap(200, .5f); - // stores osm way ids used by relations to identify which edge ids needs to be mapped later - private GHLongHashSet osmWayIdSet = new GHLongHashSet(); - private IntLongMap edgeIdToOsmWayIdMap; + private WayToEdgesMap restrictedWaysToEdgesMap = new WayToEdgesMap(); + private List restrictionRelations = new ArrayList<>(); - public OSMReader(BaseGraph baseGraph, TagParserManager tagParserManager, OSMReaderConfig config) { + public OSMReader(BaseGraph baseGraph, OSMParsers osmParsers, OSMReaderConfig config) { this.baseGraph = baseGraph; + this.edgeIntAccess = baseGraph.createEdgeIntAccess(); this.config = config; this.nodeAccess = baseGraph.getNodeAccess(); - this.tagParserManager = tagParserManager; + this.osmParsers = osmParsers; + this.restrictionSetter = new RestrictionSetter(baseGraph); simplifyAlgo.setMaxDistance(config.getMaxWayPointDistance()); simplifyAlgo.setElevationMaxDistance(config.getElevationMaxWayPointDistance()); turnCostStorage = baseGraph.getTurnCostStorage(); - tempRelFlags = tagParserManager.createRelationFlags(); + tempRelFlags = osmParsers.createRelationFlags(); if (tempRelFlags.length != 2) - throw new IllegalArgumentException("Cannot use relation flags with != 2 integers"); + // we use a long to store relation flags currently, so the relation flags ints ref must have length 2 + throw new IllegalArgumentException("OSMReader cannot use relation flags with != 2 integers"); } /** @@ -136,8 +148,8 @@ public OSMReader setCountryRuleFactory(CountryRuleFactory countryRuleFactory) { } public void readGraph() throws IOException { - if (tagParserManager == null) - throw new IllegalStateException("Encoding manager was not set."); + if (osmParsers == null) + throw new IllegalStateException("Tag parsers were not set."); if (osmFile == null) throw new IllegalStateException("No OSM file specified"); @@ -163,9 +175,11 @@ public void readGraph() throws IOException { osmDataDate = waySegmentParser.getTimeStamp(); if (baseGraph.getNodes() == 0) throw new RuntimeException("Graph after reading OSM must not be empty"); + releaseEverythingExceptRestrictionData(); + addRestrictionsToGraph(); + releaseRestrictionData(); LOGGER.info("Finished reading OSM file: {}, nodes: {}, edges: {}, zero distance edges: {}", osmFile.getAbsolutePath(), nf(baseGraph.getNodes()), nf(baseGraph.getEdges()), nf(zeroCounter)); - finishedReading(); } /** @@ -188,7 +202,7 @@ protected boolean acceptWay(ReaderWay way) { if (!way.hasTags()) return false; - return tagParserManager.acceptWay(way); + return osmParsers.acceptWay(way); } /** @@ -196,7 +210,7 @@ protected boolean acceptWay(ReaderWay way) { * junction between different ways this will be ignored and no artificial edge will be created. */ protected boolean isBarrierNode(ReaderNode node) { - return node.getTags().containsKey("barrier") || node.getTags().containsKey("ford"); + return node.hasTag("barrier") || node.hasTag("ford"); } /** @@ -214,7 +228,7 @@ private boolean isFerry(ReaderWay way) { * This method is called during the second pass of {@link WaySegmentParser} and provides an entry point to enrich * the given OSM way with additional tags before it is passed on to the tag parsers. */ - protected void setArtificialWayTags(PointList pointList, ReaderWay way, double distance, Map nodeTags) { + protected void setArtificialWayTags(PointList pointList, ReaderWay way, double distance, List> nodeTags) { way.setTag("node_tags", nodeTags); way.setTag("edge_distance", distance); way.setTag("point_list", pointList); @@ -246,13 +260,19 @@ protected void setArtificialWayTags(PointList pointList, ReaderWay way, double d // special handling for countries: since they are built-in with GraphHopper they are always fed to the EncodingManager Country country = Country.MISSING; + CustomArea prevCustomArea = null; for (CustomArea customArea : customAreas) { - Object countryCode = customArea.getProperties().get("ISO3166-1:alpha3"); - if (countryCode == null) + if (customArea.getProperties() == null) continue; + Object alpha3 = customArea.getProperties().get(Country.ISO_ALPHA3); + if (alpha3 == null) continue; - if (country != Country.MISSING) - LOGGER.warn("Multiple countries found for way {}: {}, {}", way.getId(), country, countryCode); - country = Country.valueOf(countryCode.toString()); + + // multiple countries are available -> pick the smaller one, see #2663 + if (prevCustomArea != null && prevCustomArea.getArea() < customArea.getArea()) + break; + + prevCustomArea = customArea; + country = Country.valueOf((String) alpha3); } way.setTag("country", country); @@ -273,27 +293,33 @@ protected void setArtificialWayTags(PointList pointList, ReaderWay way, double d * @param toIndex a unique integer id for the last node of this segment * @param pointList coordinates of this segment * @param way the OSM way this segment was taken from - * @param nodeTags node tags of this segment if it is an artificial edge, empty otherwise + * @param nodeTags node tags of this segment. there is one map of tags for each point. */ - protected void addEdge(int fromIndex, int toIndex, PointList pointList, ReaderWay way, Map nodeTags) { + protected void addEdge(int fromIndex, int toIndex, PointList pointList, ReaderWay way, List> nodeTags) { // sanity checks if (fromIndex < 0 || toIndex < 0) throw new AssertionError("to or from index is invalid for this edge " + fromIndex + "->" + toIndex + ", points:" + pointList); if (pointList.getDimension() != nodeAccess.getDimension()) throw new AssertionError("Dimension does not match for pointList vs. nodeAccess " + pointList.getDimension() + " <-> " + nodeAccess.getDimension()); + if (pointList.size() != nodeTags.size()) + throw new AssertionError("there should be as many maps of node tags as there are points. node tags: " + nodeTags.size() + ", points: " + pointList.size()); // todo: in principle it should be possible to delay elevation calculation so we do not need to store // elevations during import (saves memory in pillar info during import). also note that we already need to // to do some kind of elevation processing (bridge+tunnel interpolation in GraphHopper class, maybe this can // go together - // Smooth the elevation before calculating the distance because the distance will be incorrect if calculated afterwards - if (config.isSmoothElevation()) - GraphElevationSmoothing.smoothElevation(pointList); + if (pointList.is3D()) { + // sample points along long edges + if (config.getLongEdgeSamplingDistance() < Double.MAX_VALUE) + pointList = EdgeSampling.sample(pointList, config.getLongEdgeSamplingDistance(), distCalc, eleProvider); - // sample points along long edges - if (config.getLongEdgeSamplingDistance() < Double.MAX_VALUE && pointList.is3D()) - pointList = EdgeSampling.sample(pointList, config.getLongEdgeSamplingDistance(), distCalc, eleProvider); + // smooth the elevation before calculating the distance because the distance will be incorrect if calculated afterwards + if (config.getElevationSmoothing().equals("ramer")) + EdgeElevationSmoothing.smoothRamer(pointList, config.getElevationSmoothingRamerMax()); + else if (config.getElevationSmoothing().equals("moving_average")) + EdgeElevationSmoothing.smoothMovingAverage(pointList); + } if (config.getMaxWayPointDistance() > 0 && pointList.size() > 2) simplifyAlgo.simplify(pointList); @@ -323,12 +349,11 @@ protected void addEdge(int fromIndex, int toIndex, PointList pointList, ReaderWa setArtificialWayTags(pointList, way, distance, nodeTags); IntsRef relationFlags = getRelFlagsMap(way.getId()); - IntsRef edgeFlags = tagParserManager.handleWayTags(way, relationFlags); - if (edgeFlags.isEmpty()) - return; - - String name = way.getTag("way_name", ""); - EdgeIteratorState edge = baseGraph.edge(fromIndex, toIndex).setDistance(distance).setFlags(edgeFlags).setName(name); + EdgeIteratorState edge = baseGraph.edge(fromIndex, toIndex).setDistance(distance); + osmParsers.handleWayTags(edge.getEdge(), edgeIntAccess, way, relationFlags); + List list = way.getTag("key_values", Collections.emptyList()); + if (!list.isEmpty()) + edge.setKeyValues(list); // If the entire way is just the first and last point, do not waste space storing an empty way geometry if (pointList.size() > 2) { @@ -338,12 +363,9 @@ protected void addEdge(int fromIndex, int toIndex, PointList pointList, ReaderWa checkCoordinates(toIndex, pointList.get(pointList.size() - 1)); edge.setWayGeometry(pointList.shallowCopy(1, pointList.size() - 1, false)); } - tagParserManager.applyWayTags(way, edge); checkDistance(edge); - if (osmWayIdSet.contains(way.getId())) { - getEdgeIdToOsmWayIdMap().put(edge.getEdge(), way.getId()); - } + restrictedWaysToEdgesMap.putIfReserved(way.getId(), edge.getEdge()); } private void checkCoordinates(int nodeIndex, GHPoint point) { @@ -373,25 +395,40 @@ else if (Math.abs(edgeDistance - geometryDistance) > tolerance) */ protected void preprocessWay(ReaderWay way, WaySegmentParser.CoordinateSupplier coordinateSupplier) { // storing the road name does not yet depend on the flagEncoder so manage it directly + List list = new ArrayList<>(); if (config.isParseWayNames()) { - // String wayInfo = carFlagEncoder.getWayInfo(way); // http://wiki.openstreetmap.org/wiki/Key:name String name = ""; if (!config.getPreferredLanguage().isEmpty()) name = fixWayName(way.getTag("name:" + config.getPreferredLanguage())); if (name.isEmpty()) name = fixWayName(way.getTag("name")); + if (!name.isEmpty()) + list.add(new KVStorage.KeyValue(STREET_NAME, name)); + // http://wiki.openstreetmap.org/wiki/Key:ref String refName = fixWayName(way.getTag("ref")); - if (!refName.isEmpty()) { - if (name.isEmpty()) - name = refName; - else - name += ", " + refName; - } + if (!refName.isEmpty()) + list.add(new KVStorage.KeyValue(STREET_REF, refName)); - way.setTag("way_name", name); + if (way.hasTag("destination:ref")) { + list.add(new KVStorage.KeyValue(STREET_DESTINATION_REF, fixWayName(way.getTag("destination:ref")))); + } else { + if (way.hasTag("destination:ref:forward")) + list.add(new KVStorage.KeyValue(STREET_DESTINATION_REF, fixWayName(way.getTag("destination:ref:forward")), true, false)); + if (way.hasTag("destination:ref:backward")) + list.add(new KVStorage.KeyValue(STREET_DESTINATION_REF, fixWayName(way.getTag("destination:ref:backward")), false, true)); + } + if (way.hasTag("destination")) { + list.add(new KVStorage.KeyValue(STREET_DESTINATION, fixWayName(way.getTag("destination")))); + } else { + if (way.hasTag("destination:forward")) + list.add(new KVStorage.KeyValue(STREET_DESTINATION, fixWayName(way.getTag("destination:forward")), true, false)); + if (way.hasTag("destination:backward")) + list.add(new KVStorage.KeyValue(STREET_DESTINATION, fixWayName(way.getTag("destination:backward")), false, true)); + } } + way.setTag("key_values", list); if (!isCalculateWayDistance(way)) return; @@ -442,7 +479,8 @@ protected void preprocessWay(ReaderWay way, WaySegmentParser.CoordinateSupplier static String fixWayName(String str) { if (str == null) return ""; - return WAY_NAME_PATTERN.matcher(str).replaceAll(", "); + // the KVStorage does not accept too long strings -> Helper.cutStringForKV + return KVStorage.cutString(WAY_NAME_PATTERN.matcher(str).replaceAll(", ")); } /** @@ -477,132 +515,105 @@ protected void preprocessRelations(ReaderRelation relation) { if (!relation.isMetaRelation() && relation.hasTag("type", "route")) { // we keep track of all route relations, so they are available when we create edges later for (ReaderRelation.Member member : relation.getMembers()) { - if (member.getType() != ReaderRelation.Member.WAY) + if (member.getType() != ReaderElement.Type.WAY) continue; IntsRef oldRelationFlags = getRelFlagsMap(member.getRef()); - IntsRef newRelationFlags = tagParserManager.handleRelationTags(relation, oldRelationFlags); + IntsRef newRelationFlags = osmParsers.handleRelationTags(relation, oldRelationFlags); putRelFlagsMap(member.getRef(), newRelationFlags); } } - if (relation.hasTag("type", "restriction")) { - // we keep the osm way ids that occur in turn relations, because this way we know for which GH edges - // we need to remember the associated osm way id. this is just an optimization that is supposed to save - // memory compared to simply storing the osm way ids in a long array where the array index is the GH edge - // id. - List turnRelations = createTurnRelations(relation); - for (OSMTurnRelation turnRelation : turnRelations) { - osmWayIdSet.add(turnRelation.getOsmIdFrom()); - osmWayIdSet.add(turnRelation.getOsmIdTo()); - } - } + Arrays.stream(RestrictionConverter.getRestrictedWayIds(relation)) + .forEach(restrictedWaysToEdgesMap::reserve); } /** * This method is called for each relation during the second pass of {@link WaySegmentParser} - * We use it to set turn restrictions. + * We use it to save the relations and process them afterwards. */ protected void processRelation(ReaderRelation relation, LongToIntFunction getIdForOSMNodeId) { - if (turnCostStorage != null && relation.hasTag("type", "restriction")) { - TurnCostParser.ExternalInternalMap map = new TurnCostParser.ExternalInternalMap() { - @Override - public int getInternalNodeIdOfOsmNode(long nodeOsmId) { - return getIdForOSMNodeId.applyAsInt(nodeOsmId); - } - - @Override - public long getOsmIdOfInternalEdge(int edgeId) { - return getEdgeIdToOsmWayIdMap().get(edgeId); - } - }; - for (OSMTurnRelation turnRelation : createTurnRelations(relation)) { - int viaNode = map.getInternalNodeIdOfOsmNode(turnRelation.getViaOsmNodeId()); - // street with restriction was not included (access or tag limits etc) - if (viaNode >= 0) - tagParserManager.handleTurnRelationTags(turnRelation, map, baseGraph); + if (turnCostStorage != null) + if (RestrictionConverter.isTurnRestriction(relation)) { + long osmViaNode = RestrictionConverter.getViaNodeIfViaNodeRestriction(relation); + if (osmViaNode >= 0) { + int viaNode = getIdForOSMNodeId.applyAsInt(osmViaNode); + // only include the restriction if the corresponding node wasn't excluded + if (viaNode >= 0) { + relation.setTag("graphhopper:via_node", viaNode); + restrictionRelations.add(relation); + } + } else + // not a via-node restriction -> simply add it as is + restrictionRelations.add(relation); } - } } - private IntLongMap getEdgeIdToOsmWayIdMap() { - // todo: is this lazy initialization really advantageous? - if (edgeIdToOsmWayIdMap == null) - edgeIdToOsmWayIdMap = new GHIntLongHashMap(osmWayIdSet.size(), 0.5f); - - return edgeIdToOsmWayIdMap; - } - - /** - * Creates turn relations out of an unspecified OSM relation - */ - static List createTurnRelations(ReaderRelation relation) { - List osmTurnRelations = new ArrayList<>(); - String vehicleTypeRestricted = ""; - List vehicleTypesExcept = new ArrayList<>(); - if (relation.hasTag("except")) { - String tagExcept = relation.getTag("except"); - if (!Helper.isEmpty(tagExcept)) { - List vehicleTypes = new ArrayList<>(Arrays.asList(tagExcept.split(";"))); - for (String vehicleType : vehicleTypes) - vehicleTypesExcept.add(vehicleType.trim()); - } - } - if (relation.hasTag("restriction")) { - OSMTurnRelation osmTurnRelation = createTurnRelation(relation, relation.getTag("restriction"), vehicleTypeRestricted, vehicleTypesExcept); - if (osmTurnRelation != null) { - osmTurnRelations.add(osmTurnRelation); + private void addRestrictionsToGraph() { + // The OSM restriction format is explained here: https://wiki.openstreetmap.org/wiki/Relation:restriction + List> restrictions = new ArrayList<>(restrictionRelations.size()); + for (ReaderRelation restrictionRelation : restrictionRelations) { + try { + // convert the OSM relation topology to the graph representation. this only needs to be done once for all + // vehicle types (we also want to print warnings only once) + restrictions.add(RestrictionConverter.convert(restrictionRelation, baseGraph, restrictedWaysToEdgesMap::getEdges)); + } catch (OSMRestrictionException e) { + warnOfRestriction(restrictionRelation, e); } - return osmTurnRelations; } - if (relation.hasTagWithKeyPrefix("restriction:")) { - List vehicleTypesRestricted = relation.getKeysWithPrefix("restriction:"); - for (String vehicleType : vehicleTypesRestricted) { - String restrictionType = relation.getTag(vehicleType); - vehicleTypeRestricted = vehicleType.replace("restriction:", "").trim(); - OSMTurnRelation osmTurnRelation = createTurnRelation(relation, restrictionType, vehicleTypeRestricted, vehicleTypesExcept); - if (osmTurnRelation != null) { - osmTurnRelations.add(osmTurnRelation); + // The restriction type depends on the vehicle, or at least not all restrictions affect every vehicle type. + // We handle the restrictions for one vehicle after another. + for (RestrictionTagParser restrictionTagParser : osmParsers.getRestrictionTagParsers()) { + LongSet viaWaysUsedByOnlyRestrictions = new LongHashSet(); + List> restrictionsWithType = new ArrayList<>(restrictions.size()); + for (Triple r : restrictions) { + if (r.second == null) + // this relation was found to be invalid by another restriction tag parser already + continue; + try { + RestrictionTagParser.Result res = restrictionTagParser.parseRestrictionTags(r.first.getTags()); + if (res == null) + // this relation is ignored by the current restriction tag parser + continue; + RestrictionConverter.checkIfCompatibleWithRestriction(r.second, res.getRestriction()); + // we ignore 'only' via-way restrictions that share the same via way, because these would require adding + // multiple artificial edges, see here: https://github.com/graphhopper/graphhopper/pull/2689#issuecomment-1306769694 + if (r.second.isViaWayRestriction() && res.getRestrictionType() == RestrictionType.ONLY) + for (LongCursor viaWay : r.third.getViaWays()) + if (!viaWaysUsedByOnlyRestrictions.add(viaWay.value)) + throw new OSMRestrictionException("has a member with role 'via' that is also used as 'via' member by another 'only' restriction. GraphHopper cannot handle this."); + restrictionsWithType.add(new Pair<>(r.second, res.getRestrictionType())); + } catch (OSMRestrictionException e) { + warnOfRestriction(r.first, e); + // we only want to print a warning once for each restriction relation, so we make sure this + // restriction is ignored for the other vehicles + r.second = null; } } + restrictionSetter.setRestrictions(restrictionsWithType, restrictionTagParser.getTurnCostEnc()); } - return osmTurnRelations; } - static OSMTurnRelation createTurnRelation(ReaderRelation relation, String restrictionType, String - vehicleTypeRestricted, List vehicleTypesExcept) { - OSMTurnRelation.Type type = OSMTurnRelation.Type.getRestrictionType(restrictionType); - if (type != OSMTurnRelation.Type.UNSUPPORTED) { - long fromWayID = -1; - long viaNodeID = -1; - long toWayID = -1; + public IntIntMap getArtificialEdgesByEdges() { + return restrictionSetter.getArtificialEdgesByEdges(); + } - for (ReaderRelation.Member member : relation.getMembers()) { - if (ReaderElement.WAY == member.getType()) { - if ("from".equals(member.getRole())) { - fromWayID = member.getRef(); - } else if ("to".equals(member.getRole())) { - toWayID = member.getRef(); - } - } else if (ReaderElement.NODE == member.getType() && "via".equals(member.getRole())) { - viaNodeID = member.getRef(); - } - } - if (fromWayID >= 0 && toWayID >= 0 && viaNodeID >= 0) { - OSMTurnRelation osmTurnRelation = new OSMTurnRelation(fromWayID, viaNodeID, toWayID, type); - osmTurnRelation.setVehicleTypeRestricted(vehicleTypeRestricted); - osmTurnRelation.setVehicleTypesExcept(vehicleTypesExcept); - return osmTurnRelation; - } + private static void warnOfRestriction(ReaderRelation restrictionRelation, OSMRestrictionException e) { + // we do not log exceptions with an empty message + if (!e.isWithoutWarning()) { + restrictionRelation.getTags().remove("graphhopper:via_node"); + List members = restrictionRelation.getMembers().stream().map(m -> m.getRole() + " " + m.getType().toString().toLowerCase() + " " + m.getRef()).collect(Collectors.toList()); + LOGGER.warn("Restriction relation " + restrictionRelation.getId() + " " + e.getMessage() + ". tags: " + restrictionRelation.getTags() + ", members: " + members + ". Relation ignored."); } - return null; } - private void finishedReading() { - tagParserManager.releaseParsers(); + private void releaseEverythingExceptRestrictionData() { eleProvider.release(); osmWayIdToRelationFlagsMap = null; - osmWayIdSet = null; - edgeIdToOsmWayIdMap = null; + } + + private void releaseRestrictionData() { + restrictedWaysToEdgesMap = null; + restrictionRelations = null; } IntsRef getRelFlagsMap(long osmId) { diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMRestrictionException.java b/core/src/main/java/com/graphhopper/reader/osm/OSMRestrictionException.java new file mode 100644 index 00000000000..dc0940e0115 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/OSMRestrictionException.java @@ -0,0 +1,34 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +public class OSMRestrictionException extends Exception { + + public static OSMRestrictionException withoutWarning() { + return new OSMRestrictionException(""); + } + + public OSMRestrictionException(String message) { + super(message); + } + + public boolean isWithoutWarning() { + return getMessage().isEmpty(); + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/OSMXMLHelper.java b/core/src/main/java/com/graphhopper/reader/osm/OSMXMLHelper.java index f2bd6d6837c..b27c03f3f41 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/OSMXMLHelper.java +++ b/core/src/main/java/com/graphhopper/reader/osm/OSMXMLHelper.java @@ -26,13 +26,11 @@ import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; -import java.util.List; /** * @author Peter Karich */ public class OSMXMLHelper { - private static final String TYPE_DECODE = "nwr"; public static ReaderNode createNode(long id, XMLStreamReader parser) throws XMLStreamException { ReaderNode node = new ReaderNode(id, @@ -105,7 +103,12 @@ private static void readMembers(ReaderRelation rel, XMLStreamReader parser) thro public static Member createMember(XMLStreamReader parser) { String typeName = parser.getAttributeValue(null, "type"); - int type = TYPE_DECODE.indexOf(typeName.charAt(0)); + ReaderElement.Type type = ReaderElement.Type.NODE; + if (typeName.startsWith("w")) { + type = ReaderElement.Type.WAY; + } else if (typeName.startsWith("r")) { + type = ReaderElement.Type.RELATION; + } long ref = Long.parseLong(parser.getAttributeValue(null, "ref")); String role = parser.getAttributeValue(null, "role"); return new ReaderRelation.Member(type, ref, role); diff --git a/core/src/main/java/com/graphhopper/reader/osm/GraphHopperOSM.java b/core/src/main/java/com/graphhopper/reader/osm/Pair.java similarity index 76% rename from core/src/main/java/com/graphhopper/reader/osm/GraphHopperOSM.java rename to core/src/main/java/com/graphhopper/reader/osm/Pair.java index 56bfe5c93e8..6ad2aeebac5 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/GraphHopperOSM.java +++ b/core/src/main/java/com/graphhopper/reader/osm/Pair.java @@ -15,15 +15,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.graphhopper.reader.osm; -import com.graphhopper.GraphHopper; -import com.graphhopper.util.JsonFeatureCollection; +public class Pair { + public F first; + public S second; -/** - * This class only exists for backward compatibility. - * @deprecated Use {@link GraphHopper} instead. - */ -@Deprecated -public class GraphHopperOSM extends GraphHopper { + public Pair(F first, S second) { + this.first = first; + this.second = second; + } } diff --git a/core/src/main/java/com/graphhopper/reader/PillarInfo.java b/core/src/main/java/com/graphhopper/reader/osm/PillarInfo.java similarity index 98% rename from core/src/main/java/com/graphhopper/reader/PillarInfo.java rename to core/src/main/java/com/graphhopper/reader/osm/PillarInfo.java index ada0681b381..4439d90638f 100644 --- a/core/src/main/java/com/graphhopper/reader/PillarInfo.java +++ b/core/src/main/java/com/graphhopper/reader/osm/PillarInfo.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.graphhopper.reader; +package com.graphhopper.reader.osm; import com.graphhopper.storage.DataAccess; import com.graphhopper.storage.Directory; diff --git a/core/src/main/java/com/graphhopper/reader/osm/RestrictionConverter.java b/core/src/main/java/com/graphhopper/reader/osm/RestrictionConverter.java new file mode 100644 index 00000000000..67f2451e732 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/RestrictionConverter.java @@ -0,0 +1,182 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.carrotsearch.hppc.LongArrayList; +import com.carrotsearch.hppc.cursors.IntCursor; +import com.carrotsearch.hppc.cursors.LongCursor; +import com.graphhopper.reader.ReaderElement; +import com.graphhopper.reader.ReaderRelation; +import com.graphhopper.storage.BaseGraph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.Map; +import java.util.function.LongFunction; + +public class RestrictionConverter { + private static final Logger LOGGER = LoggerFactory.getLogger(RestrictionConverter.class); + private static final long[] EMPTY_LONG_ARRAY_LIST = new long[0]; + + public static boolean isTurnRestriction(ReaderRelation relation) { + return "restriction".equals(relation.getTag("type")); + } + + public static long[] getRestrictedWayIds(ReaderRelation relation) { + if (!isTurnRestriction(relation)) + return EMPTY_LONG_ARRAY_LIST; + return relation.getMembers().stream() + .filter(m -> m.getType() == ReaderElement.Type.WAY) + .filter(m -> "from".equals(m.getRole()) || "via".equals(m.getRole()) || "to".equals(m.getRole())) + .mapToLong(ReaderRelation.Member::getRef) + .toArray(); + } + + public static long getViaNodeIfViaNodeRestriction(ReaderRelation relation) { + return relation.getMembers().stream() + .filter(m -> m.getType().equals(ReaderElement.Type.NODE)) + .filter(m -> "via".equals(m.getRole())) + .mapToLong(ReaderRelation.Member::getRef) + .findFirst() + .orElse(-1); + } + + /** + * OSM restriction relations specify turn restrictions between OSM ways (of course). This method converts such a + * relation into a 'graph' representation, where the turn restrictions are specified in terms of edge/node IDs instead + * of OSM IDs. + * + * @throws OSMRestrictionException if the given relation is either not valid in some way and/or cannot be handled and + * shall be ignored + */ + public static Triple convert(ReaderRelation relation, BaseGraph baseGraph, LongFunction> edgesByWay) throws OSMRestrictionException { + if (!isTurnRestriction(relation)) + throw new IllegalArgumentException("expected a turn restriction: " + relation.getTags()); + RestrictionMembers restrictionMembers = extractMembers(relation); + if (!membersExist(restrictionMembers, edgesByWay, relation)) + throw OSMRestrictionException.withoutWarning(); + // every OSM way might be split into *multiple* edges, so now we need to figure out which edges are the ones + // that are actually part of the given relation + WayToEdgeConverter wayToEdgeConverter = new WayToEdgeConverter(baseGraph, edgesByWay); + if (restrictionMembers.isViaWay()) { + WayToEdgeConverter.EdgeResult res = wayToEdgeConverter + .convertForViaWays(restrictionMembers.getFromWays(), restrictionMembers.getViaWays(), restrictionMembers.getToWays()); + return new Triple<>(relation, GraphRestriction.way(res.getFromEdges(), res.getViaEdges(), res.getToEdges(), res.getNodes()), restrictionMembers); + } else { + int viaNode = relation.getTag("graphhopper:via_node", -1); + if (viaNode < 0) + throw new IllegalStateException("For some reason we did not set graphhopper:via_node for this relation: " + relation.getId()); + WayToEdgeConverter.NodeResult res = wayToEdgeConverter + .convertForViaNode(restrictionMembers.getFromWays(), viaNode, restrictionMembers.getToWays()); + return new Triple<>(relation, GraphRestriction.node(res.getFromEdges(), viaNode, res.getToEdges()), restrictionMembers); + } + } + + private static boolean membersExist(RestrictionMembers members, LongFunction> edgesByWay, ReaderRelation relation) { + for (LongCursor c : members.getAllWays()) + if (!edgesByWay.apply(c.value).hasNext()) { + // this happens for example at the map borders or when certain ways like footways are excluded + LOGGER.debug("Restriction relation " + relation.getId() + " uses excluded way " + c.value + ". Relation ignored."); + return false; + } + return true; + } + + public static void checkIfCompatibleWithRestriction(GraphRestriction g, String restriction) throws OSMRestrictionException { + if (g.getFromEdges().size() > 1 && !"no_entry".equals(restriction)) + throw new OSMRestrictionException("has multiple members with role 'from' even though it is not a 'no_entry' restriction"); + if (g.getToEdges().size() > 1 && !"no_exit".equals(restriction)) + throw new OSMRestrictionException("has multiple members with role 'to' even though it is not a 'no_exit' restriction"); + } + + public static RestrictionMembers extractMembers(ReaderRelation relation) throws OSMRestrictionException { + // we use -1 to indicate 'missing', which is fine because we exclude negative OSM IDs (see #2652) + long viaOSMNode = -1; + LongArrayList fromWays = new LongArrayList(); + LongArrayList viaWays = new LongArrayList(); + LongArrayList toWays = new LongArrayList(); + for (ReaderRelation.Member member : relation.getMembers()) { + if ("from".equals(member.getRole())) { + if (member.getType() != ReaderElement.Type.WAY) + throw new OSMRestrictionException("has a member with role 'from' and type '" + member.getType() + "', but it should be of type 'way'"); + fromWays.add(member.getRef()); + } else if ("to".equals(member.getRole())) { + if (member.getType() != ReaderElement.Type.WAY) + throw new OSMRestrictionException("has a member with role 'to' and type '" + member.getType() + "', but it should be of type 'way'"); + toWays.add(member.getRef()); + } else if ("via".equals(member.getRole())) { + if (member.getType() == ReaderElement.Type.NODE) { + if (viaOSMNode >= 0) + throw new OSMRestrictionException("has multiple members with role 'via' and type 'node', but multiple via-members are only allowed when they are of type: 'way'"); + // note that we check for combined usage of via nodes and ways later on + viaOSMNode = member.getRef(); + } else if (member.getType() == ReaderElement.Type.WAY) { + // note that we check for combined usage of via nodes and ways later on + viaWays.add(member.getRef()); + } else + throw new OSMRestrictionException("has a member with role 'via' and" + + " type '" + member.getType() + "', but it should be of type 'node' or 'way'"); + } else if ("location_hint".equals(member.getRole())) { + // location_hint is deprecated and should no longer be used according to the wiki, but we do not warn + // about it, or even ignore the relation in this case, because maybe not everyone is happy to remove it. + } else if (member.getRole().trim().isEmpty()) + throw new OSMRestrictionException("has a member with an empty role"); + else + throw new OSMRestrictionException("has a member with an unknown role '" + member.getRole() + "'"); + } + if (fromWays.isEmpty() && toWays.isEmpty()) + throw new OSMRestrictionException("has no member with role 'from' and 'to'"); + else if (fromWays.isEmpty()) + throw new OSMRestrictionException("has no member with role 'from'"); + else if (toWays.isEmpty()) + throw new OSMRestrictionException("has no member with role 'to'"); + + if (fromWays.size() > 1 && toWays.size() > 1) + throw new OSMRestrictionException("has multiple members with role 'from' and 'to'"); + checkTags(fromWays, toWays, relation.getTags()); + if (viaOSMNode >= 0 && !viaWays.isEmpty()) + throw new OSMRestrictionException("has members with role 'via' of type 'node' and 'way', but only one type is allowed"); + else if (viaOSMNode >= 0) + return RestrictionMembers.viaNode(viaOSMNode, fromWays, toWays); + else if (!viaWays.isEmpty()) + return RestrictionMembers.viaWay(fromWays, viaWays, toWays); + else + throw new OSMRestrictionException("has no member with role 'via'"); + } + + private static void checkTags(LongArrayList fromWays, LongArrayList toWays, Map tags) throws OSMRestrictionException { + // the exact restriction value depends on the vehicle type, but we can already print a warning for certain + // cases here, so later we do not print such warnings for every single vehicle. + boolean hasNoEntry = false; + boolean hasNoExit = false; + for (Map.Entry e : tags.entrySet()) { + if (e.getKey().startsWith("restriction")) { + if (e.getValue() != null && ((String) e.getValue()).startsWith("no_entry")) + hasNoEntry = true; + if (e.getValue() != null && ((String) e.getValue()).startsWith("no_exit")) + hasNoExit = true; + } + } + if (fromWays.size() > 1 && !hasNoEntry) + throw new OSMRestrictionException("has multiple members with role 'from' even though it is not a 'no_entry' restriction"); + if (toWays.size() > 1 && !hasNoExit) + throw new OSMRestrictionException("has multiple members with role 'to' even though it is not a 'no_exit' restriction"); + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/RestrictionMembers.java b/core/src/main/java/com/graphhopper/reader/osm/RestrictionMembers.java new file mode 100644 index 00000000000..d44f9bee053 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/RestrictionMembers.java @@ -0,0 +1,73 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.carrotsearch.hppc.LongArrayList; + +public class RestrictionMembers { + private final boolean isViaWay; + private final long viaOSMNode; + private final LongArrayList fromWays; + private final LongArrayList viaWays; + private final LongArrayList toWays; + + public static RestrictionMembers viaNode(long viaOSMNode, LongArrayList fromWays, LongArrayList toWays) { + return new RestrictionMembers(false, viaOSMNode, fromWays, null, toWays); + } + + public static RestrictionMembers viaWay(LongArrayList fromWays, LongArrayList viaWays, LongArrayList toWays) { + return new RestrictionMembers(true, -1, fromWays, viaWays, toWays); + } + + private RestrictionMembers(boolean isViaWay, long viaOSMNode, LongArrayList fromWays, LongArrayList viaWays, LongArrayList toWays) { + this.isViaWay = isViaWay; + this.viaOSMNode = viaOSMNode; + this.fromWays = fromWays; + this.viaWays = viaWays; + this.toWays = toWays; + } + + public boolean isViaWay() { + return isViaWay; + } + + public long getViaOSMNode() { + return viaOSMNode; + } + + public LongArrayList getFromWays() { + return fromWays; + } + + public LongArrayList getViaWays() { + return viaWays; + } + + public LongArrayList getToWays() { + return toWays; + } + + public LongArrayList getAllWays() { + LongArrayList result = new LongArrayList(fromWays.size() + toWays.size() + (isViaWay ? viaWays.size() : 0)); + result.addAll(fromWays); + if (isViaWay) result.addAll(viaWays); + result.addAll(toWays); + return result; + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java new file mode 100644 index 00000000000..e6ba31c0281 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/RestrictionTagParser.java @@ -0,0 +1,134 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.graphhopper.routing.ev.DecimalEncodedValue; + +import java.util.*; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyList; + +/** + * Parses the OSM restriction tags for given vehicle types / transportation modes. + */ +public class RestrictionTagParser { + private final List vehicleTypes; + private final DecimalEncodedValue turnCostEnc; + + public RestrictionTagParser(List vehicleTypes, DecimalEncodedValue turnCostEnc) { + this.vehicleTypes = vehicleTypes; + this.turnCostEnc = turnCostEnc; + } + + public DecimalEncodedValue getTurnCostEnc() { + return turnCostEnc; + } + + public Result parseRestrictionTags(Map tags) throws OSMRestrictionException { + String restriction = (String) tags.get("restriction"); + List limitedRestrictions = tags.entrySet().stream() + .filter(t -> t.getKey().startsWith("restriction:")) + // restriction:bicycle=give_way seems quite common in France, but since it isn't a 'strict' turn + // restriction we ignore it here. + .filter(e -> !"give_way".equals(e.getValue())) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + boolean hasGiveWay = tags.values().stream().anyMatch("give_way"::equals); + List exceptVehicles = tags.containsKey("except") + // todo: there are also some occurrences of except=resident(s), destination or delivery + ? Arrays.stream(((String) tags.get("except")).split(";")).map(String::trim).collect(Collectors.toList()) + : emptyList(); + if (restriction != null) { + // the 'restriction' tag limits the turns for all vehicleTypes, unless this is modified by the 'except' tag + if (!limitedRestrictions.isEmpty()) + // note that there is no warning if there is a restriction tag and restriction:*=give_way + throw new OSMRestrictionException("has a 'restriction' tag, but also 'restriction:' tags"); + if (!Collections.disjoint(vehicleTypes, exceptVehicles)) + return null; + return buildResult(restriction); + } else { + // if there is no 'restriction' tag there still might be 'restriction:xyz' tags that only affect certain vehicleTypes + if (limitedRestrictions.isEmpty()) + if (!hasGiveWay) + throw new OSMRestrictionException("neither has a 'restriction' nor 'restriction:' tags"); + else + // ignore, but no warning if there is only restriction:*=give_way + throw OSMRestrictionException.withoutWarning(); + if (!exceptVehicles.isEmpty() && limitedRestrictions.stream().noneMatch(r -> r.startsWith("restriction:conditional"))) + throw new OSMRestrictionException("has an 'except', but no 'restriction' or 'restriction:conditional' tag"); + Set restrictions = limitedRestrictions.stream() + // We do not consider the restriction[:]:conditional tag so far + .filter(r -> !r.contains("conditional")) + .filter(r -> vehicleTypes.contains(r.replace("restriction:", "").trim())) + .map(r -> (String) tags.get(r)) + .collect(Collectors.toSet()); + if (restrictions.size() > 1) + throw new OSMRestrictionException("contains multiple different restriction values: '" + restrictions + "'"); + else if (restrictions.isEmpty()) + return null; + else + return buildResult(restrictions.iterator().next()); + } + } + + private static Result buildResult(String restriction) throws OSMRestrictionException { + return new Result(parseRestrictionValue(restriction), restriction); + } + + private static RestrictionType parseRestrictionValue(String restriction) throws OSMRestrictionException { + switch (restriction) { + case "no_left_turn": + case "no_right_turn": + case "no_straight_on": + case "no_u_turn": + case "no_entry": + case "no_exit": + return RestrictionType.NO; + case "only_left_turn": + case "only_right_turn": + case "only_straight_on": + case "only_u_turn": + return RestrictionType.ONLY; + case "no_right_turn_on_red": + case "no_left_turn_on_red": + throw OSMRestrictionException.withoutWarning(); + default: + throw new OSMRestrictionException("uses unknown restriction value: '" + restriction + "'"); + } + } + + public static class Result { + private final RestrictionType restrictionType; + private final String restriction; + + public Result(RestrictionType restrictionType, String restriction) { + this.restrictionType = restrictionType; + this.restriction = restriction; + } + + public RestrictionType getRestrictionType() { + return restrictionType; + } + + public String getRestriction() { + return restriction; + } + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/RestrictionType.java b/core/src/main/java/com/graphhopper/reader/osm/RestrictionType.java new file mode 100644 index 00000000000..9ed83aac8e5 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/RestrictionType.java @@ -0,0 +1,23 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +public enum RestrictionType { + NO, ONLY +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/SegmentNode.java b/core/src/main/java/com/graphhopper/reader/osm/SegmentNode.java index a80bff0901c..cbd2b8b6288 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/SegmentNode.java +++ b/core/src/main/java/com/graphhopper/reader/osm/SegmentNode.java @@ -18,12 +18,16 @@ package com.graphhopper.reader.osm; +import java.util.Map; + class SegmentNode { long osmNodeId; int id; + Map tags; - public SegmentNode(long osmNodeId, int id) { + public SegmentNode(long osmNodeId, int id, Map tags) { this.osmNodeId = osmNodeId; this.id = id; + this.tags = tags; } } diff --git a/core/src/main/java/com/graphhopper/reader/osm/SkipOptions.java b/core/src/main/java/com/graphhopper/reader/osm/SkipOptions.java new file mode 100644 index 00000000000..a4b25a72ba8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/SkipOptions.java @@ -0,0 +1,47 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +public class SkipOptions { + private final boolean skipNodes; + private final boolean skipWays; + private final boolean skipRelations; + + public static SkipOptions none() { + return new SkipOptions(false, false, false); + } + + public SkipOptions(boolean skipNodes, boolean skipWays, boolean skipRelations) { + this.skipNodes = skipNodes; + this.skipWays = skipWays; + this.skipRelations = skipRelations; + } + + public boolean isSkipNodes() { + return skipNodes; + } + + public boolean isSkipWays() { + return skipWays; + } + + public boolean isSkipRelations() { + return skipRelations; + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/Triple.java b/core/src/main/java/com/graphhopper/reader/osm/Triple.java new file mode 100644 index 00000000000..5a88e22dd32 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/Triple.java @@ -0,0 +1,31 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +public class Triple { + public F first; + public S second; + public T third; + + public Triple(F first, S second, T third) { + this.first = first; + this.second = second; + this.third = third; + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/WaySegmentParser.java b/core/src/main/java/com/graphhopper/reader/osm/WaySegmentParser.java index 93daa67d328..52593f6401d 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/WaySegmentParser.java +++ b/core/src/main/java/com/graphhopper/reader/osm/WaySegmentParser.java @@ -38,17 +38,13 @@ import java.io.File; import java.io.IOException; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Consumer; import java.util.function.LongToIntFunction; import java.util.function.Predicate; import static com.graphhopper.reader.osm.OSMNodeData.*; import static com.graphhopper.util.Helper.nf; -import static java.util.Collections.emptyMap; /** * This class parses a given OSM file and splits OSM ways into 'segments' at all intersections (or 'junctions'). @@ -68,6 +64,7 @@ */ public class WaySegmentParser { private static final Logger LOGGER = LoggerFactory.getLogger(WaySegmentParser.class); + private static final Set INCLUDE_IF_NODE_TAGS = new HashSet<>(Arrays.asList("barrier", "highway", "railway", "crossing", "ford")); private final ElevationProvider eleProvider; private final Predicate wayFilter; @@ -107,7 +104,7 @@ public void readOSM(File osmFile) { LOGGER.info("Start reading OSM file: '" + osmFile + "'"); LOGGER.info("pass1 - start"); StopWatch sw1 = StopWatch.started(); - readOSM(osmFile, new Pass1Handler()); + readOSM(osmFile, new Pass1Handler(), new SkipOptions(true, false, false)); LOGGER.info("pass1 - finished, took: {}", sw1.stop().getTimeString()); long nodes = nodeData.getNodeCount(); @@ -116,7 +113,7 @@ public void readOSM(File osmFile) { LOGGER.info("pass2 - start"); StopWatch sw2 = new StopWatch().start(); - readOSM(osmFile, new Pass2Handler()); + readOSM(osmFile, new Pass2Handler(), SkipOptions.none()); LOGGER.info("pass2 - finished, took: {}", sw2.stop().getTimeString()); nodeData.release(); @@ -137,9 +134,9 @@ public Date getTimeStamp() { private class Pass1Handler implements ReaderElementHandler { private boolean handledWays; private boolean handledRelations; - private long wayCounter = 1; + private long wayCounter = 0; private long acceptedWays = 0; - private long relationsCounter = -1; + private long relationsCounter = 0; @Override public void handleWay(ReaderWay way) { @@ -198,10 +195,10 @@ private class Pass2Handler implements ReaderElementHandler { private boolean handledNodes; private boolean handledWays; private boolean handledRelations; - private long nodeCounter = -1; + private long nodeCounter = 0; private long acceptedNodes = 0; private long ignoredSplitNodes = 0; - private long wayCounter = -1; + private long wayCounter = 0; @Override public void handleNode(ReaderNode node) { @@ -218,20 +215,32 @@ public void handleNode(ReaderNode node) { LOGGER.info("pass2 - processed nodes: " + nf(nodeCounter) + ", accepted nodes: " + nf(acceptedNodes) + ", " + Helper.getMemInfo()); - int nodeType = nodeData.addCoordinatesIfMapped(node.getId(), node.getLat(), node.getLon(), eleProvider.getEle(node)); + int nodeType = nodeData.addCoordinatesIfMapped(node.getId(), node.getLat(), node.getLon(), () -> eleProvider.getEle(node)); if (nodeType == EMPTY_NODE) return; acceptedNodes++; - // we keep node tags for barrier nodes + // remember which nodes we want to split if (splitNodeFilter.test(node)) { if (nodeType == JUNCTION_NODE) { LOGGER.debug("OSM node {} at {},{} is a barrier node at a junction. The barrier will be ignored", node.getId(), Helper.round(node.getLat(), 7), Helper.round(node.getLon(), 7)); ignoredSplitNodes++; } else + nodeData.setSplitNode(node.getId()); + } + + // store node tags if at least one important tag is included and make this available for the edge handler + for (Map.Entry e : node.getTags().entrySet()) { + if (INCLUDE_IF_NODE_TAGS.contains(e.getKey())) { + node.removeTag("created_by"); + node.removeTag("source"); + node.removeTag("note"); + node.removeTag("fixme"); nodeData.setTags(node); + break; + } } } @@ -251,7 +260,7 @@ public void handleWay(ReaderWay way) { return; List segment = new ArrayList<>(way.getNodes().size()); for (LongCursor node : way.getNodes()) - segment.add(new SegmentNode(node.value, nodeData.getId(node.value))); + segment.add(new SegmentNode(node.value, nodeData.getId(node.value), nodeData.getTags(node.value))); wayPreprocessor.preprocessWay(way, osmNodeId -> nodeData.getCoordinates(nodeData.getId(osmNodeId))); splitWayAtJunctionsAndEmptySections(segment, way); } @@ -304,9 +313,11 @@ private void splitSegmentAtSplitNodes(List parentSegment, ReaderWay List segment = new ArrayList<>(); for (int i = 0; i < parentSegment.size(); i++) { SegmentNode node = parentSegment.get(i); - Map nodeTags = nodeData.getTags(node.osmNodeId); - // so far we only consider node tags of split nodes, so if there are node tags we split the node - if (!nodeTags.isEmpty()) { + if (nodeData.isSplitNode(node.osmNodeId)) { + // do not split this node again. for example a barrier can be connecting two ways (appear in both + // ways) and we only want to add a barrier edge once (but we want to add one). + nodeData.unsetSplitNode(node.osmNodeId); + // this node is a barrier. we will copy it and add an extra edge SegmentNode barrierFrom = node; SegmentNode barrierTo = nodeData.addCopyOfNode(node); @@ -318,28 +329,30 @@ private void splitSegmentAtSplitNodes(List parentSegment, ReaderWay } if (!segment.isEmpty()) { segment.add(barrierFrom); - handleSegment(segment, way, emptyMap()); + handleSegment(segment, way); segment = new ArrayList<>(); } + + // mark barrier edge + way.setTag("gh:barrier_edge", true); segment.add(barrierFrom); segment.add(barrierTo); - handleSegment(segment, way, nodeTags); + handleSegment(segment, way); + way.removeTag("gh:barrier_edge"); + segment = new ArrayList<>(); segment.add(barrierTo); - - // ignore this barrier node from now. for example a barrier can be connecting two ways (appear in both - // ways) and we only want to add a barrier edge once (but we want to add one). - nodeData.removeTags(node.osmNodeId); } else { segment.add(node); } } if (segment.size() > 1) - handleSegment(segment, way, emptyMap()); + handleSegment(segment, way); } - void handleSegment(List segment, ReaderWay way, Map nodeTags) { + void handleSegment(List segment, ReaderWay way) { final PointList pointList = new PointList(segment.size(), nodeData.is3D()); + final List> nodeTags = new ArrayList<>(segment.size()); int from = -1; int to = -1; for (int i = 0; i < segment.size(); i++) { @@ -359,6 +372,7 @@ else if (i == segment.size() - 1) else if (isTowerNode(id)) throw new IllegalStateException("Tower nodes should only appear at the end of segments, way: " + way.getId()); nodeData.addCoordinatesToPointList(id, pointList); + nodeTags.add(node.tags); } if (from < 0 || to < 0) throw new IllegalStateException("The first and last nodes of a segment must be tower nodes, way: " + way.getId()); @@ -377,8 +391,8 @@ public void handleRelation(ReaderRelation relation) { @Override public void onFinish() { - LOGGER.info("pass2 - finished, processed ways: {}, way nodes: {}, with tags: {}, ignored barriers at junctions: {}", - nf(wayCounter), nf(acceptedNodes), nf(nodeData.getTaggedNodeCount()), nf(ignoredSplitNodes)); + LOGGER.info("pass2 - finished, processed ways: {}, way nodes: {}, nodes with tags: {}, node tag capacity: {}, ignored barriers at junctions: {}", + nf(wayCounter), nf(acceptedNodes), nf(nodeData.getTaggedNodeCount()), nf(nodeData.getNodeTagCapacity()), nf(ignoredSplitNodes)); } public int getInternalNodeIdOfOSMNode(long nodeOsmId) { @@ -389,8 +403,8 @@ public int getInternalNodeIdOfOSMNode(long nodeOsmId) { } } - private void readOSM(File file, ReaderElementHandler handler) { - try (OSMInput osmInput = openOsmInputFile(file)) { + private void readOSM(File file, ReaderElementHandler handler, SkipOptions skipOptions) { + try (OSMInput osmInput = openOsmInputFile(file, skipOptions)) { ReaderElement elem; while ((elem = osmInput.getNext()) != null) handler.handleElement(elem); @@ -402,8 +416,8 @@ private void readOSM(File file, ReaderElementHandler handler) { } } - protected OSMInput openOsmInputFile(File osmFile) throws XMLStreamException, IOException { - return new OSMInputFile(osmFile).setWorkerThreads(workerThreads).open(); + protected OSMInput openOsmInputFile(File osmFile, SkipOptions skipOptions) throws XMLStreamException, IOException { + return new OSMInputFile(osmFile).setWorkerThreads(workerThreads).setSkipOptions(skipOptions).open(); } public static class Builder { @@ -513,16 +527,16 @@ public WaySegmentParser build() { private interface ReaderElementHandler { default void handleElement(ReaderElement elem) throws ParseException { switch (elem.getType()) { - case ReaderElement.NODE: + case NODE: handleNode((ReaderNode) elem); break; - case ReaderElement.WAY: + case WAY: handleWay((ReaderWay) elem); break; - case ReaderElement.RELATION: + case RELATION: handleRelation((ReaderRelation) elem); break; - case ReaderElement.FILEHEADER: + case FILEHEADER: handleFileHeader((OSMFileHeader) elem); break; default: @@ -547,7 +561,7 @@ default void onFinish() { } public interface EdgeHandler { - void handleEdge(int from, int to, PointList pointList, ReaderWay way, Map nodeTags); + void handleEdge(int from, int to, PointList pointList, ReaderWay way, List> nodeTags); } public interface RelationProcessor { diff --git a/core/src/main/java/com/graphhopper/reader/osm/WayToEdgeConverter.java b/core/src/main/java/com/graphhopper/reader/osm/WayToEdgeConverter.java new file mode 100644 index 00000000000..e12d1adb9ac --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/WayToEdgeConverter.java @@ -0,0 +1,229 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.carrotsearch.hppc.IntArrayList; +import com.carrotsearch.hppc.LongArrayList; +import com.carrotsearch.hppc.cursors.IntCursor; +import com.carrotsearch.hppc.cursors.LongCursor; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.EdgeIteratorState; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.LongFunction; + +public class WayToEdgeConverter { + private final BaseGraph baseGraph; + private final LongFunction> edgesByWay; + + public WayToEdgeConverter(BaseGraph baseGraph, LongFunction> edgesByWay) { + this.baseGraph = baseGraph; + this.edgesByWay = edgesByWay; + } + + /** + * Finds the edge IDs associated with the given OSM ways that are adjacent to the given via-node. + * For each way there can be multiple edge IDs and there should be exactly one that is adjacent to the via-node + * for each way. Otherwise we throw {@link OSMRestrictionException} + */ + public NodeResult convertForViaNode(LongArrayList fromWays, int viaNode, LongArrayList toWays) throws OSMRestrictionException { + if (fromWays.isEmpty() || toWays.isEmpty()) + throw new IllegalArgumentException("There must be at least one from- and to-way"); + if (fromWays.size() > 1 && toWays.size() > 1) + throw new IllegalArgumentException("There can only be multiple from- or to-ways, but not both"); + NodeResult result = new NodeResult(fromWays.size(), toWays.size()); + for (LongCursor fromWay : fromWays) + edgesByWay.apply(fromWay.value).forEachRemaining(e -> { + if (baseGraph.isAdjacentToNode(e.value, viaNode)) + result.fromEdges.add(e.value); + }); + if (result.fromEdges.size() < fromWays.size()) + throw new OSMRestrictionException("has from member ways that aren't adjacent to the via member node"); + else if (result.fromEdges.size() > fromWays.size()) + throw new OSMRestrictionException("has from member ways that aren't split at the via member node"); + + for (LongCursor toWay : toWays) + edgesByWay.apply(toWay.value).forEachRemaining(e -> { + if (baseGraph.isAdjacentToNode(e.value, viaNode)) + result.toEdges.add(e.value); + }); + if (result.toEdges.size() < toWays.size()) + throw new OSMRestrictionException("has to member ways that aren't adjacent to the via member node"); + else if (result.toEdges.size() > toWays.size()) + throw new OSMRestrictionException("has to member ways that aren't split at the via member node"); + return result; + } + + public static class NodeResult { + private final IntArrayList fromEdges; + private final IntArrayList toEdges; + + public NodeResult(int numFrom, int numTo) { + fromEdges = new IntArrayList(numFrom); + toEdges = new IntArrayList(numTo); + } + + public IntArrayList getFromEdges() { + return fromEdges; + } + + public IntArrayList getToEdges() { + return toEdges; + } + } + + /** + * Finds the edge IDs associated with the given OSM ways that are adjacent to each other. For example for given + * from-, via- and to-ways there can be multiple edges associated with each (because each way can be split into + * multiple edges). We then need to find the from-edge that is connected with one of the via-edges which in turn + * must be connected with one of the to-edges. We use DFS/backtracking to do this. + * There can also be *multiple* via-ways, but the concept is the same. + * Note that there can also be multiple from- or to-*ways*, but only one of each of them should be considered at a + * time. In contrast to the via-ways there are only multiple from/to-ways, because of restrictions like no_entry or + * no_exit where there can be multiple from- or to-members. So we need to find one edge-chain for each pair of from- + * and to-ways. + * Besides the edge IDs we also return the node IDs that connect the edges, so we can add turn restrictions at these + * nodes later. + */ + public EdgeResult convertForViaWays(LongArrayList fromWays, LongArrayList viaWays, LongArrayList toWays) throws OSMRestrictionException { + if (fromWays.isEmpty() || toWays.isEmpty() || viaWays.isEmpty()) + throw new IllegalArgumentException("There must be at least one from-, via- and to-way"); + if (fromWays.size() > 1 && toWays.size() > 1) + throw new IllegalArgumentException("There can only be multiple from- or to-ways, but not both"); + List solutions = new ArrayList<>(); + for (LongCursor fromWay : fromWays) + for (LongCursor toWay : toWays) + findEdgeChain(fromWay.value, viaWays, toWay.value, solutions); + if (solutions.size() < fromWays.size() * toWays.size()) + throw new OSMRestrictionException("has from/to member ways that aren't connected with the via member way(s)"); + else if (solutions.size() > fromWays.size() * toWays.size()) + throw new OSMRestrictionException("has from/to member ways that aren't split at the via member way(s)"); + return buildResult(solutions, new EdgeResult(fromWays.size(), viaWays.size(), toWays.size())); + } + + private static EdgeResult buildResult(List edgeChains, EdgeResult result) { + for (IntArrayList edgeChain : edgeChains) { + result.fromEdges.add(edgeChain.get(0)); + if (result.nodes.isEmpty()) { + // the via-edges and nodes are the same for edge chain + for (int i = 1; i < edgeChain.size() - 3; i += 2) { + result.nodes.add(edgeChain.get(i)); + result.viaEdges.add(edgeChain.get(i + 1)); + } + result.nodes.add(edgeChain.get(edgeChain.size() - 2)); + } + result.toEdges.add(edgeChain.get(edgeChain.size() - 1)); + } + return result; + } + + private void findEdgeChain(long fromWay, LongArrayList viaWays, long toWay, List solutions) throws OSMRestrictionException { + // For each edge chain there must be one edge associated with the from-way, one for each via-way and one + // associated with the to-way. We use DFS with backtracking to find all edge chains that connect an edge + // associated with the from-way with one associated with the to-way. + IntArrayList viaEdgesForViaWays = new IntArrayList(viaWays.size()); + for (LongCursor c : viaWays) { + Iterator iterator = edgesByWay.apply(c.value); + viaEdgesForViaWays.add(iterator.next().value); + if (iterator.hasNext()) + throw new OSMRestrictionException("has via member way that isn't split at adjacent ways: " + c.value); + } + IntArrayList toEdges = listFromIterator(edgesByWay.apply(toWay)); + + // the search starts at *every* from edge + edgesByWay.apply(fromWay).forEachRemaining(from -> { + EdgeIteratorState edge = baseGraph.getEdgeIteratorState(from.value, Integer.MIN_VALUE); + explore(viaEdgesForViaWays, toEdges, edge.getBaseNode(), 0, IntArrayList.from(edge.getEdge(), edge.getBaseNode()), solutions); + explore(viaEdgesForViaWays, toEdges, edge.getAdjNode(), 0, IntArrayList.from(edge.getEdge(), edge.getAdjNode()), solutions); + }); + } + + private void explore(IntArrayList viaEdgesForViaWays, IntArrayList toEdges, int node, int viaCount, IntArrayList curr, List solutions) { + if (viaCount == viaEdgesForViaWays.size()) { + for (IntCursor to : toEdges) { + if (baseGraph.isAdjacentToNode(to.value, node)) { + IntArrayList solution = new IntArrayList(curr); + solution.add(to.value); + solutions.add(solution); + } + } + return; + } + for (int i = 0; i < viaEdgesForViaWays.size(); i++) { + int viaEdge = viaEdgesForViaWays.get(i); + if (viaEdge < 0) continue; + if (baseGraph.isAdjacentToNode(viaEdge, node)) { + int otherNode = baseGraph.getOtherNode(viaEdge, node); + curr.add(viaEdge, otherNode); + // every via edge must only be used once, but instead of removing it we just set it to -1 + viaEdgesForViaWays.set(i, -1); + explore(viaEdgesForViaWays, toEdges, otherNode, viaCount + 1, curr, solutions); + // backtrack + curr.elementsCount -= 2; + viaEdgesForViaWays.set(i, viaEdge); + } + } + } + + private static IntArrayList listFromIterator(Iterator iterator) { + IntArrayList result = new IntArrayList(); + iterator.forEachRemaining(c -> result.add(c.value)); + return result; + } + + public static class EdgeResult { + private final IntArrayList fromEdges; + private final IntArrayList viaEdges; + private final IntArrayList toEdges; + private final IntArrayList nodes; + + public EdgeResult(int numFrom, int numVia, int numTo) { + fromEdges = new IntArrayList(numFrom); + viaEdges = new IntArrayList(numVia); + toEdges = new IntArrayList(numTo); + nodes = new IntArrayList(numVia + 1); + } + + public IntArrayList getFromEdges() { + return fromEdges; + } + + public IntArrayList getViaEdges() { + return viaEdges; + } + + public IntArrayList getToEdges() { + return toEdges; + } + + /** + * All the intermediate nodes, i.e. for an edge chain like this: + *

+         *   a   b   c   d
+         * 0---1---2---3---4
+         * 
+ * where 'a' is the from-edge and 'd' is the to-edge this will be [1,2,3] + */ + public IntArrayList getNodes() { + return nodes; + } + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/WayToEdgesMap.java b/core/src/main/java/com/graphhopper/reader/osm/WayToEdgesMap.java new file mode 100644 index 00000000000..fffff814c60 --- /dev/null +++ b/core/src/main/java/com/graphhopper/reader/osm/WayToEdgesMap.java @@ -0,0 +1,94 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.carrotsearch.hppc.IntArrayList; +import com.carrotsearch.hppc.LongIntMap; +import com.carrotsearch.hppc.LongIntScatterMap; +import com.carrotsearch.hppc.cursors.IntCursor; + +import java.util.Iterator; + +import static java.util.Collections.emptyIterator; + +/** + * This map can store multiple edges (int) for each way ID (long). All way-edge pairs with the same way must be inserted + * consecutively. This allows us to simply store all edges in an array along with a mapping between the ways and the + * position of the associated edges in this array. + */ +public class WayToEdgesMap { + private static final int RESERVED = -1; + private final LongIntMap offsetIndexByWay = new LongIntScatterMap(); + private final IntArrayList offsets = new IntArrayList(); + private final IntArrayList edges = new IntArrayList(); + private long lastWay = -1; + + /** + * We need to reserve a way before we can put the associated edges into the map. + * This way we can define a set of keys/ways for which we shall add edges later. + */ + public void reserve(long way) { + offsetIndexByWay.put(way, RESERVED); + } + + public void putIfReserved(long way, int edge) { + if (edge < 0) + throw new IllegalArgumentException("edge must be >= 0, but was: " + edge); + if (way != lastWay) { + int idx = offsetIndexByWay.indexOf(way); + if (idx < 0) + // not reserved yet + return; + if (offsetIndexByWay.indexGet(idx) != RESERVED) + // already taken + throw new IllegalArgumentException("You need to add all edges for way: " + way + " consecutively"); + offsetIndexByWay.indexReplace(idx, offsets.size()); + offsets.add(this.edges.size()); + lastWay = way; + } + this.edges.add(edge); + } + + public Iterator getEdges(long way) { + int idx = offsetIndexByWay.indexOf(way); + if (idx < 0) + return emptyIterator(); + int offsetIndex = offsetIndexByWay.indexGet(idx); + if (offsetIndex == RESERVED) + // we reserved this, but did not put a value later + return emptyIterator(); + int offsetBegin = offsets.get(offsetIndex); + int offsetEnd = offsetIndex + 1 < offsets.size() ? offsets.get(offsetIndex + 1) : edges.size(); + IntCursor cursor = new IntCursor(); + cursor.index = -1; + return new Iterator() { + @Override + public boolean hasNext() { + return offsetBegin + cursor.index + 1 < offsetEnd; + } + + @Override + public IntCursor next() { + cursor.index++; + cursor.value = edges.get(offsetBegin + cursor.index); + return cursor; + } + }; + } +} diff --git a/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspector.java b/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspector.java index 83300999246..eec6ac9ed42 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspector.java +++ b/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspector.java @@ -17,7 +17,6 @@ */ package com.graphhopper.reader.osm.conditional; -import com.graphhopper.reader.ConditionalTagInspector; import com.graphhopper.reader.ReaderWay; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/com/graphhopper/reader/ConditionalTagInspector.java b/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalTagInspector.java similarity index 91% rename from core/src/main/java/com/graphhopper/reader/ConditionalTagInspector.java rename to core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalTagInspector.java index fee51086749..7b5e54eaeb8 100644 --- a/core/src/main/java/com/graphhopper/reader/ConditionalTagInspector.java +++ b/core/src/main/java/com/graphhopper/reader/osm/conditional/ConditionalTagInspector.java @@ -15,7 +15,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.graphhopper.reader; +package com.graphhopper.reader.osm.conditional; + +import com.graphhopper.reader.ReaderWay; /** * @author Peter Karich diff --git a/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfBlobDecoder.java b/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfBlobDecoder.java index 498af8d267d..13b41b5b2c1 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfBlobDecoder.java +++ b/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfBlobDecoder.java @@ -8,6 +8,7 @@ import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; import com.graphhopper.reader.osm.OSMFileHeader; +import com.graphhopper.reader.osm.SkipOptions; import com.graphhopper.util.Helper; import org.openstreetmap.osmosis.osmbinary.Fileformat; import org.openstreetmap.osmosis.osmbinary.Osmformat; @@ -33,6 +34,7 @@ public class PbfBlobDecoder implements Runnable { private final byte[] rawBlob; private final PbfBlobDecoderListener listener; private List decodedEntities; + private final SkipOptions skipOptions; /** * Creates a new instance. @@ -42,10 +44,11 @@ public class PbfBlobDecoder implements Runnable { * @param rawBlob The raw data of the blob. * @param listener The listener for receiving decoding results. */ - public PbfBlobDecoder(String blobType, byte[] rawBlob, PbfBlobDecoderListener listener) { + public PbfBlobDecoder(String blobType, byte[] rawBlob, PbfBlobDecoderListener listener, SkipOptions skipOptions) { this.blobType = blobType; this.rawBlob = rawBlob; this.listener = listener; + this.skipOptions = skipOptions; } private byte[] readBlobContent() throws IOException { @@ -116,7 +119,7 @@ private void processOsmHeader(byte[] data) throws InvalidProtocolBufferException */ } - private Map buildTags(List keys, List values, PbfFieldDecoder fieldDecoder) { + private Map buildTags(List keys, List values, PbfFieldDecoder fieldDecoder) { // Ensure parallel lists are of equal size. if (checkData) { @@ -129,7 +132,7 @@ private Map buildTags(List keys, List values, Iterator keyIterator = keys.iterator(); Iterator valueIterator = values.iterator(); if (keyIterator.hasNext()) { - Map tags = new HashMap<>(keys.size()); + Map tags = new HashMap<>(keys.size()); while (keyIterator.hasNext()) { String key = fieldDecoder.decodeString(keyIterator.next()); String value = fieldDecoder.decodeString(valueIterator.next()); @@ -142,7 +145,7 @@ private Map buildTags(List keys, List values, private void processNodes(List nodes, PbfFieldDecoder fieldDecoder) { for (Osmformat.Node node : nodes) { - Map tags = buildTags(node.getKeysList(), node.getValsList(), fieldDecoder); + Map tags = buildTags(node.getKeysList(), node.getValsList(), fieldDecoder); ReaderNode osmNode = new ReaderNode(node.getId(), fieldDecoder.decodeLatitude(node .getLat()), fieldDecoder.decodeLatitude(node.getLon())); @@ -215,7 +218,7 @@ private void processNodes(Osmformat.DenseNodes nodes, PbfFieldDecoder fieldDecod // 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 tags = null; + Map tags = null; while (keysValuesIterator.hasNext()) { int keyIndex = keysValuesIterator.next(); if (keyIndex == 0) { @@ -247,7 +250,7 @@ private void processNodes(Osmformat.DenseNodes nodes, PbfFieldDecoder fieldDecod private void processWays(List ways, PbfFieldDecoder fieldDecoder) { for (Osmformat.Way way : ways) { - Map tags = buildTags(way.getKeysList(), way.getValsList(), fieldDecoder); + Map tags = buildTags(way.getKeysList(), way.getValsList(), fieldDecoder); ReaderWay osmWay = new ReaderWay(way.getId()); osmWay.setTags(tags); @@ -289,14 +292,14 @@ private void buildRelationMembers(ReaderRelation relation, Osmformat.Relation.MemberType memberType = memberTypeIterator.next(); refId += memberIdIterator.next(); - int entityType = ReaderRelation.Member.NODE; + ReaderElement.Type entityType = ReaderElement.Type.NODE; if (memberType == Osmformat.Relation.MemberType.WAY) { - entityType = ReaderRelation.Member.WAY; + entityType = ReaderElement.Type.WAY; } else if (memberType == Osmformat.Relation.MemberType.RELATION) { - entityType = ReaderRelation.Member.RELATION; + entityType = ReaderElement.Type.RELATION; } if (checkData) { - if (entityType == ReaderRelation.Member.NODE && memberType != Osmformat.Relation.MemberType.NODE) { + if (entityType == ReaderElement.Type.NODE && memberType != Osmformat.Relation.MemberType.NODE) { throw new RuntimeException("Member type of " + memberType + " is not supported."); } } @@ -308,7 +311,7 @@ private void buildRelationMembers(ReaderRelation relation, private void processRelations(List relations, PbfFieldDecoder fieldDecoder) { for (Osmformat.Relation relation : relations) { - Map tags = buildTags(relation.getKeysList(), relation.getValsList(), fieldDecoder); + Map tags = buildTags(relation.getKeysList(), relation.getValsList(), fieldDecoder); ReaderRelation osmRelation = new ReaderRelation(relation.getId()); osmRelation.setTags(tags); @@ -326,10 +329,14 @@ private void processOsmPrimitives(byte[] data) throws InvalidProtocolBufferExcep PbfFieldDecoder fieldDecoder = new PbfFieldDecoder(block); for (Osmformat.PrimitiveGroup primitiveGroup : block.getPrimitivegroupList()) { - processNodes(primitiveGroup.getDense(), fieldDecoder); - processNodes(primitiveGroup.getNodesList(), fieldDecoder); - processWays(primitiveGroup.getWaysList(), fieldDecoder); - processRelations(primitiveGroup.getRelationsList(), fieldDecoder); + if (!skipOptions.isSkipNodes()) { + processNodes(primitiveGroup.getDense(), fieldDecoder); + processNodes(primitiveGroup.getNodesList(), fieldDecoder); + } + if (!skipOptions.isSkipWays()) + processWays(primitiveGroup.getWaysList(), fieldDecoder); + if (!skipOptions.isSkipRelations()) + processRelations(primitiveGroup.getRelationsList(), fieldDecoder); } } diff --git a/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfDecoder.java b/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfDecoder.java index 89763ac5414..396c0c505ac 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfDecoder.java +++ b/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfDecoder.java @@ -2,6 +2,7 @@ package com.graphhopper.reader.osm.pbf; import com.graphhopper.reader.ReaderElement; +import com.graphhopper.reader.osm.SkipOptions; import java.util.LinkedList; import java.util.List; @@ -26,6 +27,7 @@ public class PbfDecoder { private final Lock lock; private final Condition dataWaitCondition; private final Queue blobResults; + private final SkipOptions skipOptions; /** * Creates a new instance. @@ -37,11 +39,12 @@ public class PbfDecoder { * @param sink The sink to send all decoded entities to. */ public PbfDecoder(PbfStreamSplitter streamSplitter, ExecutorService executorService, int maxPendingBlobs, - Sink sink) { + Sink sink, SkipOptions skipOptions) { this.streamSplitter = streamSplitter; this.executorService = executorService; this.maxPendingBlobs = maxPendingBlobs; this.sink = sink; + this.skipOptions = skipOptions; // Create the thread synchronisation primitives. lock = new ReentrantLock(); @@ -140,7 +143,7 @@ public void complete(List decodedEntities) { }; // Create the blob decoder itself and execute it on a worker thread. - PbfBlobDecoder blobDecoder = new PbfBlobDecoder(rawBlob.getType(), rawBlob.getData(), decoderListener); + PbfBlobDecoder blobDecoder = new PbfBlobDecoder(rawBlob.getType(), rawBlob.getData(), decoderListener, skipOptions); executorService.execute(blobDecoder); // If the number of pending blobs has reached capacity we must begin diff --git a/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfReader.java b/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfReader.java index 8e7a2017743..02313f5f2bc 100644 --- a/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfReader.java +++ b/core/src/main/java/com/graphhopper/reader/osm/pbf/PbfReader.java @@ -1,6 +1,8 @@ // This software is released into the Public Domain. See copying.txt for details. package com.graphhopper.reader.osm.pbf; +import com.graphhopper.reader.osm.SkipOptions; + import java.io.DataInputStream; import java.io.InputStream; import java.util.concurrent.ExecutorService; @@ -14,9 +16,10 @@ */ public class PbfReader implements Runnable { private Throwable throwable; - private InputStream inputStream; - private Sink sink; - private int workers; + private final InputStream inputStream; + private final Sink sink; + private final int workers; + private final SkipOptions skipOptions; /** * Creates a new instance. @@ -25,10 +28,11 @@ public class PbfReader implements Runnable { * @param in The file to read. * @param workers The number of worker threads for decoding PBF blocks. */ - public PbfReader(InputStream in, Sink sink, int workers) { + public PbfReader(InputStream in, Sink sink, int workers, SkipOptions skipOptions) { this.inputStream = in; this.sink = sink; this.workers = workers; + this.skipOptions = skipOptions; } @Override @@ -44,7 +48,7 @@ public void run() { // immediately ready for processing when a worker thread completes. // The main thread is responsible for splitting blobs from the // request stream, and sending decoded entities to the sink. - PbfDecoder pbfDecoder = new PbfDecoder(streamSplitter, executorService, workers + 1, sink); + PbfDecoder pbfDecoder = new PbfDecoder(streamSplitter, executorService, workers + 1, sink, skipOptions); pbfDecoder.run(); } catch (Throwable t) { diff --git a/core/src/main/java/com/graphhopper/routing/AStar.java b/core/src/main/java/com/graphhopper/routing/AStar.java index 1d2143bb44c..deb2e8f0117 100644 --- a/core/src/main/java/com/graphhopper/routing/AStar.java +++ b/core/src/main/java/com/graphhopper/routing/AStar.java @@ -27,6 +27,9 @@ import java.util.PriorityQueue; +import static com.graphhopper.util.EdgeIterator.ANY_EDGE; +import static com.graphhopper.util.EdgeIterator.NO_EDGE; + /** * This class implements the A* algorithm according to * http://en.wikipedia.org/wiki/A*_search_algorithm @@ -36,13 +39,15 @@ * * @author Peter Karich */ -public class AStar extends AbstractRoutingAlgorithm { +public class AStar extends AbstractRoutingAlgorithm implements EdgeToEdgeRoutingAlgorithm { private GHIntObjectHashMap fromMap; private PriorityQueue fromHeap; private AStarEntry currEdge; private int visitedNodes; private int to = -1; private WeightApproximator weightApprox; + private int fromOutEdge; + private int toInEdge; public AStar(Graph graph, Weighting weighting, TraversalMode tMode) { super(graph, weighting, tMode); @@ -68,21 +73,36 @@ protected void initCollections(int size) { @Override public Path calcPath(int from, int to) { + return calcPath(from, to, EdgeIterator.ANY_EDGE, EdgeIterator.ANY_EDGE); + } + + @Override + public Path calcPath(int from, int to, int fromOutEdge, int toInEdge) { + if ((fromOutEdge != ANY_EDGE || toInEdge != ANY_EDGE) && !traversalMode.isEdgeBased()) { + throw new IllegalArgumentException("Restricting the start/target edges is only possible for edge-based graph traversal"); + } + this.fromOutEdge = fromOutEdge; + this.toInEdge = toInEdge; checkAlreadyRun(); this.to = to; weightApprox.setTo(to); double weightToGoal = weightApprox.approximate(from); - currEdge = new AStarEntry(EdgeIterator.NO_EDGE, from, 0 + weightToGoal, 0); - if (!traversalMode.isEdgeBased()) { + AStarEntry startEntry = new AStarEntry(EdgeIterator.NO_EDGE, from, 0 + weightToGoal, 0); + fromHeap.add(startEntry); + if (fromOutEdge == NO_EDGE || toInEdge == NO_EDGE) + return extractPath(); + if (!traversalMode.isEdgeBased()) fromMap.put(from, currEdge); - } runAlgo(); return extractPath(); } private void runAlgo() { double currWeightToGoal, estimationFullWeight; - while (true) { + while (!fromHeap.isEmpty()) { + currEdge = fromHeap.poll(); + if (currEdge.isDeleted()) + continue; visitedNodes++; if (isMaxVisitedNodesExceeded() || finished()) break; @@ -90,7 +110,7 @@ private void runAlgo() { int currNode = currEdge.adjNode; EdgeIterator iter = edgeExplorer.setBaseNode(currNode); while (iter.next()) { - if (!accept(iter, currEdge.edge)) + if (!accept(iter, currEdge.edge) || (currEdge.edge == NO_EDGE && fromOutEdge != ANY_EDGE && iter.getEdge() != fromOutEdge)) continue; double tmpWeight = GHUtility.calcWeightWithTurnWeightWithAccess(weighting, iter, false, currEdge.edge) + currEdge.weightOfVisitedPath; @@ -111,40 +131,30 @@ private void runAlgo() { // assert (ase.weight > 0.9999999 * estimationFullWeight) : "Inconsistent distance estimate. It is expected weight >= estimationFullWeight but was " // + ase.weight + " < " + estimationFullWeight + " (" + ase.weight / estimationFullWeight + "), and weightOfVisitedPath:" // + ase.weightOfVisitedPath + " vs. alreadyVisitedWeight:" + alreadyVisitedWeight + " (" + ase.weightOfVisitedPath / alreadyVisitedWeight + ")"; - - fromHeap.remove(ase); - ase.edge = iter.getEdge(); - ase.weight = estimationFullWeight; - ase.weightOfVisitedPath = tmpWeight; - ase.parent = currEdge; + ase.setDeleted(); + ase = new AStarEntry(iter.getEdge(), neighborNode, estimationFullWeight, tmpWeight, currEdge); + fromMap.put(traversalId, ase); } - fromHeap.add(ase); - updateBestPath(iter, ase, traversalId); } } - - if (fromHeap.isEmpty()) - break; - - currEdge = fromHeap.poll(); - if (currEdge == null) - throw new AssertionError("Empty edge cannot happen"); } } - @Override - protected boolean finished() { - return currEdge.adjNode == to; + private boolean finished() { + return currEdge.adjNode == to && (toInEdge == ANY_EDGE || currEdge.edge == toInEdge) && (fromOutEdge == ANY_EDGE || currEdge.edge != NO_EDGE); } - @Override protected Path extractPath() { if (currEdge == null || !finished()) return createEmptyPath(); - return PathExtractor.extractPath(graph, weighting, currEdge); + return PathExtractor.extractPath(graph, weighting, currEdge) + // the path extractor uses currEdge.weight to set the weight, but this is the one that includes the + // A* approximation, not the weight of the visited path! this is still correct, because the approximation + // at the to-node (the end of the route) must be zero. Still it seems clearer to set the weight explicitly. + .setWeight(currEdge.getWeightOfVisitedPath()); } @Override diff --git a/core/src/main/java/com/graphhopper/routing/AStarBidirection.java b/core/src/main/java/com/graphhopper/routing/AStarBidirection.java index f8e5beed920..3b4c1527bfd 100644 --- a/core/src/main/java/com/graphhopper/routing/AStarBidirection.java +++ b/core/src/main/java/com/graphhopper/routing/AStarBidirection.java @@ -94,14 +94,6 @@ protected SPTEntry createEntry(EdgeIteratorState edge, double weight, SPTEntry p return new AStarEntry(edge.getEdge(), neighborNode, heapWeight, weight, parent); } - @Override - protected void updateEntry(SPTEntry entry, EdgeIteratorState edge, double weight, SPTEntry parent, boolean reverse) { - entry.edge = edge.getEdge(); - entry.weight = weight + weightApprox.approximate(edge.getAdjNode(), reverse); - ((AStarEntry) entry).weightOfVisitedPath = weight; - entry.parent = parent; - } - @Override protected double calcWeight(EdgeIteratorState iter, SPTEntry currEdge, boolean reverse) { // TODO performance: check if the node is already existent in the opposite direction diff --git a/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java b/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java index 989e36ec6e4..85ebd1c47fb 100644 --- a/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java +++ b/core/src/main/java/com/graphhopper/routing/AbstractBidirAlgo.java @@ -28,7 +28,7 @@ import static com.graphhopper.util.EdgeIterator.ANY_EDGE; -public abstract class AbstractBidirAlgo implements BidirRoutingAlgorithm { +public abstract class AbstractBidirAlgo implements EdgeToEdgeRoutingAlgorithm { protected final TraversalMode traversalMode; protected int from; protected int to; @@ -196,8 +196,6 @@ protected void updateBestPath(double edgeWeight, SPTEntry entry, int origEdgeIdF protected abstract double getInEdgeWeight(SPTEntry entry); - protected abstract int getOtherNode(int edge, int node); - protected int getIncomingEdge(SPTEntry entry) { return entry.edge; } diff --git a/core/src/main/java/com/graphhopper/routing/AbstractBidirCHAlgo.java b/core/src/main/java/com/graphhopper/routing/AbstractBidirCHAlgo.java index ee9ddfac58e..e9c9ad76eb4 100644 --- a/core/src/main/java/com/graphhopper/routing/AbstractBidirCHAlgo.java +++ b/core/src/main/java/com/graphhopper/routing/AbstractBidirCHAlgo.java @@ -21,6 +21,7 @@ import com.graphhopper.routing.ch.NodeBasedCHBidirPathExtractor; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.storage.*; +import com.graphhopper.util.GHUtility; import java.util.PriorityQueue; import java.util.function.Supplier; @@ -35,7 +36,7 @@ * @author easbar * @see AbstractNonCHBidirAlgo for non-CH bidirectional algorithms */ -public abstract class AbstractBidirCHAlgo extends AbstractBidirAlgo implements BidirRoutingAlgorithm { +public abstract class AbstractBidirCHAlgo extends AbstractBidirAlgo implements EdgeToEdgeRoutingAlgorithm { protected final RoutingCHGraph graph; protected final NodeAccess nodeAccess; protected RoutingCHEdgeExplorer inEdgeExplorer; @@ -84,7 +85,7 @@ protected void postInitFrom() { } else { // need to use a local reference here, because levelEdgeFilter is modified when calling fillEdgesFromUsingFilter final CHEdgeFilter tmpFilter = levelEdgeFilter; - fillEdgesFromUsingFilter(edgeState -> (tmpFilter == null || tmpFilter.accept(edgeState)) && edgeState.getOrigEdgeFirst() == fromOutEdge); + fillEdgesFromUsingFilter(edgeState -> (tmpFilter == null || tmpFilter.accept(edgeState)) && GHUtility.getEdgeFromEdgeKey(edgeState.getOrigEdgeKeyFirst()) == fromOutEdge); } } @@ -94,7 +95,7 @@ protected void postInitTo() { fillEdgesToUsingFilter(levelEdgeFilter); } else { final CHEdgeFilter tmpFilter = levelEdgeFilter; - fillEdgesToUsingFilter(edgeState -> (tmpFilter == null || tmpFilter.accept(edgeState)) && edgeState.getOrigEdgeLast() == toInEdge); + fillEdgesToUsingFilter(edgeState -> (tmpFilter == null || tmpFilter.accept(edgeState)) && GHUtility.getEdgeFromEdgeKey(edgeState.getOrigEdgeKeyLast()) == toInEdge); } } @@ -133,10 +134,13 @@ public boolean finished() { @Override boolean fillEdgesFrom() { - if (pqOpenSetFrom.isEmpty()) { - return false; + while (true) { + if (pqOpenSetFrom.isEmpty()) + return false; + currFrom = pqOpenSetFrom.poll(); + if (!currFrom.isDeleted()) + break; } - currFrom = pqOpenSetFrom.poll(); visitedCountFrom++; if (fromEntryCanBeSkipped()) { return true; @@ -151,10 +155,13 @@ boolean fillEdgesFrom() { @Override boolean fillEdgesTo() { - if (pqOpenSetTo.isEmpty()) { - return false; + while (true) { + if (pqOpenSetTo.isEmpty()) + return false; + currTo = pqOpenSetTo.poll(); + if (!currTo.isDeleted()) + break; } - currTo = pqOpenSetTo.poll(); visitedCountTo++; if (toEntryCanBeSkipped()) { return true; @@ -178,17 +185,28 @@ private void fillEdges(SPTEntry currEdge, PriorityQueue prioQueue, if (Double.isInfinite(weight)) { continue; } - final int origEdgeId = getOrigEdgeId(iter, reverse); - final int traversalId = getTraversalId(iter, origEdgeId, reverse); + final int origEdgeId = GHUtility.getEdgeFromEdgeKey(reverse ? iter.getOrigEdgeKeyFirst() : iter.getOrigEdgeKeyLast()); + final int traversalId = traversalMode.createTraversalId(iter, reverse); SPTEntry entry = bestWeightMap.get(traversalId); if (entry == null) { entry = createEntry(iter.getEdge(), iter.getAdjNode(), origEdgeId, weight, currEdge, reverse); bestWeightMap.put(traversalId, entry); prioQueue.add(entry); } else if (entry.getWeightOfVisitedPath() > weight) { - prioQueue.remove(entry); - updateEntry(entry, iter.getEdge(), iter.getAdjNode(), origEdgeId, weight, currEdge, reverse); + // flagging this entry, so it will be ignored when it is polled the next time + // this is faster than removing the entry from the queue and adding again, but for CH it does not really + // make a difference overall. + entry.setDeleted(); + boolean isBestEntry = reverse ? (entry == bestBwdEntry) : (entry == bestFwdEntry); + entry = createEntry(iter.getEdge(), iter.getAdjNode(), origEdgeId, weight, currEdge, reverse); + bestWeightMap.put(traversalId, entry); prioQueue.add(entry); + // if this is the best entry we need to update the best reference as well + if (isBestEntry) + if (reverse) + bestBwdEntry = entry; + else + bestFwdEntry = entry; } else continue; @@ -201,7 +219,7 @@ private void fillEdges(SPTEntry currEdge, PriorityQueue prioQueue, protected double calcWeight(RoutingCHEdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) { double edgeWeight = edgeState.getWeight(reverse); - final int origEdgeId = reverse ? edgeState.getOrigEdgeLast() : edgeState.getOrigEdgeFirst(); + final int origEdgeId = GHUtility.getEdgeFromEdgeKey(reverse ? edgeState.getOrigEdgeKeyLast() : edgeState.getOrigEdgeKeyFirst()); double turnCosts = reverse ? graph.getTurnWeight(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId) : graph.getTurnWeight(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId); @@ -223,19 +241,6 @@ protected boolean accept(RoutingCHEdgeIteratorState edge, SPTEntry currEdge, boo return levelEdgeFilter == null || levelEdgeFilter.accept(edge); } - protected int getOrigEdgeId(RoutingCHEdgeIteratorState edge, boolean reverse) { - return edge.getEdge(); - } - - protected int getTraversalId(RoutingCHEdgeIteratorState edge, int origEdgeId, boolean reverse) { - return traversalMode.createTraversalId(edge.getBaseNode(), edge.getAdjNode(), edge.getEdge(), reverse); - } - - @Override - protected int getOtherNode(int edge, int node) { - return graph.getBaseGraph().getOtherNode(edge, node); - } - protected double calcWeight(RoutingCHEdgeIteratorState iter, SPTEntry currEdge, boolean reverse) { return calcWeight(iter, reverse, getIncomingEdge(currEdge)) + currEdge.getWeightOfVisitedPath(); } diff --git a/core/src/main/java/com/graphhopper/routing/AbstractBidirectionEdgeCHNoSOD.java b/core/src/main/java/com/graphhopper/routing/AbstractBidirectionEdgeCHNoSOD.java index 445719c9cd1..5c01da51040 100644 --- a/core/src/main/java/com/graphhopper/routing/AbstractBidirectionEdgeCHNoSOD.java +++ b/core/src/main/java/com/graphhopper/routing/AbstractBidirectionEdgeCHNoSOD.java @@ -55,7 +55,7 @@ protected void postInitFrom() { if (fromOutEdge == ANY_EDGE) { fillEdgesFromUsingFilter(CHEdgeFilter.ALL_EDGES); } else { - fillEdgesFromUsingFilter(edgeState -> edgeState.getOrigEdgeFirst() == fromOutEdge); + fillEdgesFromUsingFilter(edgeState -> GHUtility.getEdgeFromEdgeKey(edgeState.getOrigEdgeKeyFirst()) == fromOutEdge); } } @@ -64,7 +64,7 @@ protected void postInitTo() { if (toInEdge == ANY_EDGE) { fillEdgesToUsingFilter(CHEdgeFilter.ALL_EDGES); } else { - fillEdgesToUsingFilter(edgeState -> edgeState.getOrigEdgeLast() == toInEdge); + fillEdgesToUsingFilter(edgeState -> GHUtility.getEdgeFromEdgeKey(edgeState.getOrigEdgeKeyLast()) == toInEdge); } } @@ -91,7 +91,7 @@ protected void updateBestPath(double edgeWeight, SPTEntry entry, int origEdgeId, EdgeIterator iter = innerExplorer.setBaseNode(entry.adjNode); while (iter.next()) { final int edgeId = iter.getEdge(); - int key = GHUtility.createEdgeKey(iter.getAdjNode(), iter.getBaseNode(), edgeId, !reverse); + int key = traversalMode.createTraversalId(iter, reverse); SPTEntry entryOther = bestWeightMapOther.get(key); if (entryOther == null) { continue; @@ -105,27 +105,17 @@ protected void updateBestPath(double edgeWeight, SPTEntry entry, int origEdgeId, if (newWeight < bestWeight) { bestFwdEntry = reverse ? entryOther : entry; bestBwdEntry = reverse ? entry : entryOther; + assert bestFwdEntry.adjNode == bestBwdEntry.adjNode; bestWeight = newWeight; } } } - @Override - protected int getOrigEdgeId(RoutingCHEdgeIteratorState edge, boolean reverse) { - return reverse ? edge.getOrigEdgeFirst() : edge.getOrigEdgeLast(); - } - @Override protected int getIncomingEdge(SPTEntry entry) { return ((CHEntry) entry).incEdge; } - @Override - protected int getTraversalId(RoutingCHEdgeIteratorState edge, int origEdgeId, boolean reverse) { - int baseNode = getOtherNode(origEdgeId, edge.getAdjNode()); - return GHUtility.createEdgeKey(baseNode, edge.getAdjNode(), origEdgeId, reverse); - } - @Override protected boolean accept(RoutingCHEdgeIteratorState edge, SPTEntry currEdge, boolean reverse) { return levelEdgeFilter == null || levelEdgeFilter.accept(edge); diff --git a/core/src/main/java/com/graphhopper/routing/AbstractNonCHBidirAlgo.java b/core/src/main/java/com/graphhopper/routing/AbstractNonCHBidirAlgo.java index 2e8d2036ebc..1975257d3e8 100644 --- a/core/src/main/java/com/graphhopper/routing/AbstractNonCHBidirAlgo.java +++ b/core/src/main/java/com/graphhopper/routing/AbstractNonCHBidirAlgo.java @@ -39,7 +39,7 @@ * @author easbar * @see AbstractBidirCHAlgo for bidirectional CH algorithms */ -public abstract class AbstractNonCHBidirAlgo extends AbstractBidirAlgo implements BidirRoutingAlgorithm { +public abstract class AbstractNonCHBidirAlgo extends AbstractBidirAlgo implements EdgeToEdgeRoutingAlgorithm { protected final Graph graph; protected final NodeAccess nodeAccess; protected final Weighting weighting; @@ -109,10 +109,13 @@ protected void fillEdgesToUsingFilter(EdgeFilter edgeFilter) { @Override boolean fillEdgesFrom() { - if (pqOpenSetFrom.isEmpty()) { - return false; + while (true) { + if (pqOpenSetFrom.isEmpty()) + return false; + currFrom = pqOpenSetFrom.poll(); + if (!currFrom.isDeleted()) + break; } - currFrom = pqOpenSetFrom.poll(); visitedCountFrom++; if (fromEntryCanBeSkipped()) { return true; @@ -127,10 +130,13 @@ boolean fillEdgesFrom() { @Override boolean fillEdgesTo() { - if (pqOpenSetTo.isEmpty()) { - return false; + while (true) { + if (pqOpenSetTo.isEmpty()) + return false; + currTo = pqOpenSetTo.poll(); + if (!currTo.isDeleted()) + break; } - currTo = pqOpenSetTo.poll(); visitedCountTo++; if (toEntryCanBeSkipped()) { return true; @@ -160,9 +166,18 @@ private void fillEdges(SPTEntry currEdge, PriorityQueue prioQueue, Int bestWeightMap.put(traversalId, entry); prioQueue.add(entry); } else if (entry.getWeightOfVisitedPath() > weight) { - prioQueue.remove(entry); - updateEntry(entry, iter, weight, currEdge, reverse); + // flagging this entry, so it will be ignored when it is polled the next time + entry.setDeleted(); + boolean isBestEntry = reverse ? (entry == bestBwdEntry) : (entry == bestFwdEntry); + entry = createEntry(iter, weight, currEdge, reverse); + bestWeightMap.put(traversalId, entry); prioQueue.add(entry); + // if this is the best entry we need to update the best reference as well + if (isBestEntry) + if (reverse) + bestBwdEntry = entry; + else + bestFwdEntry = entry; } else continue; @@ -176,12 +191,6 @@ private void fillEdges(SPTEntry currEdge, PriorityQueue prioQueue, Int } } - protected void updateEntry(SPTEntry entry, EdgeIteratorState edge, double weight, SPTEntry parent, boolean reverse) { - entry.edge = edge.getEdge(); - entry.weight = weight; - entry.parent = parent; - } - protected double calcWeight(EdgeIteratorState iter, SPTEntry currEdge, boolean reverse) { // note that for node-based routing the weights will be wrong in case the weighting is returning non-zero // turn weights, see discussion in #1960 @@ -193,11 +202,6 @@ protected double getInEdgeWeight(SPTEntry entry) { return weighting.calcEdgeWeight(graph.getEdgeIteratorState(entry.edge, entry.adjNode), false); } - @Override - protected int getOtherNode(int edge, int node) { - return graph.getOtherNode(edge, node); - } - @Override protected Path extractPath() { if (finished()) diff --git a/core/src/main/java/com/graphhopper/routing/AbstractRoutingAlgorithm.java b/core/src/main/java/com/graphhopper/routing/AbstractRoutingAlgorithm.java index 99c73c741cf..e969f8e8732 100644 --- a/core/src/main/java/com/graphhopper/routing/AbstractRoutingAlgorithm.java +++ b/core/src/main/java/com/graphhopper/routing/AbstractRoutingAlgorithm.java @@ -72,24 +72,6 @@ protected void checkAlreadyRun() { alreadyRun = true; } - /** - * To be overwritten from extending class. Should we make this available in RoutingAlgorithm - * interface? - *

- * - * @return true if finished. - */ - protected abstract boolean finished(); - - /** - * To be overwritten from extending class. Should we make this available in RoutingAlgorithm - * interface? - *

- * - * @return true if finished. - */ - protected abstract Path extractPath(); - @Override public List calcPaths(int from, int to) { return Collections.singletonList(calcPath(from, to)); diff --git a/core/src/main/java/com/graphhopper/routing/AlternativeRoute.java b/core/src/main/java/com/graphhopper/routing/AlternativeRoute.java index 4bb1a73dc92..6170b53ef90 100644 --- a/core/src/main/java/com/graphhopper/routing/AlternativeRoute.java +++ b/core/src/main/java/com/graphhopper/routing/AlternativeRoute.java @@ -22,13 +22,9 @@ import com.graphhopper.coll.GHIntHashSet; import com.graphhopper.coll.GHIntObjectHashMap; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.WeightApproximator; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; -import com.graphhopper.util.EdgeIterator; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.Parameters; +import com.graphhopper.util.*; import java.util.ArrayList; import java.util.Collections; @@ -36,6 +32,9 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static com.graphhopper.util.Parameters.Algorithms.AltRoute.*; /** * This class implements the alternative paths search using the "plateau" and partially the @@ -54,43 +53,52 @@ * http://algo2.iti.kit.edu/download/altgraph_tapas_extended.pdf * * + *

+ * Note: This algorithm can be slow for longer routes and alternatives are only really practical in combination with CH, see #2566 * * @author Peter Karich */ -public class AlternativeRoute implements RoutingAlgorithm { - private static final Comparator ALT_COMPARATOR = new Comparator() { - @Override - public int compare(AlternativeInfo o1, AlternativeInfo o2) { - return Double.compare(o1.sortBy, o2.sortBy); - } - }; - private final Graph graph; - private final Weighting weighting; - private final TraversalMode traversalMode; - private int visitedNodes; - private int maxVisitedNodes = Integer.MAX_VALUE; - private double maxWeightFactor = 1.4; - // the higher the maxWeightFactor the higher the explorationFactor needs to be - // 1 is default for bidir Dijkstra, 0.8 seems to be a very similar value for bidir A* but roughly 1/2 of the nodes explored - private double maxExplorationFactor = 0.8; - private double maxShareFactor = 0.6; - private double minPlateauFactor = 0.2; - private int maxPaths = 2; - private WeightApproximator weightApproximator; - - public AlternativeRoute(Graph graph, Weighting weighting, TraversalMode traversalMode) { - if (weighting.hasTurnCosts() && !traversalMode.isEdgeBased()) - throw new IllegalStateException("Weightings supporting turn costs cannot be used with node-based traversal mode"); - this.graph = graph; - this.weighting = weighting; - this.traversalMode = traversalMode; - } +public class AlternativeRoute extends AStarBidirection implements RoutingAlgorithm { + private static final Comparator ALT_COMPARATOR = Comparator.comparingDouble(o -> o.sortBy); + private final int maxPaths; + /** + * This variable influences the graph exploration for alternative paths. Specify a higher value than the default to + * potentially get more alternatives and a lower value to improve query time but reduces chance to find alternatives. + */ + private final double explorationFactor; + /** + * Decreasing this factor filters found alternatives and increases quality. E.g. if the factor is 2 than + * all alternatives with a weight 2 times longer than the optimal weight are return. + */ + private final double maxWeightFactor; + /** + * Decreasing this factor filters found alternatives and might increase quality. This parameter is used to avoid + * alternatives too similar to the best path. Specify 0.2 to ensure maximum 20% of the best path are on the same roads. + * The unit is also the 'weight'. + */ + private final double maxShareFactor; /** - * This method sets the approximation used for the internal bidirectional A*. + * Increasing this factor filters found alternatives and might increase quality. This specifies the minimum plateau + * portion of every alternative path that is required. Keep in mind that a plateau is often not complete especially + * when the explorationFactor is low (and for performance reasons the explorationFactor should be as low as possible). + * This is the reason we cannot require a too big plateau portion here as default. */ - public void setApproximation(WeightApproximator weightApproximator) { - this.weightApproximator = weightApproximator; + private final double minPlateauFactor; + + public AlternativeRoute(Graph graph, Weighting weighting, TraversalMode traversalMode, PMap hints) { + super(graph, weighting, traversalMode); + if (weighting.hasTurnCosts() && !traversalMode.isEdgeBased()) + throw new IllegalStateException("Weightings supporting turn costs cannot be used with node-based traversal mode"); + + this.maxPaths = hints.getInt(MAX_PATHS, 2); + if (this.maxPaths < 2) + throw new IllegalArgumentException("Use normal algorithm with less overhead instead if no alternatives are required"); + + this.explorationFactor = hints.getDouble("alternative_route.max_exploration_factor", 1.12); + this.maxWeightFactor = hints.getDouble(MAX_WEIGHT, 1.25); + this.maxShareFactor = hints.getDouble(MAX_SHARE, 0.6); + this.minPlateauFactor = hints.getDouble("alternative_route.min_plateau_factor", 0.1); } static List getAltNames(Graph graph, SPTEntry ee) { @@ -119,78 +127,19 @@ public void setMaxVisitedNodes(int numberOfNodes) { this.maxVisitedNodes = numberOfNodes; } - /** - * Increasing this factor results in returning more alternatives. E.g. if the factor is 2 than - * all alternatives with a weight 2 times longer than the optimal weight are return. (default is - * 1) - */ - public void setMaxWeightFactor(double maxWeightFactor) { - this.maxWeightFactor = maxWeightFactor; - } - - /** - * This parameter is used to avoid alternatives too similar to the best path. Specify 0.5 to - * force a same paths of maximum 50%. The unit is the 'weight' returned in the Weighting. - */ - public void setMaxShareFactor(double maxShareFactor) { - this.maxShareFactor = maxShareFactor; - } - - /** - * This method sets the minimum plateau portion of every alternative path that is required. - */ - public void setMinPlateauFactor(double minPlateauFactor) { - this.minPlateauFactor = minPlateauFactor; - } - - /** - * This method sets the graph exploration percentage for alternative paths. Default for bidirectional A* - * is 0.8 (80%). Specify a higher value to get more alternatives (especially if maxWeightFactor is higher than - * 1.5) and a lower value to improve query time but reduces the possibility to find alternatives. - */ - public void setMaxExplorationFactor(double explorationFactor) { - this.maxExplorationFactor = explorationFactor; - } - - /** - * Specifies how many paths (including the optimal) are returned. (default is 2) - */ - public void setMaxPaths(int maxPaths) { - this.maxPaths = maxPaths; - if (this.maxPaths < 2) - throw new IllegalArgumentException("Use normal algorithm with less overhead instead if no alternatives are required"); - } - - /** - * This method calculates best paths (alternatives) between 'from' and 'to', where maxPaths-1 - * alternatives are searched and they are only accepted if they are not too similar but close to - * the best path. - */ public List calcAlternatives(int from, int to) { - AlternativeBidirSearch altBidirDijktra = new AlternativeBidirSearch( - graph, weighting, traversalMode, maxExplorationFactor * 2); - altBidirDijktra.setMaxVisitedNodes(maxVisitedNodes); - if (weightApproximator != null) { - altBidirDijktra.setApproximation(weightApproximator); - } - - Path bestPath = altBidirDijktra.searchBest(from, to); - visitedNodes = altBidirDijktra.getVisitedNodes(); - - return altBidirDijktra. - calcAlternatives(bestPath, maxPaths, maxWeightFactor, 7, maxShareFactor, 0.8, minPlateauFactor, -0.2); - } - - @Override - public Path calcPath(int from, int to) { - return calcPaths(from, to).get(0); + Path bestPath = searchBest(from, to); + return calcAlternatives(bestPath, maxPaths, + maxWeightFactor, 7, + maxShareFactor, 0.8, + minPlateauFactor, -0.2); } @Override public List calcPaths(int from, int to) { - List alts = calcAlternatives(from, to); - List paths = new ArrayList<>(alts.size()); - for (AlternativeInfo a : alts) { + List alternatives = calcAlternatives(from, to); + List paths = new ArrayList<>(alternatives.size()); + for (AlternativeInfo a : alternatives) { paths.add(a.getPath()); } return paths; @@ -201,11 +150,6 @@ public String getName() { return Parameters.Algorithms.ALT_ROUTE; } - @Override - public int getVisitedNodes() { - return visitedNodes; - } - public static class AlternativeInfo { private final double sortBy; private final Path path; @@ -251,312 +195,263 @@ public String toString() { } } - /** - * Helper class to find alternatives and alternatives for round trip. - */ - public static class AlternativeBidirSearch extends AStarBidirection { - private final double explorationFactor; - - public AlternativeBidirSearch(Graph graph, Weighting weighting, TraversalMode tMode, - double explorationFactor) { - super(graph, weighting, tMode); - this.explorationFactor = explorationFactor; - } - - @Override - public boolean finished() { - // we need to finish BOTH searches identical to CH - if (finishedFrom && finishedTo) - return true; - - if (isMaxVisitedNodesExceeded()) - return true; - - // The following condition is necessary to avoid traversing the full graph if areas are disconnected - // but it is only valid for non-CH e.g. for CH it can happen that finishedTo is true but the from-SPT could still reach 'to' - if (finishedFrom || finishedTo) - return true; - - // increase overlap of both searches: - return currFrom.weight + currTo.weight > explorationFactor * (bestWeight + stoppingCriterionOffset); - // This is more precise but takes roughly 20% longer: return currFrom.weight > bestWeight && currTo.weight > bestWeight; - // For bidir A* and AStarEdge.getWeightOfVisitedPath see comment in AStarBidirection.finished - } - - public Path searchBest(int from, int to) { - init(from, 0, to, 0); - // init collections and bestPath.getWeight properly - runAlgo(); - return extractPath(); - } + @Override + public boolean finished() { + // we need to finish BOTH searches identical to CH + if (finishedFrom && finishedTo) + return true; + + if (isMaxVisitedNodesExceeded()) + return true; + + // The following condition is necessary to avoid traversing the full graph if areas are disconnected + // but it is only valid for non-CH e.g. for CH it can happen that finishedTo is true but the from-SPT could still reach 'to' + if (finishedFrom || finishedTo) + return true; + + // increase overlap of both searches: + return currFrom.weight + currTo.weight > explorationFactor * (bestWeight + stoppingCriterionOffset); + // This is more precise but takes roughly 20% longer: return currFrom.weight > bestWeight && currTo.weight > bestWeight; + // For bidir A* and AStarEdge.getWeightOfVisitedPath see comment in AStarBidirection.finished + } - /** - * @return the information necessary to handle alternative paths. Note that the paths are - * not yet extracted. - */ - public List calcAlternatives(final Path bestPath, final int maxPaths, - double maxWeightFactor, final double weightInfluence, - final double maxShareFactor, final double shareInfluence, - final double minPlateauFactor, final double plateauInfluence) { - final double maxWeight = maxWeightFactor * bestWeight; - final GHIntObjectHashMap traversalIdMap = new GHIntObjectHashMap<>(); - final AtomicInteger startTID = addToMap(traversalIdMap, bestPath); - - // find all 'good' alternatives from forward-SPT matching the backward-SPT and optimize by - // small total weight (1), small share and big plateau (3a+b) and do these expensive calculations - // only for plateau start candidates (2) - final List alternatives = new ArrayList<>(maxPaths); - - double bestPlateau = bestWeight; - double bestShare = 0; - double sortBy = calcSortBy(weightInfluence, bestWeight, - shareInfluence, bestShare, - plateauInfluence, bestPlateau); - - final AlternativeInfo bestAlt = new AlternativeInfo(sortBy, bestPath, - bestFwdEntry, bestBwdEntry, bestShare, getAltNames(graph, bestFwdEntry)); - alternatives.add(bestAlt); - final List bestPathEntries = new ArrayList<>(2); - - bestWeightMapFrom.forEach(new IntObjectPredicate() { - @Override - public boolean apply(final int traversalId, final SPTEntry fromSPTEntry) { - SPTEntry toSPTEntry = bestWeightMapTo.get(traversalId); - if (toSPTEntry == null) - return true; + public Path searchBest(int from, int to) { + init(from, 0, to, 0); + // init collections and bestPath.getWeight properly + runAlgo(); + return extractPath(); + } - if (traversalMode.isEdgeBased()) { - if (toSPTEntry.parent != null) - // move to parent for two reasons: - // 1. make only turn costs missing in 'weight' and not duplicating current edge.weight - // 2. to avoid duplicate edge in Path - toSPTEntry = toSPTEntry.parent; - // TODO else if fromSPTEntry.parent != null fromSPTEntry = fromSPTEntry.parent; + /** + * @return the information necessary to handle alternative paths. Note that the paths are + * not yet extracted. + */ + public List calcAlternatives(final Path bestPath, final int maxPaths, + double maxWeightFactor, final double weightInfluence, + final double maxShareFactor, final double shareInfluence, + final double minPlateauFactor, final double plateauInfluence) { + final double maxWeight = maxWeightFactor * bestWeight; + final GHIntObjectHashMap traversalIdMap = new GHIntObjectHashMap<>(); + final AtomicInteger startTID = addToMap(traversalIdMap, bestPath); + + // find all 'good' alternatives from forward-SPT matching the backward-SPT and optimize by + // small total weight (1), small share and big plateau (3a+b) and do these expensive calculations + // only for plateau start candidates (2) + final List alternatives = new ArrayList<>(maxPaths); + + double bestPlateau = bestWeight; + double bestShare = 0; + double sortBy = calcSortBy(weightInfluence, bestWeight, + shareInfluence, bestShare, + plateauInfluence, bestPlateau); + + final AlternativeInfo bestAlt = new AlternativeInfo(sortBy, bestPath, + bestFwdEntry, bestBwdEntry, bestShare, getAltNames(graph, bestFwdEntry)); + alternatives.add(bestAlt); + AtomicReference bestEntry = new AtomicReference<>(); + + bestWeightMapFrom.forEach(new IntObjectPredicate() { + @Override + public boolean apply(final int traversalId, final SPTEntry fromSPTEntry) { + SPTEntry toSPTEntry = bestWeightMapTo.get(traversalId); + if (toSPTEntry == null) + return true; - } else // The alternative path is suboptimal when both entries are parallel - if (fromSPTEntry.edge == toSPTEntry.edge) - return true; + // Using the parent is required to avoid duplicate edge in Path. + // TODO we miss the turn cost weight (but at least we not duplicate the current edge weight) + if (traversalMode.isEdgeBased() && toSPTEntry.parent != null) + toSPTEntry = toSPTEntry.parent; - // (1) skip too long paths - final double weight = fromSPTEntry.getWeightOfVisitedPath() + toSPTEntry.getWeightOfVisitedPath(); - if (weight > maxWeight) - return true; + // The alternative path is suboptimal if U-turn (after fromSPTEntry) + if (fromSPTEntry.edge == toSPTEntry.edge) + return true; - // (2) Use the start traversal ID of a plateau as ID for the alternative path. - // Accept from-EdgeEntries only if such a start of a plateau - // i.e. discard if its parent has the same edgeId as the next to-SPTEntry. - // Ignore already added best path - if (isBestPath(fromSPTEntry)) - return true; + // (1) skip too long paths + final double weight = fromSPTEntry.getWeightOfVisitedPath() + toSPTEntry.getWeightOfVisitedPath(); + if (weight > maxWeight) + return true; - // For edge based traversal we need the next entry to find out the plateau start - SPTEntry tmpFromEntry = traversalMode.isEdgeBased() ? fromSPTEntry.parent : fromSPTEntry; - if (tmpFromEntry == null || tmpFromEntry.parent == null) { - // we can be here only if edge based and only if entry is not part of the best path - // e.g. when starting point has two edges and one is part of the best path the other edge is path of an alternative - assert traversalMode.isEdgeBased(); - } else { - int nextToTraversalId = traversalMode.createTraversalId(tmpFromEntry.adjNode, - tmpFromEntry.parent.adjNode, tmpFromEntry.edge, true); - SPTEntry tmpNextToSPTEntry = bestWeightMapTo.get(nextToTraversalId); - if (tmpNextToSPTEntry == null) - return true; + if (isBestPath(fromSPTEntry)) + return true; + // For edge based traversal we need the next entry to find out the plateau start + SPTEntry tmpFromEntry = traversalMode.isEdgeBased() ? fromSPTEntry.parent : fromSPTEntry; + if (tmpFromEntry == null || tmpFromEntry.parent == null) { + // we can be here only if edge based and only if entry is not part of the best path + // e.g. when starting point has two edges and one is part of the best path the other edge is path of an alternative + assert traversalMode.isEdgeBased(); + } else { + int nextToTraversalId = traversalMode.createTraversalId(graph.getEdgeIteratorState(tmpFromEntry.edge, tmpFromEntry.parent.adjNode), true); + SPTEntry correspondingToEntry = bestWeightMapTo.get(nextToTraversalId); + if (correspondingToEntry != null) { if (traversalMode.isEdgeBased()) - tmpNextToSPTEntry = tmpNextToSPTEntry.parent; - // skip if on plateau - if (fromSPTEntry.edge == tmpNextToSPTEntry.edge) + correspondingToEntry = correspondingToEntry.parent; + if (correspondingToEntry.edge == fromSPTEntry.edge) return true; } + } - // (3a) calculate plateau, we know we are at the beginning of the 'from'-side of - // the plateau A-B-C and go further to B - // where B is the next-'from' of A and B is also the previous-'to' of A. - // - // *<-A-B-C->* - // / \ - // start end - // - // extend plateau in only one direction necessary (A to B to ...) as we know - // that the from-SPTEntry is the start of the plateau or there is no plateau at all - // - double plateauWeight = 0; - SPTEntry prevToSPTEntry = toSPTEntry; - // List plateauEdges = new ArrayList(); - while (prevToSPTEntry.parent != null) { - int nextFromTraversalId = traversalMode.createTraversalId(prevToSPTEntry.adjNode, prevToSPTEntry.parent.adjNode, - prevToSPTEntry.edge, false); - - SPTEntry nextFromSPTEntry = bestWeightMapFrom.get(nextFromTraversalId); - // end of a plateau - if (nextFromSPTEntry == null) - break; - - // is the next from-SPTEntry on the plateau? - if (prevToSPTEntry.edge != nextFromSPTEntry.edge) - break; - - // plateauEdges.add(prevToSPTEntry.edge); - plateauWeight += (prevToSPTEntry.getWeightOfVisitedPath() - prevToSPTEntry.parent.getWeightOfVisitedPath()); - prevToSPTEntry = prevToSPTEntry.parent; - } - - if (plateauWeight <= 0 || plateauWeight / weight < minPlateauFactor) - return true; - - if (fromSPTEntry.parent == null) - throw new IllegalStateException("not implemented yet. in case of an edge based traversal the parent of fromSPTEntry could be null"); + // (3a) calculate plateau, we know we are at the beginning of the 'from'-side of + // the plateau A-B-C and go further to B + // where B is the next-'from' of A and B is also the previous-'to' of A. + // + // *<-A-B-C->* + // / \ + // start end + // + // extend plateau in only one direction necessary (A to B to ...) as we know + // that the from-SPTEntry is the start of the plateau or there is no plateau at all + // + double plateauWeight = 0; + SPTEntry prevToSPTEntry = toSPTEntry, prevFrom = fromSPTEntry; + while (prevToSPTEntry.parent != null) { + int nextFromTraversalId = traversalMode.createTraversalId(graph.getEdgeIteratorState(prevToSPTEntry.edge, prevToSPTEntry.parent.adjNode), false); + SPTEntry otherFromEntry = bestWeightMapFrom.get(nextFromTraversalId); + // end of a plateau + if (otherFromEntry == null || + otherFromEntry.parent != prevFrom || + otherFromEntry.edge != prevToSPTEntry.edge) + break; + + prevFrom = otherFromEntry; + plateauWeight += (prevToSPTEntry.getWeightOfVisitedPath() - prevToSPTEntry.parent.getWeightOfVisitedPath()); + prevToSPTEntry = prevToSPTEntry.parent; + } - // (3b) calculate share - SPTEntry fromEE = getFirstShareEE(fromSPTEntry.parent, true); - SPTEntry toEE = getFirstShareEE(toSPTEntry.parent, false); - double shareWeight = fromEE.getWeightOfVisitedPath() + toEE.getWeightOfVisitedPath(); - boolean smallShare = shareWeight / bestWeight < maxShareFactor; - if (smallShare) { - List altNames = getAltNames(graph, fromSPTEntry); + if (plateauWeight <= 0 || plateauWeight / weight < minPlateauFactor) + return true; - double sortBy = calcSortBy(weightInfluence, weight, shareInfluence, shareWeight, plateauInfluence, plateauWeight); - double worstSortBy = getWorstSortBy(); + if (fromSPTEntry.parent == null) + throw new IllegalStateException("not implemented yet. in case of an edge based traversal the parent of fromSPTEntry could be null"); - // plateaus.add(new PlateauInfo(altName, plateauEdges)); - if (sortBy < worstSortBy || alternatives.size() < maxPaths) { - Path path = DefaultBidirPathExtractor.extractPath(graph, weighting, fromSPTEntry, toSPTEntry, weight); + // (3b) calculate share + SPTEntry fromEE = getFirstShareEE(fromSPTEntry.parent, true); + SPTEntry toEE = getFirstShareEE(toSPTEntry.parent, false); + double shareWeight = fromEE.getWeightOfVisitedPath() + toEE.getWeightOfVisitedPath(); + boolean smallShare = shareWeight / bestWeight < maxShareFactor; + if (smallShare) { + List altNames = getAltNames(graph, fromSPTEntry); - // for now do not add alternatives to set, if we do we need to remove then on alternatives.clear too (see below) - // AtomicInteger tid = addToMap(traversalIDMap, path); - // int tid = traversalMode.createTraversalId(path.calcEdges().get(0), false); - alternatives.add(new AlternativeInfo(sortBy, path, fromEE, toEE, shareWeight, altNames)); + double sortBy = calcSortBy(weightInfluence, weight, shareInfluence, shareWeight, plateauInfluence, plateauWeight); + double worstSortBy = getWorstSortBy(); - Collections.sort(alternatives, ALT_COMPARATOR); - if (alternatives.get(0) != bestAlt) - throw new IllegalStateException("best path should be always first entry"); + // plateaus.add(new PlateauInfo(altName, plateauEdges)); + if (sortBy < worstSortBy || alternatives.size() < maxPaths) { + Path path = DefaultBidirPathExtractor.extractPath(graph, weighting, fromSPTEntry, toSPTEntry, weight); - if (alternatives.size() > maxPaths) - alternatives.subList(maxPaths, alternatives.size()).clear(); - } - } + // for now do not add alternatives to set, if we do we need to remove then on alternatives.clear too (see below) + // AtomicInteger tid = addToMap(traversalIDMap, path); + // int tid = traversalMode.createTraversalId(path.calcEdges().get(0), false); + alternatives.add(new AlternativeInfo(sortBy, path, fromEE, toEE, shareWeight, altNames)); - return true; - } + Collections.sort(alternatives, ALT_COMPARATOR); + if (alternatives.get(0) != bestAlt) + throw new IllegalStateException("best path should be always first entry"); - /** - * Extract path until we stumble over an existing traversal id - */ - SPTEntry getFirstShareEE(SPTEntry startEE, boolean reverse) { - while (startEE.parent != null) { - // TODO we could make use of traversal ID directly if stored in SPTEntry - int tid = traversalMode.createTraversalId(startEE.adjNode, startEE.parent.adjNode, startEE.edge, reverse); - if (isAlreadyExisting(tid)) - return startEE; - - startEE = startEE.parent; + if (alternatives.size() > maxPaths) + alternatives.subList(maxPaths, alternatives.size()).clear(); } - - return startEE; } - /** - * This method returns true if the specified tid is already existent in the - * traversalIDMap - */ - boolean isAlreadyExisting(final int tid) { - final AtomicBoolean exists = new AtomicBoolean(false); - traversalIdMap.forEach(new IntObjectPredicate() { - @Override - public boolean apply(int key, IntSet set) { - if (set.contains(tid)) { - exists.set(true); - return false; - } - return true; - } - }); - - return exists.get(); - } + return true; + } - /** - * Return the current worst weight for all alternatives - */ - double getWorstSortBy() { - if (alternatives.isEmpty()) - throw new IllegalStateException("Empty alternative list cannot happen"); - return alternatives.get(alternatives.size() - 1).sortBy; + /** + * Extract path until we stumble over an existing traversal id + */ + SPTEntry getFirstShareEE(SPTEntry startEE, boolean reverse) { + while (startEE.parent != null) { + // TODO we could make use of traversal ID directly if stored in SPTEntry + int tid = traversalMode.createTraversalId(graph.getEdgeIteratorState(startEE.edge, startEE.parent.adjNode), reverse); + if (isAlreadyExisting(tid)) + return startEE; + + startEE = startEE.parent; } - // returns true if fromSPTEntry is identical to the specified best path - boolean isBestPath(SPTEntry fromSPTEntry) { - if (traversalMode.isEdgeBased()) { - if (GHUtility.getEdgeFromEdgeKey(startTID.get()) == fromSPTEntry.edge) { - if (fromSPTEntry.parent == null) - throw new IllegalStateException("best path must have no parent but was non-null: " + fromSPTEntry); + return startEE; + } - return true; + /** + * This method returns true if the specified tid is already existent in the + * traversalIDMap + */ + boolean isAlreadyExisting(final int tid) { + final AtomicBoolean exists = new AtomicBoolean(false); + traversalIdMap.forEach(new IntObjectPredicate() { + @Override + public boolean apply(int key, IntSet set) { + if (set.contains(tid)) { + exists.set(true); + return false; } - - } else if (fromSPTEntry.parent == null) { - bestPathEntries.add(fromSPTEntry); - if (bestPathEntries.size() > 1) - throw new IllegalStateException("There is only one best path but was: " + bestPathEntries); - - if (startTID.get() != fromSPTEntry.adjNode) - throw new IllegalStateException("Start traversal ID has to be identical to root edge entry " - + "which is the plateau start of the best path but was: " + startTID + " vs. adjNode: " + fromSPTEntry.adjNode); - return true; } + }); - return false; - } - }); + return exists.get(); + } - return alternatives; - } + /** + * Return the current worst weight for all alternatives + */ + double getWorstSortBy() { + if (alternatives.isEmpty()) + throw new IllegalStateException("Empty alternative list cannot happen"); + return alternatives.get(alternatives.size() - 1).sortBy; + } - /** - * This method adds the traversal IDs of the specified path as set to the specified map. - */ - AtomicInteger addToMap(GHIntObjectHashMap map, Path path) { - IntSet set = new GHIntHashSet(); - final AtomicInteger startTID = new AtomicInteger(-1); - for (EdgeIteratorState iterState : path.calcEdges()) { - int tid = traversalMode.createTraversalId(iterState, false); - set.add(tid); - if (startTID.get() < 0) { - // for node based traversal we need to explicitly add base node as starting node and to list - if (!traversalMode.isEdgeBased()) { - tid = iterState.getBaseNode(); - set.add(tid); + // returns true if fromSPTEntry is identical to the specified best path + boolean isBestPath(SPTEntry fromSPTEntry) { + if (traversalMode.isEdgeBased()) { + if (GHUtility.getEdgeFromEdgeKey(startTID.get()) == fromSPTEntry.edge) { + if (fromSPTEntry.parent == null) + throw new IllegalStateException("best path must have no parent but was non-null: " + fromSPTEntry); + if (bestEntry.get() != null && bestEntry.get().edge != fromSPTEntry.edge) + throw new IllegalStateException("there can be only one best entry but was " + fromSPTEntry + " vs old: " + bestEntry.get() + + " " + graph.getEdgeIteratorState(fromSPTEntry.edge, fromSPTEntry.adjNode).fetchWayGeometry(FetchMode.ALL)); + bestEntry.set(fromSPTEntry); + return true; } - startTID.set(tid); + } else if (fromSPTEntry.parent == null) { + if (startTID.get() != fromSPTEntry.adjNode) + throw new IllegalStateException("Start traversal ID has to be identical to root edge entry " + + "which is the plateau start of the best path but was: " + startTID + " vs. adjNode: " + fromSPTEntry.adjNode); + if (bestEntry.get() != null) + throw new IllegalStateException("there can be only one best entry but was " + fromSPTEntry + " vs old: " + bestEntry.get() + + " " + graph.getEdgeIteratorState(fromSPTEntry.edge, fromSPTEntry.adjNode).fetchWayGeometry(FetchMode.ALL)); + bestEntry.set(fromSPTEntry); + return true; } - } - map.put(startTID.get(), set); - return startTID; - } - } - - public static class PlateauInfo { - String name; - List edges; - public PlateauInfo(String name, List edges) { - this.name = name; - this.edges = edges; - } + return false; + } + }); - @Override - public String toString() { - return name; - } + return alternatives; + } - public List getEdges() { - return edges; - } + /** + * This method adds the traversal IDs of the specified path as set to the specified map. + */ + AtomicInteger addToMap(GHIntObjectHashMap map, Path path) { + IntSet set = new GHIntHashSet(); + final AtomicInteger startTID = new AtomicInteger(-1); + for (EdgeIteratorState iterState : path.calcEdges()) { + int tid = traversalMode.createTraversalId(iterState, false); + set.add(tid); + if (startTID.get() < 0) { + // for node based traversal we need to explicitly add base node as starting node and to list + if (!traversalMode.isEdgeBased()) { + tid = iterState.getBaseNode(); + set.add(tid); + } - public String getName() { - return name; + startTID.set(tid); + } } + map.put(startTID.get(), set); + return startTID; } } diff --git a/core/src/main/java/com/graphhopper/routing/CHPathCalculator.java b/core/src/main/java/com/graphhopper/routing/CHPathCalculator.java index 65f87b5e672..2c92a1e88fa 100644 --- a/core/src/main/java/com/graphhopper/routing/CHPathCalculator.java +++ b/core/src/main/java/com/graphhopper/routing/CHPathCalculator.java @@ -44,18 +44,18 @@ public CHPathCalculator(CHRoutingAlgorithmFactory algoFactory, PMap algoOpts) { public List calcPaths(int from, int to, EdgeRestrictions edgeRestrictions) { if (!edgeRestrictions.getUnfavoredEdges().isEmpty()) throw new IllegalArgumentException("Using unfavored edges is currently not supported for CH"); - BidirRoutingAlgorithm algo = createAlgo(); + EdgeToEdgeRoutingAlgorithm algo = createAlgo(); return calcPaths(from, to, edgeRestrictions, algo); } - private BidirRoutingAlgorithm createAlgo() { + private EdgeToEdgeRoutingAlgorithm createAlgo() { StopWatch sw = new StopWatch().start(); - BidirRoutingAlgorithm algo = algoFactory.createAlgo(algoOpts); + EdgeToEdgeRoutingAlgorithm algo = algoFactory.createAlgo(algoOpts); debug = ", algoInit:" + (sw.stop().getNanos() / 1000) + " μs"; return algo; } - private List calcPaths(int from, int to, EdgeRestrictions edgeRestrictions, BidirRoutingAlgorithm algo) { + private List calcPaths(int from, int to, EdgeRestrictions edgeRestrictions, EdgeToEdgeRoutingAlgorithm algo) { StopWatch sw = new StopWatch().start(); List paths; if (edgeRestrictions.getSourceOutEdge() != ANY_EDGE || edgeRestrictions.getTargetInEdge() != ANY_EDGE) { diff --git a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java index f53d9642bbb..c8c2c0f6321 100644 --- a/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java +++ b/core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java @@ -19,8 +19,9 @@ package com.graphhopper.routing; import com.graphhopper.config.Profile; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.util.VehicleEncodedValues; import com.graphhopper.routing.weighting.*; import com.graphhopper.routing.weighting.custom.CustomModelParser; import com.graphhopper.routing.weighting.custom.CustomProfile; @@ -30,11 +31,14 @@ import com.graphhopper.util.PMap; import com.graphhopper.util.Parameters; +import static com.graphhopper.routing.weighting.FastestWeighting.DESTINATION_FACTOR; +import static com.graphhopper.routing.weighting.FastestWeighting.PRIVATE_FACTOR; import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER; import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS; import static com.graphhopper.util.Helper.toLowerCase; public class DefaultWeightingFactory implements WeightingFactory { + private final BaseGraph graph; private final EncodingManager encodingManager; @@ -48,19 +52,24 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis // Merge profile hints with request hints, the request hints take precedence. // Note that so far we do not check if overwriting the profile hints actually works with the preparation // for LM/CH. Later we should also limit the number of parameters that can be used to modify the profile. - // todo: since we are not dealing with block_area here yet we cannot really apply any merging rules - // for it, see discussion here: https://github.com/graphhopper/graphhopper/pull/1958#discussion_r395462901 PMap hints = new PMap(); hints.putAll(profile.getHints()); hints.putAll(requestHints); - FlagEncoder encoder = encodingManager.getEncoder(profile.getVehicle()); + final String vehicle = profile.getVehicle(); + if (isOutdoorVehicle(vehicle)) { + hints.putObject(PRIVATE_FACTOR, hints.getDouble(PRIVATE_FACTOR, 1.2)); + } else { + hints.putObject(DESTINATION_FACTOR, hints.getDouble(DESTINATION_FACTOR, 10)); + hints.putObject(PRIVATE_FACTOR, hints.getDouble(PRIVATE_FACTOR, 10)); + } TurnCostProvider turnCostProvider; if (profile.isTurnCosts() && !disableTurnCosts) { - if (!encoder.supportsTurnCosts()) - throw new IllegalArgumentException("Encoder " + encoder + " does not support turn costs"); + DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(vehicle)); + if (turnCostEnc == null) + throw new IllegalArgumentException("Vehicle " + vehicle + " does not support turn costs"); int uTurnCosts = hints.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS); - turnCostProvider = new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage(), uTurnCosts); + turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), uTurnCosts); } else { turnCostProvider = NO_TURN_COST_PROVIDER; } @@ -70,29 +79,38 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis throw new IllegalArgumentException("You have to specify a weighting"); Weighting weighting = null; + BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key(vehicle)); + DecimalEncodedValue speedEnc = encodingManager.getDecimalEncodedValue(VehicleSpeed.key(vehicle)); + DecimalEncodedValue priorityEnc = encodingManager.hasEncodedValue(VehiclePriority.key(vehicle)) + ? encodingManager.getDecimalEncodedValue(VehiclePriority.key(vehicle)) + : null; if (CustomWeighting.NAME.equalsIgnoreCase(weightingStr)) { if (!(profile instanceof CustomProfile)) throw new IllegalArgumentException("custom weighting requires a CustomProfile but was profile=" + profile.getName()); CustomModel queryCustomModel = requestHints.getObject(CustomModel.KEY, null); CustomProfile customProfile = (CustomProfile) profile; - if (queryCustomModel != null) - queryCustomModel.checkLMConstraints(customProfile.getCustomModel()); queryCustomModel = CustomModel.merge(customProfile.getCustomModel(), queryCustomModel); - weighting = CustomModelParser.createWeighting(encoder, encodingManager, turnCostProvider, queryCustomModel); + weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, + priorityEnc, encodingManager, turnCostProvider, queryCustomModel); } else if ("shortest".equalsIgnoreCase(weightingStr)) { - weighting = new ShortestWeighting(encoder, turnCostProvider); + weighting = new ShortestWeighting(accessEnc, speedEnc, turnCostProvider); } else if ("fastest".equalsIgnoreCase(weightingStr)) { - if (encoder.supports(PriorityWeighting.class)) - weighting = new PriorityWeighting(encoder, hints, turnCostProvider); + if (!encodingManager.hasEncodedValue(RoadAccess.KEY)) + throw new IllegalArgumentException("The fastest weighting requires road_access"); + EnumEncodedValue roadAccessEnc = encodingManager.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); + if (priorityEnc != null) + weighting = new PriorityWeighting(accessEnc, speedEnc, priorityEnc, roadAccessEnc, hints, turnCostProvider); else - weighting = new FastestWeighting(encoder, hints, turnCostProvider); + weighting = new FastestWeighting(accessEnc, speedEnc, roadAccessEnc, hints, turnCostProvider); } else if ("curvature".equalsIgnoreCase(weightingStr)) { - if (encoder.supports(CurvatureWeighting.class)) - weighting = new CurvatureWeighting(encoder, hints, turnCostProvider); - + throw new IllegalArgumentException("The curvature weighting is no longer supported since 7.0. Use a custom " + + "model with the EncodedValue 'curvature' instead"); } else if ("short_fastest".equalsIgnoreCase(weightingStr)) { - weighting = new ShortFastestWeighting(encoder, hints, turnCostProvider); + if (!encodingManager.hasEncodedValue(RoadAccess.KEY)) + throw new IllegalArgumentException("The short_fastest weighting requires road_access"); + EnumEncodedValue roadAccessEnc = encodingManager.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); + weighting = new ShortFastestWeighting(accessEnc, speedEnc, roadAccessEnc, hints, turnCostProvider); } if (weighting == null) @@ -100,4 +118,8 @@ public Weighting createWeighting(Profile profile, PMap requestHints, boolean dis return weighting; } + + public boolean isOutdoorVehicle(String name) { + return VehicleEncodedValues.OUTDOOR_VEHICLES.contains(name); + } } \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/Dijkstra.java b/core/src/main/java/com/graphhopper/routing/Dijkstra.java index d821918a464..f52dd20d12d 100644 --- a/core/src/main/java/com/graphhopper/routing/Dijkstra.java +++ b/core/src/main/java/com/graphhopper/routing/Dijkstra.java @@ -58,16 +58,19 @@ protected void initCollections(int size) { public Path calcPath(int from, int to) { checkAlreadyRun(); this.to = to; - currEdge = new SPTEntry(from, 0); - if (!traversalMode.isEdgeBased()) { + SPTEntry startEntry = new SPTEntry(from, 0); + fromHeap.add(startEntry); + if (!traversalMode.isEdgeBased()) fromMap.put(from, currEdge); - } runAlgo(); return extractPath(); } protected void runAlgo() { - while (true) { + while (!fromHeap.isEmpty()) { + currEdge = fromHeap.poll(); + if (currEdge.isDeleted()) + continue; visitedNodes++; if (isMaxVisitedNodesExceeded() || finished()) break; @@ -90,33 +93,23 @@ protected void runAlgo() { fromMap.put(traversalId, nEdge); fromHeap.add(nEdge); } else if (nEdge.weight > tmpWeight) { - fromHeap.remove(nEdge); - nEdge.edge = iter.getEdge(); - nEdge.weight = tmpWeight; - nEdge.parent = currEdge; + nEdge.setDeleted(); + nEdge = new SPTEntry(iter.getEdge(), iter.getAdjNode(), tmpWeight, currEdge); + fromMap.put(traversalId, nEdge); fromHeap.add(nEdge); } else continue; updateBestPath(iter, nEdge, traversalId); } - - if (fromHeap.isEmpty()) - break; - - currEdge = fromHeap.poll(); - if (currEdge == null) - throw new AssertionError("Empty edge cannot happen"); } } - @Override protected boolean finished() { return currEdge.adjNode == to; } - @Override - protected Path extractPath() { + private Path extractPath() { if (currEdge == null || !finished()) return createEmptyPath(); diff --git a/core/src/main/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHNoSOD.java b/core/src/main/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHNoSOD.java index 116f95a8e1c..12bb4c441a4 100644 --- a/core/src/main/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHNoSOD.java +++ b/core/src/main/java/com/graphhopper/routing/DijkstraBidirectionEdgeCHNoSOD.java @@ -40,6 +40,7 @@ protected CHEntry createEntry(int edge, int adjNode, int incEdge, double weight, @Override protected void updateEntry(SPTEntry entry, int edge, int adjNode, int incEdge, double weight, SPTEntry parent, boolean reverse) { + assert entry.adjNode == adjNode; entry.edge = edge; ((CHEntry) entry).incEdge = incEdge; entry.weight = weight; diff --git a/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java b/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java index 33d13dd5460..b2ec769c322 100644 --- a/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java +++ b/core/src/main/java/com/graphhopper/routing/DijkstraOneToMany.java @@ -69,11 +69,6 @@ public DijkstraOneToMany(Graph graph, Weighting weighting, TraversalMode tMode) public Path calcPath(int from, int to) { fromNode = from; endNode = findEndNode(from, to); - return extractPath(); - } - - @Override - public Path extractPath() { if (endNode < 0 || isWeightLimitExceeded()) { Path path = createEmptyPath(); path.setFromNode(fromNode); @@ -204,8 +199,7 @@ public int findEndNode(int from, int to) { } } - @Override - public boolean finished() { + private boolean finished() { return currNode == to; } diff --git a/core/src/main/java/com/graphhopper/routing/BidirRoutingAlgorithm.java b/core/src/main/java/com/graphhopper/routing/EdgeToEdgeRoutingAlgorithm.java similarity index 87% rename from core/src/main/java/com/graphhopper/routing/BidirRoutingAlgorithm.java rename to core/src/main/java/com/graphhopper/routing/EdgeToEdgeRoutingAlgorithm.java index c78595391bf..c28d0008d23 100644 --- a/core/src/main/java/com/graphhopper/routing/BidirRoutingAlgorithm.java +++ b/core/src/main/java/com/graphhopper/routing/EdgeToEdgeRoutingAlgorithm.java @@ -19,7 +19,7 @@ import com.graphhopper.util.EdgeIterator; -public interface BidirRoutingAlgorithm extends RoutingAlgorithm { +public interface EdgeToEdgeRoutingAlgorithm extends RoutingAlgorithm { /** * like {@link #calcPath(int, int)}, but this method also allows to strictly restrict the edge the * path will begin with and the edge it will end with. @@ -29,7 +29,5 @@ public interface BidirRoutingAlgorithm extends RoutingAlgorithm { * @param toInEdge the edge id of the last edge of the path. using {@link EdgeIterator#ANY_EDGE} means * not enforcing the last edge of the path */ - // todo: in principle its also possible to implement this method for unidirectional algorithms (but not sure - // if it is really worth it). Path calcPath(int from, int to, int fromOutEdge, int toInEdge); } diff --git a/core/src/main/java/com/graphhopper/routing/FlexiblePathCalculator.java b/core/src/main/java/com/graphhopper/routing/FlexiblePathCalculator.java index 2f49eff309d..183343749e3 100644 --- a/core/src/main/java/com/graphhopper/routing/FlexiblePathCalculator.java +++ b/core/src/main/java/com/graphhopper/routing/FlexiblePathCalculator.java @@ -70,9 +70,9 @@ private List calcPaths(int from, int to, EdgeRestrictions edgeRestrictions List paths; if (edgeRestrictions.getSourceOutEdge() != ANY_EDGE || edgeRestrictions.getTargetInEdge() != ANY_EDGE) { - if (!(algo instanceof BidirRoutingAlgorithm)) + if (!(algo instanceof EdgeToEdgeRoutingAlgorithm)) throw new IllegalArgumentException("To make use of the " + Parameters.Routing.CURBSIDE + " parameter you need a bidirectional algorithm, got: " + algo.getName()); - paths = Collections.singletonList(((BidirRoutingAlgorithm) algo).calcPath(from, to, edgeRestrictions.getSourceOutEdge(), edgeRestrictions.getTargetInEdge())); + paths = Collections.singletonList(((EdgeToEdgeRoutingAlgorithm) algo).calcPath(from, to, edgeRestrictions.getSourceOutEdge(), edgeRestrictions.getTargetInEdge())); } else { paths = algo.calcPaths(from, to); } diff --git a/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java b/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java index 84791342770..29db65622f2 100644 --- a/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java +++ b/core/src/main/java/com/graphhopper/routing/InstructionsFromEdges.java @@ -18,13 +18,14 @@ package com.graphhopper.routing; import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.FiniteWeightFilter; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.util.*; import com.graphhopper.util.shapes.GHPoint; +import static com.graphhopper.search.KVStorage.KeyValue.*; + /** * This class calculates instructions from the edges in a Path. * @@ -74,6 +75,7 @@ public class InstructionsFromEdges implements Path.EdgeVisitor { private double prevInstructionPrevOrientation = Double.NaN; private Instruction prevInstruction; private boolean prevInRoundabout; + private String prevDestinationAndRef; private String prevName; private String prevInstructionName; @@ -138,18 +140,24 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { assert Double.compare(prevLon, nodeAccess.getLon(baseNode)) == 0; } - String name = edge.getName(); - - if ((prevName == null) && (!isRoundabout)) // very first instruction (if not in Roundabout) + final String name = (String) edge.getValue(STREET_NAME); + final String ref = (String) edge.getValue(STREET_REF); + final String destination = (String) edge.getValue(STREET_DESTINATION); // getValue is fast if it does not exist in edge + final String destinationRef = (String) edge.getValue(STREET_DESTINATION_REF); + if ((prevInstruction == null) && (!isRoundabout)) // very first instruction (if not in Roundabout) { int sign = Instruction.CONTINUE_ON_STREET; prevInstruction = new Instruction(sign, name, new PointList(10, nodeAccess.is3D())); + prevInstruction.setExtraInfo(STREET_REF, ref); + prevInstruction.setExtraInfo(STREET_DESTINATION, destination); + prevInstruction.setExtraInfo(STREET_DESTINATION_REF, destinationRef); double startLat = nodeAccess.getLat(baseNode); double startLon = nodeAccess.getLon(baseNode); double heading = AngleCalc.ANGLE_CALC.calcAzimuth(startLat, startLon, latitude, longitude); prevInstruction.setExtraInfo("heading", Helper.round(heading, 2)); ways.add(prevInstruction); prevName = name; + prevDestinationAndRef = destination + destinationRef; } else if (isRoundabout) { // remark: names and annotations within roundabout are ignored @@ -159,7 +167,7 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { RoundaboutInstruction roundaboutInstruction = new RoundaboutInstruction(sign, name, new PointList(10, nodeAccess.is3D())); prevInstructionPrevOrientation = prevOrientation; - if (prevName != null) { + if (prevInstruction != null) { // check if there is an exit at the same node the roundabout was entered EdgeIterator edgeIter = outEdgeExplorer.setBaseNode(baseNode); while (edgeIter.next()) { @@ -183,6 +191,7 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { { prevOrientation = AngleCalc.ANGLE_CALC.calcOrientation(prevLat, prevLon, latitude, longitude); prevName = name; + prevDestinationAndRef = destination + destinationRef; } prevInstruction = roundaboutInstruction; ways.add(prevInstruction); @@ -201,6 +210,9 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { } else if (prevInRoundabout) //previously in roundabout but not anymore { prevInstruction.setName(name); + prevInstruction.setExtraInfo(STREET_REF, ref); + prevInstruction.setExtraInfo(STREET_DESTINATION, destination); + prevInstruction.setExtraInfo(STREET_DESTINATION_REF, destinationRef); // calc angle between roundabout entrance and exit double orientation = AngleCalc.ANGLE_CALC.calcOrientation(prevLat, prevLon, latitude, longitude); @@ -220,10 +232,10 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { prevInstructionName = prevName; prevName = name; + prevDestinationAndRef = destination + destinationRef; } else { - int sign = getTurn(edge, baseNode, prevNode, adjNode, name); - + int sign = getTurn(edge, baseNode, prevNode, adjNode, name, destination + destinationRef); if (sign != Instruction.IGNORE) { /* Check if the next instruction is likely to only be a short connector to execute a u-turn @@ -274,10 +286,14 @@ public void next(EdgeIteratorState edge, int index, int prevEdgeId) { prevInstructionName = prevName; ways.add(prevInstruction); } + prevInstruction.setExtraInfo(STREET_REF, ref); + prevInstruction.setExtraInfo(STREET_DESTINATION, destination); + prevInstruction.setExtraInfo(STREET_DESTINATION_REF, destinationRef); } // Update the prevName, since we don't always create an instruction on name changes the previous // name can be an old name. This leads to incorrect turn instructions due to name changes prevName = name; + prevDestinationAndRef = destination + destinationRef; } updatePointsAndInstruction(edge, wayGeo); @@ -315,7 +331,10 @@ public void finish() { ways.add(finishInstruction); } - private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjNode, String name) { + private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjNode, String name, String destinationAndRef) { + if (edge.getEdge() == prevEdge.getEdge()) + // this is the simplest turn to recognize, a plain u-turn. + return Instruction.U_TURN_UNKNOWN; GHPoint point = InstructionsHelper.getPointForOrientationCalculation(edge, nodeAccess); double lat = point.getLat(); double lon = point.getLon(); @@ -340,10 +359,8 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN // Very certain, this is a turn if (Math.abs(sign) > 1) { - /* - * Don't show an instruction if the user is following a street, even though the street is - * bending. We should only do this, if following the street is the obvious choice. - */ + // Don't show an instruction if the user is following a street, even though the street is + // bending. We should only do this, if following the street is the obvious choice. if (InstructionsHelper.isNameSimilar(name, prevName) && outgoingEdges.outgoingEdgesAreSlowerByFactor(2)) { return Instruction.IGNORE; } @@ -378,6 +395,7 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN if (otherContinue != null) { // We are at a fork if (!InstructionsHelper.isNameSimilar(name, prevName) + || !InstructionsHelper.isNameSimilar(destinationAndRef, prevDestinationAndRef) || InstructionsHelper.isNameSimilar(otherContinue.getName(), prevName) || !outgoingEdgesAreSlower) { @@ -395,7 +413,6 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN } } - GHPoint tmpPoint = InstructionsHelper.getPointForOrientationCalculation(otherContinue, nodeAccess); double otherDelta = InstructionsHelper.calculateOrientationDelta(prevLat, prevLon, tmpPoint.getLat(), tmpPoint.getLon(), prevOrientation); @@ -409,18 +426,12 @@ private int getTurn(EdgeIteratorState edge, int baseNode, int prevNode, int adjN } else { return Instruction.KEEP_RIGHT; } - - } } - if (!outgoingEdgesAreSlower) { - if (Math.abs(delta) > .6 - || outgoingEdges.isLeavingCurrentStreet(prevName, name)) { - // Leave the current road -> create instruction - return sign; - - } + if (!outgoingEdgesAreSlower && (Math.abs(delta) > .6 || outgoingEdges.isLeavingCurrentStreet(prevName, name))) { + // Leave the current road -> create instruction + return sign; } return Instruction.IGNORE; @@ -434,8 +445,10 @@ private void updatePointsAndInstruction(EdgeIteratorState edge, PointList pl) { } double newDist = edge.getDistance(); prevInstruction.setDistance(newDist + prevInstruction.getDistance()); - // todo: why do we not account for turn times here ? - prevInstruction.setTime(weighting.calcEdgeMillis(edge, false) + prevInstruction.getTime()); + if (prevEdge != null) + prevInstruction.setTime(GHUtility.calcMillisWithTurnMillis(weighting, edge, false, prevEdge.getEdge()) + prevInstruction.getTime()); + else + prevInstruction.setTime(weighting.calcEdgeMillis(edge, false) + prevInstruction.getTime()); } } \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/InstructionsHelper.java b/core/src/main/java/com/graphhopper/routing/InstructionsHelper.java index 0cb8bf442eb..68186ff175d 100644 --- a/core/src/main/java/com/graphhopper/routing/InstructionsHelper.java +++ b/core/src/main/java/com/graphhopper/routing/InstructionsHelper.java @@ -61,9 +61,8 @@ static int calculateSign(double prevLatitude, double prevLongitude, double latit } static boolean isNameSimilar(String name1, String name2) { - // We don't want two empty names to be similar - // The idea is, if there are only a random tracks, they usually don't have names - if (name1.isEmpty() && name2.isEmpty()) + // We don't want two empty names to be similar (they usually don't have names if they are random tracks) + if (name1 == null || name2 == null || name1.isEmpty() || name2.isEmpty()) return false; return name1.equals(name2); } diff --git a/core/src/main/java/com/graphhopper/routing/OSMReaderConfig.java b/core/src/main/java/com/graphhopper/routing/OSMReaderConfig.java index 26d7a795e05..2265c41c4d5 100644 --- a/core/src/main/java/com/graphhopper/routing/OSMReaderConfig.java +++ b/core/src/main/java/com/graphhopper/routing/OSMReaderConfig.java @@ -18,15 +18,38 @@ package com.graphhopper.routing; +import java.util.ArrayList; +import java.util.List; + public class OSMReaderConfig { + private List ignoredHighways = new ArrayList<>(); private boolean parseWayNames = true; private String preferredLanguage = ""; private double maxWayPointDistance = 1; private double elevationMaxWayPointDistance = Double.MAX_VALUE; - private boolean smoothElevation = false; + private String smoothElevation = ""; + private int ramerElevationSmoothingMax = 5; private double longEdgeSamplingDistance = Double.MAX_VALUE; private int workerThreads = 2; + public List getIgnoredHighways() { + return ignoredHighways; + } + + /** + * Sets the values of the highway tag that shall be ignored when we read the OSM file. This can be used to speed up + * the import and reduce the size of the resulting routing graph. For example if one is only interested in routing + * for motorized vehicles the routing graph size can be reduced by excluding footways, cycleways, paths and/or + * tracks. This can be quite significant depending on your area. Not only are there fewer ways to be processed, but + * there are also fewer junctions, which means fewer nodes and edges. Another reason to exclude footways etc. for + * motorized vehicle routing could be preventing undesired u-turns (#1858). Similarly, one could exclude motorway, + * trunk or even primary highways for bicycle or pedestrian routing. + */ + public OSMReaderConfig setIgnoredHighways(List ignoredHighways) { + this.ignoredHighways = ignoredHighways; + return this; + } + public String getPreferredLanguage() { return preferredLanguage; } @@ -58,7 +81,7 @@ public double getMaxWayPointDistance() { } /** - * This parameter affects the routine used to simplify the edge geometries (Douglas-Peucker). Higher values mean + * This parameter affects the routine used to simplify the edge geometries (Ramer-Douglas-Peucker). Higher values mean * more details are preserved. The default is 1 (meter). Simplification can be disabled by setting it to 0. */ public OSMReaderConfig setMaxWayPointDistance(double maxWayPointDistance) { @@ -78,18 +101,27 @@ public OSMReaderConfig setElevationMaxWayPointDistance(double elevationMaxWayPoi return this; } - public boolean isSmoothElevation() { + public String getElevationSmoothing() { return smoothElevation; } /** * Enables/disables elevation smoothing */ - public OSMReaderConfig setSmoothElevation(boolean smoothElevation) { + public OSMReaderConfig setElevationSmoothing(String smoothElevation) { this.smoothElevation = smoothElevation; return this; } + public int getElevationSmoothingRamerMax() { + return ramerElevationSmoothingMax; + } + + public OSMReaderConfig setElevationSmoothingRamerMax(int max) { + this.ramerElevationSmoothingMax = max; + return this; + } + public double getLongEdgeSamplingDistance() { return longEdgeSamplingDistance; } diff --git a/core/src/main/java/com/graphhopper/routing/Path.java b/core/src/main/java/com/graphhopper/routing/Path.java index 466fe76b7bd..6a3d5cba961 100644 --- a/core/src/main/java/com/graphhopper/routing/Path.java +++ b/core/src/main/java/com/graphhopper/routing/Path.java @@ -57,6 +57,10 @@ public Path(Graph graph) { this.nodeAccess = graph.getNodeAccess(); } + public Graph getGraph() { + return graph; + } + /** * @return the description of this route alternative to make it meaningful for the user e.g. it * displays one or two main roads of the route. diff --git a/core/src/main/java/com/graphhopper/routing/ProfileResolver.java b/core/src/main/java/com/graphhopper/routing/ProfileResolver.java deleted file mode 100644 index 3785eef7e00..00000000000 --- a/core/src/main/java/com/graphhopper/routing/ProfileResolver.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.graphhopper.routing; - -import com.graphhopper.config.CHProfile; -import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.util.PMap; -import com.graphhopper.util.Parameters; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.graphhopper.routing.weighting.Weighting.INFINITE_U_TURN_COSTS; - -/** - * Before the `profile` parameter was introduced in #1958 the cost-function used for route calculations could be - * specified by setting the vehicle and weighting parameters as well as the turn_costs/edge_based flags. This class does - * the conversion between these legacy parameters and the corresponding profile. To resolve a profile we consider both - * the request parameters as well as the available LM/CH preparations. - * Note that this class is meant to be only used for the top-most web layer, while the GH engine should only deal with - * the profile parameter. - */ -public class ProfileResolver { - private final EncodingManager encodingManager; - private final List profiles; - private final List chProfiles; - private final List lmProfiles; - - public ProfileResolver(EncodingManager encodingManager, List profiles, List chProfiles, List lmProfiles) { - this.encodingManager = encodingManager; - this.profiles = profiles; - Map profilesByName = new HashMap<>(profiles.size()); - for (Profile p : profiles) { - profilesByName.put(p.getName(), p); - } - if (profilesByName.size() != profiles.size()) { - throw new IllegalStateException("Profiles must have distinct names"); - } - this.chProfiles = new ArrayList<>(); - for (CHProfile p : chProfiles) { - Profile profile = profilesByName.get(p.getProfile()); - if (profile == null) { - throw new IllegalStateException("There is no profile for CH preparation '" + p.getProfile() + "'"); - } - this.chProfiles.add(profile); - } - this.lmProfiles = new ArrayList<>(); - for (LMProfile p : lmProfiles) { - Profile profile = profilesByName.get(p.getProfile()); - if (profile == null) { - throw new IllegalStateException("There is no profile for LM preparation '" + p.getProfile() + "'"); - } - this.lmProfiles.add(profile); - } - } - - public Profile resolveProfile(PMap hints) { - boolean disableCH = hints.getBool(Parameters.CH.DISABLE, false); - boolean disableLM = hints.getBool(Parameters.Landmark.DISABLE, false); - - String vehicle = hints.getString("vehicle", "").toLowerCase(); - if (!vehicle.isEmpty() && !encodingManager.hasEncoder(vehicle)) - throw new IllegalArgumentException("Vehicle not supported: `" + vehicle + "`. Supported are: `" + encodingManager.toString() + - "`\nYou should consider using the `profile` parameter instead of specifying a vehicle." + - "\nAvailable profiles: " + getProfileNames() + - "\nTo learn more about profiles, see: docs/core/profiles.md"); - - // we select the profile based on the given request hints and the available profiles - if (!chProfiles.isEmpty() && !disableCH) { - return selectProfileCH(hints); - } else if (!lmProfiles.isEmpty() && !disableLM) { - return selectProfileLM(hints); - } else { - return selectProfileUnprepared(hints); - } - } - - /** - * @param hintsMap a map used to describe the profile that shall be selected - * @throws IllegalArgumentException if no profile supporting CH could be selected for the given parameters - */ - public Profile selectProfileCH(PMap hintsMap) { - List matchingProfiles = new ArrayList<>(); - for (Profile p : chProfiles) { - if (!chProfileMatchesHints(p, hintsMap)) - continue; - matchingProfiles.add(p); - } - - Boolean edgeBased = getEdgeBased(hintsMap); - Integer uTurnCosts = getUTurnCosts(hintsMap); - if (matchingProfiles.isEmpty()) { - throw new IllegalArgumentException("Cannot find matching profile that supports CH for your request. Please check your parameters." + - "\nYou can try disabling CH using " + Parameters.CH.DISABLE + "=true" + - "\nrequested: " + getCHRequestAsString(hintsMap, edgeBased, uTurnCosts) + "\navailable: " + chProfilesAsString(chProfiles) + - "\nYou should consider using the `profile` parameter. The available profiles are: " + getProfileNames() + - "\nTo learn more about profiles, see: docs/core/profiles.md"); - } else if (matchingProfiles.size() == 1) { - return matchingProfiles.get(0); - } else { - // special case: prefer profile with turn costs over one without turn costs if both are available and there - // aren't any other options - Profile match1 = matchingProfiles.get(0); - Profile match2 = matchingProfiles.get(1); - if (edgeBased == null && matchingProfiles.size() == 2 && - match1.getWeighting().equals(match2.getWeighting()) && - match1.getVehicle().equals(match2.getVehicle()) && - match1.isTurnCosts() != match2.isTurnCosts()) { - return match1.isTurnCosts() ? match1 : match2; - } - throw new IllegalArgumentException("There are multiple CH profiles matching your request. Use the `weighting`," + - "`vehicle`,`turn_costs` and/or `u_turn_costs` parameters to be more specific." + - "\nYou can also try disabling CH altogether using " + Parameters.CH.DISABLE + "=true" + - "\nrequested: " + getCHRequestAsString(hintsMap, edgeBased, uTurnCosts) + "\nmatched: " + chProfilesAsString(matchingProfiles) + "\navailable: " + chProfilesAsString(chProfiles) + - "\nYou should consider using the `profile` parameter. The available profiles are: " + getProfileNames() + - "\nTo learn more about profiles, see: docs/core/profiles.md"); - } - } - - protected boolean chProfileMatchesHints(Profile p, PMap hintsMap) { - Boolean edgeBased = getEdgeBased(hintsMap); - Integer uTurnCosts = getUTurnCosts(hintsMap); - return (edgeBased == null || p.isTurnCosts() == edgeBased) && - (uTurnCosts == null || uTurnCosts.equals(getUTurnCosts(p.getHints()))) && - (!hintsMap.has("weighting") || p.getWeighting().equalsIgnoreCase(hintsMap.getString("weighting", ""))) && - (!hintsMap.has("vehicle") || p.getVehicle().equalsIgnoreCase(hintsMap.getString("vehicle", ""))); - } - - public Profile selectProfileLM(PMap hintsMap) { - List matchingProfiles = new ArrayList<>(); - for (Profile p : lmProfiles) { - if (!lmProfileMatchesHints(p, hintsMap)) - continue; - matchingProfiles.add(p); - } - // Note: - // There are situations where we can use the requested encoder/weighting with an existing LM preparation, even - // though the preparation was done with a different weighting. For example this works when the new weighting - // only yields higher (but never lower) weights than the one that was used for the preparation. However, its not - // trivial to check whether or not this is the case so we do not allow this for now. - if (matchingProfiles.isEmpty()) { - throw new IllegalArgumentException("Cannot find matching LM profile for your request. Please check your parameters." + - "\nYou can try disabling LM by setting " + Parameters.Landmark.DISABLE + "=true" + - "\nrequested: " + getRequestAsString(hintsMap) + "\navailable: " + profilesAsString(lmProfiles) + - "\nYou should consider using the `profile` parameter. The available profiles are: " + getProfileNames() + - "\nTo learn more about profiles, see: docs/core/profiles.md"); - } else if (matchingProfiles.size() == 1) { - return matchingProfiles.get(0); - } else { - // special case: prefer profile with turn costs over one without turn costs if both are available and there - // aren't any other options - Profile match1 = matchingProfiles.get(0); - Profile match2 = matchingProfiles.get(1); - Boolean edgeBased = getEdgeBased(hintsMap); - if (edgeBased == null && matchingProfiles.size() == 2 && - match1.getWeighting().equals(match2.getWeighting()) && - match1.getVehicle().equals(match2.getVehicle()) && - match1.isTurnCosts() != match2.isTurnCosts()) { - return match1.isTurnCosts() ? match1 : match2; - } - throw new IllegalArgumentException("There are multiple LM profiles matching your request. Use the `weighting`," + - " `vehicle` and `turn_costs` parameters to be more specific." + - "\nYou can also try disabling LM altogether using " + Parameters.Landmark.DISABLE + "=true" + - "\nrequested: " + getRequestAsString(hintsMap) + "\nmatched: " + profilesAsString(matchingProfiles) + "\navailable: " + profilesAsString(lmProfiles) + - "\nYou should consider using the `profile` parameter. The available profiles are: " + getProfileNames() + - "\nTo learn more about profiles, see: docs/core/profiles.md"); - } - } - - protected boolean lmProfileMatchesHints(Profile p, PMap hints) { - return profileMatchesHints(p, hints); - } - - private Profile selectProfileUnprepared(PMap hints) { - List matchingProfiles = new ArrayList<>(); - for (Profile p : profiles) { - if (!profileMatchesHints(p, hints)) - continue; - matchingProfiles.add(p); - } - if (matchingProfiles.isEmpty()) { - throw new IllegalArgumentException("Cannot find matching profile for your request. Please check your parameters." + - "\nrequested: " + getRequestAsString(hints) + "\navailable: " + profilesAsString(profiles) + - "\nYou should consider using the `profile` parameter. The available profiles are: " + getProfileNames() + - "\nTo learn more about profiles, see: docs/core/profiles.md"); - } else if (matchingProfiles.size() == 1) { - return matchingProfiles.get(0); - } else { - // special case: prefer profile with turn costs over one without turn costs if both are available and there - // aren't any other options - Profile match1 = matchingProfiles.get(0); - Profile match2 = matchingProfiles.get(1); - Boolean edgeBased = getEdgeBased(hints); - if (edgeBased == null && matchingProfiles.size() == 2 && - match1.getWeighting().equals(match2.getWeighting()) && - match1.getVehicle().equals(match2.getVehicle()) && - match1.isTurnCosts() != match2.isTurnCosts()) { - return match1.isTurnCosts() ? match1 : match2; - } - throw new IllegalArgumentException("There are multiple profiles matching your request. Use the `weighting`," + - " `vehicle and `turn_costs` parameters to be more specific." + - "\nrequested: " + getRequestAsString(hints) + "\nmatched: " + profilesAsString(matchingProfiles) + "\navailable: " + profilesAsString(profiles) + - "\nYou should consider using the `profile` parameter. The available profiles are: " + getProfileNames() + - "\nTo learn more about profiles, see: docs/core/profiles.md"); - } - } - - protected boolean profileMatchesHints(Profile p, PMap hints) { - Boolean edgeBased = getEdgeBased(hints); - return (edgeBased == null || p.isTurnCosts() == edgeBased) && - (!hints.has("weighting") || p.getWeighting().equalsIgnoreCase(hints.getString("weighting", ""))) && - (!hints.has("vehicle") || p.getVehicle().equalsIgnoreCase(hints.getString("vehicle", ""))); - } - - private String getRequestAsString(PMap map) { - Boolean edgeBased = getEdgeBased(map); - return (!map.has("weighting") ? "*" : map.getString("weighting", "")) + - "|" + - (!map.has("vehicle") ? "*" : map.getString("vehicle", "")) + - "|" + - "turn_costs=" + (edgeBased != null ? edgeBased : "*"); - } - - private String getCHRequestAsString(PMap hintsMap, Boolean edgeBased, Integer uTurnCosts) { - return (!hintsMap.has("weighting") ? "*" : hintsMap.getString("weighting", "")) + - "|" + - (!hintsMap.has("vehicle") ? "*" : hintsMap.getString("vehicle", "")) + - "|" + - "turn_costs=" + (edgeBased != null ? edgeBased : "*") + - "|" + - "u_turn_costs=" + (uTurnCosts != null ? uTurnCosts : "*"); - } - - private List profilesAsString(List profiles) { - List result = new ArrayList<>(profiles.size()); - for (Profile p : profiles) { - result.add(p.getWeighting() + "|" + p.getVehicle() + "|turn_costs=" + p.isTurnCosts()); - } - return result; - } - - private List chProfilesAsString(List profiles) { - List result = new ArrayList<>(profiles.size()); - for (Profile p : profiles) { - String str = p.getWeighting() + "|" + p.getVehicle() + "|turn_costs=" + p.isTurnCosts(); - str += (p.isTurnCosts() ? "|u_turn_costs=" + p.getHints().getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS) : ""); - result.add(str); - } - return result; - } - - private List getProfileNames() { - List result = new ArrayList<>(profiles.size()); - for (Profile p : profiles) { - result.add(p.getName()); - } - return result; - } - - private Boolean getEdgeBased(PMap hintsMap) { - if (hintsMap.has(Parameters.Routing.TURN_COSTS)) - return hintsMap.getBool(Parameters.Routing.TURN_COSTS, false); - else if (hintsMap.has(Parameters.Routing.EDGE_BASED)) - return hintsMap.getBool(Parameters.Routing.EDGE_BASED, false); - else - return null; - } - - private Integer getUTurnCosts(PMap hintsMap) { - return hintsMap.has(Parameters.Routing.U_TURN_COSTS) ? hintsMap.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS) : null; - } -} diff --git a/core/src/main/java/com/graphhopper/routing/RoundTripRouting.java b/core/src/main/java/com/graphhopper/routing/RoundTripRouting.java index cf44e29b5b4..cc4e9a2cb39 100644 --- a/core/src/main/java/com/graphhopper/routing/RoundTripRouting.java +++ b/core/src/main/java/com/graphhopper/routing/RoundTripRouting.java @@ -29,6 +29,7 @@ import com.graphhopper.util.DistanceCalcEarth; import com.graphhopper.util.PMap; import com.graphhopper.util.Parameters.Algorithms.RoundTrip; +import com.graphhopper.util.PointList; import com.graphhopper.util.exceptions.PointNotFoundException; import com.graphhopper.util.shapes.GHPoint; @@ -123,6 +124,11 @@ public static Result calcPaths(List snaps, FlexiblePathCalculator pathCalc int endNode = (endSnap == start) ? endSnap.getClosestNode() : endSnap.getClosestEdge().getBaseNode(); Path path = roundTripCalculator.calcPath(startNode, endNode); + if (snapIndex == 1) { + result.wayPoints = new PointList(snaps.size(), path.graph.getNodeAccess().is3D()); + result.wayPoints.add(path.graph.getNodeAccess(), startNode); + } + result.wayPoints.add(path.graph.getNodeAccess(), endNode); result.visitedNodes += pathCalculator.getVisitedNodes(); result.paths.add(path); } @@ -132,6 +138,7 @@ public static Result calcPaths(List snaps, FlexiblePathCalculator pathCalc public static class Result { public List paths; + public PointList wayPoints; public long visitedNodes; Result(int legs) { diff --git a/core/src/main/java/com/graphhopper/routing/Router.java b/core/src/main/java/com/graphhopper/routing/Router.java index df0bed59934..a918c5a7bff 100644 --- a/core/src/main/java/com/graphhopper/routing/Router.java +++ b/core/src/main/java/com/graphhopper/routing/Router.java @@ -31,12 +31,11 @@ import com.graphhopper.routing.lm.LandmarkStorage; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.*; -import com.graphhopper.routing.weighting.BlockAreaWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomProfile; +import com.graphhopper.routing.weighting.custom.FindMinMax; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; -import com.graphhopper.storage.GraphEdgeIdFinder; import com.graphhopper.storage.RoutingCHGraph; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; @@ -65,12 +64,22 @@ public class Router { private final TranslationMap translationMap; protected final RouterConfig routerConfig; private final WeightingFactory weightingFactory; - // todo: these should not be necessary anymore as soon as GraphHopperStorage (or something that replaces) it acts - // like a 'graph database' protected final Map chGraphs; private final Map landmarks; private final boolean chEnabled; private final boolean lmEnabled; + protected final BaseGraph graph; + protected final EncodingManager encodingManager; + protected final LocationIndex locationIndex; + protected final Map profilesByName; + protected final PathDetailsBuilderFactory pathDetailsBuilderFactory; + protected final TranslationMap translationMap; + protected final RouterConfig routerConfig; + protected final WeightingFactory weightingFactory; + protected final Map chGraphs; + protected final Map landmarks; + protected final boolean chEnabled; + protected final boolean lmEnabled; public Router(BaseGraph graph, EncodingManager encodingManager, LocationIndex locationIndex, Map profilesByName, PathDetailsBuilderFactory pathDetailsBuilderFactory, @@ -105,7 +114,7 @@ public GHResponse route(GHRequest request) { checkHeadings(request); checkPointHints(request); checkCurbsides(request); - checkNoBlockAreaWithCustomModel(request); + checkNoBlockArea(request); Solver solver = createSolver(request); solver.checkRequest(); @@ -178,23 +187,40 @@ private void checkCurbsides(GHRequest request) { throw new IllegalArgumentException("If you pass " + CURBSIDE + ", you need to pass exactly one curbside for every point, empty curbsides will be ignored"); } - private void checkNoBlockAreaWithCustomModel(GHRequest request) { - if (request.getCustomModel() != null && request.getHints().has(BLOCK_AREA)) - throw new IllegalArgumentException("When using `custom_model` do not use `block_area`. Use `areas` in the custom model instead"); + private void checkNoBlockArea(GHRequest request) { + if (request.getHints().has("block_area")) + throw new IllegalArgumentException("The `block_area` parameter is no longer supported. Use a custom model with `areas` instead."); } protected Solver createSolver(GHRequest request) { final boolean disableCH = getDisableCH(request.getHints()); final boolean disableLM = getDisableLM(request.getHints()); if (chEnabled && !disableCH) { - return new CHSolver(request, profilesByName, routerConfig, encodingManager, chGraphs); + return createCHSolver(request, profilesByName, routerConfig, encodingManager, chGraphs); } else if (lmEnabled && !disableLM) { - return new LMSolver(request, profilesByName, routerConfig, encodingManager, weightingFactory, graph, locationIndex, landmarks); + return createLMSolver(request, profilesByName, routerConfig, encodingManager, weightingFactory, graph, locationIndex, landmarks); } else { - return new FlexSolver(request, profilesByName, routerConfig, encodingManager, weightingFactory, graph, locationIndex); + return createFlexSolver(request, profilesByName, routerConfig, encodingManager, weightingFactory, graph, locationIndex); } } + protected Solver createCHSolver(GHRequest request, Map profilesByName, RouterConfig routerConfig, + EncodingManager encodingManager, Map chGraphs) { + return new CHSolver(request, profilesByName, routerConfig, encodingManager, chGraphs); + } + + protected Solver createLMSolver(GHRequest request, Map profilesByName, RouterConfig routerConfig, + EncodingManager encodingManager, WeightingFactory weightingFactory, BaseGraph baseGraph, + LocationIndex locationIndex, Map landmarks) { + return new LMSolver(request, profilesByName, routerConfig, encodingManager, weightingFactory, baseGraph, locationIndex, landmarks); + } + + protected Solver createFlexSolver(GHRequest request, Map profilesByName, RouterConfig routerConfig, + EncodingManager encodingManager, WeightingFactory weightingFactory, BaseGraph baseGraph, + LocationIndex locationIndex) { + return new FlexSolver(request, profilesByName, routerConfig, encodingManager, weightingFactory, baseGraph, locationIndex); + } + protected GHResponse routeRoundTrip(GHRequest request, FlexSolver solver) { GHResponse ghRsp = new GHResponse(); StopWatch sw = new StopWatch().start(); @@ -208,7 +234,9 @@ protected GHResponse routeRoundTrip(GHRequest request, FlexSolver solver) { RoundTripRouting.Result result = RoundTripRouting.calcPaths(snaps, pathCalculator); // we merge the different legs of the roundtrip into one response path - ResponsePath responsePath = concatenatePaths(request, solver.weighting, queryGraph, result.paths, getWaypoints(snaps)); + // note that the waypoints are not just the snapped points of the snaps, as usual, because we do some kind of tweak + // to avoid 'unnecessary tails' in the roundtrip algo + ResponsePath responsePath = concatenatePaths(request, solver.weighting, queryGraph, result.paths, result.wayPoints); ghRsp.add(responsePath); ghRsp.getHints().putObject("visited_nodes.sum", result.visitedNodes); ghRsp.getHints().putObject("visited_nodes.average", (float) result.visitedNodes / (snaps.size() - 1)); @@ -283,12 +311,12 @@ private PathMerger createPathMerger(GHRequest request, Weighting weighting, Grap double wayPointMaxDistance = request.getHints().getDouble(Parameters.Routing.WAY_POINT_MAX_DISTANCE, 1d); double elevationWayPointMaxDistance = request.getHints().getDouble(ELEVATION_WAY_POINT_MAX_DISTANCE, routerConfig.getElevationWayPointMaxDistance()); - DouglasPeucker peucker = new DouglasPeucker(). + RamerDouglasPeucker peucker = new RamerDouglasPeucker(). setMaxDistance(wayPointMaxDistance). setElevationMaxDistance(elevationWayPointMaxDistance); PathMerger pathMerger = new PathMerger(graph, weighting). setCalcPoints(calcPoints). - setDouglasPeucker(peucker). + setRamerDouglasPeucker(peucker). setEnableInstructions(enableInstructions). setPathDetailsBuilders(pathDetailsBuilderFactory, request.getPathDetails()). setSimplifyResponse(routerConfig.isSimplifyResponse() && wayPointMaxDistance > 0); @@ -304,7 +332,7 @@ private ResponsePath concatenatePaths(GHRequest request, Weighting weighting, Qu } private PointList getWaypoints(List snaps) { - PointList pointList = new PointList(snaps.size(), true); + PointList pointList = new PointList(snaps.size(), graph.getNodeAccess().is3D()); for (Snap snap : snaps) { pointList.add(snap.getSnappedPoint()); } @@ -429,9 +457,6 @@ protected void checkRequest() { if (getPassThrough(request.getHints())) throw new IllegalArgumentException("The '" + Parameters.Routing.PASS_THROUGH + "' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`. See issue #1765"); - if (request.getHints().has(Parameters.Routing.BLOCK_AREA)) - throw new IllegalArgumentException("The '" + Parameters.Routing.BLOCK_AREA + "' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`."); - if (request.getCustomModel() != null) throw new IllegalArgumentException("The 'custom_model' parameter is currently not supported for speed mode, you need to disable speed mode with `ch.disable=true`."); @@ -468,14 +493,14 @@ private RoutingCHGraph getRoutingCHGraph(String profileName) { } } - private static class FlexSolver extends Solver { + public static class FlexSolver extends Solver { protected final RouterConfig routerConfig; private final WeightingFactory weightingFactory; private final BaseGraph baseGraph; private final LocationIndex locationIndex; - FlexSolver(GHRequest request, Map profilesByName, RouterConfig routerConfig, - EncodedValueLookup lookup, WeightingFactory weightingFactory, BaseGraph graph, LocationIndex locationIndex) { + protected FlexSolver(GHRequest request, Map profilesByName, RouterConfig routerConfig, + EncodedValueLookup lookup, WeightingFactory weightingFactory, BaseGraph graph, LocationIndex locationIndex) { super(request, profilesByName, routerConfig, lookup); this.routerConfig = routerConfig; this.weightingFactory = weightingFactory; @@ -493,13 +518,7 @@ protected void checkRequest() { protected Weighting createWeighting() { PMap requestHints = new PMap(request.getHints()); requestHints.putObject(CustomModel.KEY, request.getCustomModel()); - Weighting weighting = weightingFactory.createWeighting(profile, requestHints, false); - if (requestHints.has(Parameters.Routing.BLOCK_AREA)) { - GraphEdgeIdFinder.BlockArea blockArea = GraphEdgeIdFinder.createBlockArea(baseGraph, locationIndex, - request.getPoints(), requestHints, new FiniteWeightFilter(weighting)); - weighting = new BlockAreaWeighting(weighting, blockArea); - } - return weighting; + return weightingFactory.createWeighting(profile, requestHints, false); } @Override @@ -508,7 +527,7 @@ protected FlexiblePathCalculator createPathCalculator(QueryGraph queryGraph) { return new FlexiblePathCalculator(queryGraph, algorithmFactory, weighting, getAlgoOpts()); } - AlgorithmOptions getAlgoOpts() { + protected AlgorithmOptions getAlgoOpts() { AlgorithmOptions algoOpts = new AlgorithmOptions(). setAlgorithm(request.getAlgorithm()). setTraversalMode(profile.isTurnCosts() ? TraversalMode.EDGE_BASED : TraversalMode.NODE_BASED). @@ -561,6 +580,9 @@ protected FlexiblePathCalculator createPathCalculator(QueryGraph queryGraph) { throw new IllegalArgumentException("Cannot find LM preparation for the requested profile: '" + profile.getName() + "'" + "\nYou can try disabling LM using " + Parameters.Landmark.DISABLE + "=true" + "\navailable LM profiles: " + landmarks.keySet()); + if (profile instanceof CustomProfile && request.getCustomModel() != null + && !request.getHints().getBool("lm.disable", false)) + FindMinMax.checkLMConstraints(((CustomProfile) profile).getCustomModel(), request.getCustomModel(), lookup); RoutingAlgorithmFactory routingAlgorithmFactory = new LMRoutingAlgorithmFactory(landmarkStorage).setDefaultActiveLandmarks(routerConfig.getActiveLandmarkCount()); return new FlexiblePathCalculator(queryGraph, routingAlgorithmFactory, weighting, getAlgoOpts()); } diff --git a/core/src/main/java/com/graphhopper/routing/RouterConfig.java b/core/src/main/java/com/graphhopper/routing/RouterConfig.java index 0345b00ad23..398b0d05f9b 100644 --- a/core/src/main/java/com/graphhopper/routing/RouterConfig.java +++ b/core/src/main/java/com/graphhopper/routing/RouterConfig.java @@ -83,7 +83,7 @@ public boolean isSimplifyResponse() { } /** - * This method specifies if the returned path should be simplified or not, via douglas-peucker + * This method specifies if the returned path should be simplified or not, via Ramer-Douglas-Peucker * or similar algorithm. */ public void setSimplifyResponse(boolean simplifyResponse) { diff --git a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java index f6bd5185a62..d7840439b17 100644 --- a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java +++ b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactorySimple.java @@ -28,7 +28,6 @@ import com.graphhopper.util.PMap; import static com.graphhopper.util.Parameters.Algorithms.*; -import static com.graphhopper.util.Parameters.Algorithms.AltRoute.*; /** * A simple factory creating normal algorithms (RoutingAlgorithm) without preparation. @@ -62,12 +61,7 @@ public RoutingAlgorithm createAlgo(Graph g, Weighting w, AlgorithmOptions opts) ra = aStar; } else if (ALT_ROUTE.equalsIgnoreCase(algoStr)) { - AlternativeRoute altRouteAlgo = new AlternativeRoute(g, weighting, opts.getTraversalMode()); - altRouteAlgo.setMaxPaths(opts.getHints().getInt(MAX_PATHS, 2)); - altRouteAlgo.setMaxWeightFactor(opts.getHints().getDouble(MAX_WEIGHT, 1.4)); - altRouteAlgo.setMaxShareFactor(opts.getHints().getDouble(MAX_SHARE, 0.6)); - altRouteAlgo.setMinPlateauFactor(opts.getHints().getDouble("alternative_route.min_plateau_factor", 0.2)); - altRouteAlgo.setMaxExplorationFactor(opts.getHints().getDouble("alternative_route.max_exploration_factor", 1)); + AlternativeRoute altRouteAlgo = new AlternativeRoute(g, weighting, opts.getTraversalMode(), opts.getHints()); ra = altRouteAlgo; } else { diff --git a/core/src/main/java/com/graphhopper/routing/SPTEntry.java b/core/src/main/java/com/graphhopper/routing/SPTEntry.java index b4305d74d5e..2bffa3f54ac 100644 --- a/core/src/main/java/com/graphhopper/routing/SPTEntry.java +++ b/core/src/main/java/com/graphhopper/routing/SPTEntry.java @@ -30,6 +30,7 @@ public class SPTEntry implements Comparable { public int adjNode; public double weight; public SPTEntry parent; + public boolean deleted; public SPTEntry(int node, double weight) { this(EdgeIterator.NO_EDGE, node, weight, null); @@ -42,6 +43,14 @@ public SPTEntry(int edgeId, int adjNode, double weight, SPTEntry parent) { this.parent = parent; } + public void setDeleted() { + deleted = true; + } + + public boolean isDeleted() { + return deleted; + } + /** * This method returns the weight to the origin e.g. to the start for the forward SPT and to the * destination for the backward SPT. Where the variable 'weight' is used to let heap select diff --git a/core/src/main/java/com/graphhopper/routing/ViaRouting.java b/core/src/main/java/com/graphhopper/routing/ViaRouting.java index f4667b691c3..f39262cd61d 100644 --- a/core/src/main/java/com/graphhopper/routing/ViaRouting.java +++ b/core/src/main/java/com/graphhopper/routing/ViaRouting.java @@ -119,7 +119,7 @@ public static List lookup(EncodedValueLookup lookup, List points, snap = locationIndex.findClosest(point.lat, point.lon, new HeadingEdgeFilter(directedSnapFilter, headings.get(placeIndex), point)); } else if (!pointHints.isEmpty()) { snap = locationIndex.findClosest(point.lat, point.lon, new NameSimilarityEdgeFilter(strictEdgeFilter, - pointHints.get(placeIndex), point, 100)); + pointHints.get(placeIndex), point, 170)); } else if (!snapPreventions.isEmpty()) { snap = locationIndex.findClosest(point.lat, point.lon, strictEdgeFilter); } @@ -195,7 +195,7 @@ public static Result calcPaths(List points, QueryGraph queryGraph, List } result.visitedNodes += pathCalculator.getVisitedNodes(); - result.debug += "visited nodes sum: " + result.visitedNodes; + result.debug += ", visited nodes sum: " + result.visitedNodes; } return result; diff --git a/core/src/main/java/com/graphhopper/routing/ch/BridgePathFinder.java b/core/src/main/java/com/graphhopper/routing/ch/BridgePathFinder.java index 4c904571356..0b4eae548ad 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/BridgePathFinder.java +++ b/core/src/main/java/com/graphhopper/routing/ch/BridgePathFinder.java @@ -138,7 +138,7 @@ public IntObjectMap find(int startInEdgeKey, int startNode, int } } } - // We arrived at some node that is not the center node. We do not expand the search as we oare only + // We arrived at some node that is not the center node. We do not expand the search as we are only // concerned with finding bridge paths. } } diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHEntry.java b/core/src/main/java/com/graphhopper/routing/ch/CHEntry.java index 8abf3dea061..b0cbf058723 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/CHEntry.java +++ b/core/src/main/java/com/graphhopper/routing/ch/CHEntry.java @@ -18,15 +18,15 @@ package com.graphhopper.routing.ch; import com.graphhopper.routing.SPTEntry; +import com.graphhopper.storage.RoutingCHEdgeIteratorState; import com.graphhopper.util.EdgeIterator; -import com.graphhopper.util.EdgeIteratorState; public class CHEntry extends SPTEntry { /** * The id of the incoming original edge at this shortest path tree entry. For original edges this is the same * as the edge id, but for shortcuts this is the id of the last original edge of the shortcut. * - * @see EdgeIteratorState#getOrigEdgeLast() + * @see RoutingCHEdgeIteratorState#getOrigEdgeKeyLast() */ public int incEdge; diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java index b160c6c1f29..72600ee33cf 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java +++ b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationGraph.java @@ -141,7 +141,7 @@ public static TurnCostFunction buildTurnCostFunctionFromTurnCostStorage(Graph gr DoubleArrayList turnCosts = new DoubleArrayList(); // for each node we store the index of the first turn cost entry/triple in the list final int[] turnCostNodes = new int[graph.getNodes() + 1]; - TurnCostStorage.TurnRelationIterator tcIter = turnCostStorage.getAllTurnRelations(); + TurnCostStorage.Iterator tcIter = turnCostStorage.getAllTurnCosts(); int lastNode = -1; while (tcIter.next()) { int viaNode = tcIter.getViaNode(); @@ -565,7 +565,7 @@ public void setOrigEdgeCount(int origEdgeCount) { @Override public String toString() { - return currEdge == null ? "not_started" : getBaseNode() + "-" + getAdjNode(); + return currEdge == null ? "not_started" : currEdge.toString(); } private boolean nodeAisBase() { @@ -702,14 +702,12 @@ public double getDistance() { @Override public int getOrigEdgeKeyFirstAB() { - int key = prepareEdge << 1; - return nodeA > nodeB ? key + 1 : key; + return GHUtility.createEdgeKey(prepareEdge, nodeA == nodeB, false); } @Override public int getOrigEdgeKeyFirstBA() { - int key = prepareEdge << 1; - return nodeB > nodeA ? key + 1 : key; + return GHUtility.createEdgeKey(prepareEdge, nodeA == nodeB, true); } @Override @@ -1021,17 +1019,17 @@ public String toString() { * edge-based CH. In principle we could use base graph for this, but it turned out it is faster to use this * graph (because it does not need to read all the edge flags to determine the access flags). */ - private static class OrigGraph { + static class OrigGraph { // we store a list of 'edges' in the format: adjNode|edgeId|accessFlags, we use two ints for each edge private final IntArrayList adjNodes; - private final IntArrayList edgesAndFlags; + private final IntArrayList keysAndFlags; // for each node we store the index at which the edges for this node begin in the above edge list private final IntArrayList firstEdgesByNode; - private OrigGraph(IntArrayList firstEdgesByNode, IntArrayList adjNodes, IntArrayList edgesAndFlags) { + private OrigGraph(IntArrayList firstEdgesByNode, IntArrayList adjNodes, IntArrayList keysAndFlags) { this.firstEdgesByNode = firstEdgesByNode; this.adjNodes = adjNodes; - this.edgesAndFlags = edgesAndFlags; + this.keysAndFlags = keysAndFlags; } PrepareGraphOrigEdgeExplorer createOutOrigEdgeExplorer() { @@ -1045,20 +1043,20 @@ PrepareGraphOrigEdgeExplorer createInOrigEdgeExplorer() { static class Builder { private final IntArrayList fromNodes = new IntArrayList(); private final IntArrayList toNodes = new IntArrayList(); - private final IntArrayList edgesAndFlags = new IntArrayList(); + private final IntArrayList keysAndFlags = new IntArrayList(); private int maxFrom = -1; private int maxTo = -1; void addEdge(int from, int to, int edge, boolean fwd, boolean bwd) { fromNodes.add(from); toNodes.add(to); - edgesAndFlags.add(getEdgeWithFlags(edge, fwd, bwd)); + keysAndFlags.add(getKeyWithFlags(GHUtility.createEdgeKey(edge, from == to, false), fwd, bwd)); maxFrom = Math.max(maxFrom, from); maxTo = Math.max(maxTo, to); fromNodes.add(to); toNodes.add(from); - edgesAndFlags.add(getEdgeWithFlags(edge, bwd, fwd)); + keysAndFlags.add(getKeyWithFlags(GHUtility.createEdgeKey(edge, from == to, true), bwd, fwd)); maxFrom = Math.max(maxFrom, to); maxTo = Math.max(maxTo, from); } @@ -1067,21 +1065,24 @@ OrigGraph build() { int[] sortOrder = IndirectSort.mergesort(0, fromNodes.elementsCount, new IndirectComparator.AscendingIntComparator(fromNodes.buffer)); sortAndTrim(fromNodes, sortOrder); sortAndTrim(toNodes, sortOrder); - sortAndTrim(edgesAndFlags, sortOrder); - return new OrigGraph(buildFirstEdgesByNode(), toNodes, edgesAndFlags); + sortAndTrim(keysAndFlags, sortOrder); + return new OrigGraph(buildFirstEdgesByNode(), toNodes, keysAndFlags); } - private int getEdgeWithFlags(int edge, boolean fwd, boolean bwd) { - // we use only 30 bits for the edge Id and store two access flags along with the same int - if (edge >= Integer.MAX_VALUE >> 2) - throw new IllegalArgumentException("Maximum edge ID exceeded: " + Integer.MAX_VALUE); - edge <<= 1; + private static int getKeyWithFlags(int key, boolean fwd, boolean bwd) { + // we use only 30 bits for the key and store two access flags along with the same int + // this allows for a maximum of 536mio edges in base graph which is still enough for planet-wide OSM, + // but if we exceed this limit we should probably move one of the fwd/bwd bits to the nodes field or + // store the edge instead of the key as we did before #2567 (only here) + if (key > Integer.MAX_VALUE >> 1) + throw new IllegalArgumentException("Maximum edge key exceeded: " + key + ", max: " + (Integer.MAX_VALUE >> 1)); + key <<= 1; if (fwd) - edge++; - edge <<= 1; + key++; + key <<= 1; if (bwd) - edge++; - return edge; + key++; + return key; } private IntArrayList buildFirstEdgesByNode() { @@ -1151,8 +1152,7 @@ public int getAdjNode() { @Override public int getOrigEdgeKeyFirst() { - int e = graph.edgesAndFlags.get(index); - return GHUtility.createEdgeKey(node, getAdjNode(), e >> 2, false); + return graph.keysAndFlags.get(index) >>> 2; } @Override @@ -1161,12 +1161,11 @@ public int getOrigEdgeKeyLast() { } private boolean hasAccess() { - int e = graph.edgesAndFlags.get(index); - if (reverse) { + int e = graph.keysAndFlags.get(index); + if (reverse) return (e & 0b01) == 0b01; - } else { + else return (e & 0b10) == 0b10; - } } @Override diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationHandler.java b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationHandler.java index 278456caa4f..80e471ad926 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/CHPreparationHandler.java +++ b/core/src/main/java/com/graphhopper/routing/ch/CHPreparationHandler.java @@ -29,6 +29,7 @@ import java.util.*; import java.util.concurrent.Callable; import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.graphhopper.util.Helper.createFormatter; import static com.graphhopper.util.Helper.getMemInfo; @@ -98,8 +99,8 @@ public void setPreparationThreads(int preparationThreads) { public Map load(BaseGraph graph, List chConfigs) { Map loaded = Collections.synchronizedMap(new LinkedHashMap<>()); - List> callables = chConfigs.stream() - .map(c -> (Callable) () -> { + Stream> callables = chConfigs.stream() + .map(c -> () -> { CHStorage chStorage = new CHStorage(graph.getDirectory(), c.getName(), graph.getSegmentSize(), c.isEdgeBased()); if (chStorage.loadExisting()) loaded.put(c.getName(), RoutingCHGraphImpl.fromGraph(graph, chStorage, c)); @@ -109,22 +110,19 @@ public Map load(BaseGraph graph, List chConfig graph.getDirectory().remove("shortcuts_" + c.getName()); } return c.getName(); - }) - .collect(Collectors.toList()); + }); GHUtility.runConcurrently(callables, preparationThreads); return loaded; } - public Map prepare(GraphHopperStorage ghStorage, - List chConfigs, - final boolean closeEarly) { + public Map prepare(BaseGraph baseGraph, StorableProperties properties, List chConfigs, final boolean closeEarly) { if (chConfigs.isEmpty()) { LOGGER.info("There are no CHs to prepare"); return Collections.emptyMap(); } LOGGER.info("Creating CH preparations, {}", getMemInfo()); List preparations = chConfigs.stream() - .map(c -> createCHPreparation(ghStorage.getBaseGraph(), c)) + .map(c -> createCHPreparation(baseGraph, c)) .collect(Collectors.toList()); Map results = Collections.synchronizedMap(new LinkedHashMap<>()); List> callables = new ArrayList<>(preparations.size()); @@ -141,11 +139,11 @@ public Map prepare(GraphHopperStor prepare.flush(); if (closeEarly) prepare.close(); - ghStorage.getProperties().put(CH.PREPARE + "date." + name, createFormatter().format(new Date())); + properties.put(CH.PREPARE + "date." + name, createFormatter().format(new Date())); return name; }); } - GHUtility.runConcurrently(callables, preparationThreads); + GHUtility.runConcurrently(callables.stream(), preparationThreads); LOGGER.info("Finished CH preparation, {}", getMemInfo()); return results; } diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java b/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java index 8c8a798944d..a31cbc1f1ed 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java +++ b/core/src/main/java/com/graphhopper/routing/ch/CHRoutingAlgorithmFactory.java @@ -45,8 +45,8 @@ public CHRoutingAlgorithmFactory(RoutingCHGraph routingCHGraph) { this.routingCHGraph = routingCHGraph; } - public BidirRoutingAlgorithm createAlgo(PMap opts) { - BidirRoutingAlgorithm algo = routingCHGraph.isEdgeBased() + public EdgeToEdgeRoutingAlgorithm createAlgo(PMap opts) { + EdgeToEdgeRoutingAlgorithm algo = routingCHGraph.isEdgeBased() ? createAlgoEdgeBased(routingCHGraph, opts) : createAlgoNodeBased(routingCHGraph, opts); if (opts.has(MAX_VISITED_NODES)) @@ -54,7 +54,7 @@ public BidirRoutingAlgorithm createAlgo(PMap opts) { return algo; } - private BidirRoutingAlgorithm createAlgoEdgeBased(RoutingCHGraph g, PMap opts) { + private EdgeToEdgeRoutingAlgorithm createAlgoEdgeBased(RoutingCHGraph g, PMap opts) { String defaultAlgo = ASTAR_BI; String algo = opts.getString(ALGORITHM, defaultAlgo); if (Helper.isEmpty(algo)) @@ -71,7 +71,7 @@ private BidirRoutingAlgorithm createAlgoEdgeBased(RoutingCHGraph g, PMap opts) { } } - private BidirRoutingAlgorithm createAlgoNodeBased(RoutingCHGraph g, PMap opts) { + private EdgeToEdgeRoutingAlgorithm createAlgoNodeBased(RoutingCHGraph g, PMap opts) { // use dijkstra by default for node-based (its faster) String defaultAlgo = DIJKSTRA_BI; String algo = opts.getString(ALGORITHM, defaultAlgo); diff --git a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java index aa1f24ab6bc..0a24b46f91a 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java +++ b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedNodeContractor.java @@ -31,7 +31,6 @@ import java.util.Locale; import static com.graphhopper.routing.ch.CHParameters.*; -import static com.graphhopper.util.GHUtility.getEdgeFromEdgeKey; import static com.graphhopper.util.GHUtility.reverseEdgeKey; import static com.graphhopper.util.Helper.nf; @@ -250,9 +249,8 @@ private void insertOutShortcuts(int node) { PrepareEncoder.getScFwdDir(), iter.getWeight(), iter.getDistance(),iter.getTime(), iter.getSkipped1(), iter.getSkipped2(), - getEdgeFromEdgeKey(iter.getOrigEdgeKeyFirst()), - getEdgeFromEdgeKey(iter.getOrigEdgeKeyLast())); - + iter.getOrigEdgeKeyFirst(), + iter.getOrigEdgeKeyLast()); prepareGraph.setShortcutForPrepareEdge(iter.getPrepareEdge(), prepareGraph.getOriginalEdges() + shortcut); addedShortcutsCount++; } @@ -273,9 +271,8 @@ private void insertInShortcuts(int node) { PrepareEncoder.getScBwdDir(), iter.getWeight(), iter.getDistance(),iter.getTime(), iter.getSkipped1(), iter.getSkipped2(), - getEdgeFromEdgeKey(iter.getOrigEdgeKeyFirst()), - getEdgeFromEdgeKey(iter.getOrigEdgeKeyLast())); - + iter.getOrigEdgeKeyFirst(), + iter.getOrigEdgeKeyLast()); prepareGraph.setShortcutForPrepareEdge(iter.getPrepareEdge(), prepareGraph.getOriginalEdges() + shortcut); addedShortcutsCount++; diff --git a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java index f9da7866212..ccf5b2e977e 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java +++ b/core/src/main/java/com/graphhopper/routing/ch/EdgeBasedWitnessPathSearcher.java @@ -69,7 +69,7 @@ public class EdgeBasedWitnessPathSearcher { private double[] weights; private int[] parents; private int[] adjNodesAndIsPathToCenters; - private IntArrayList changedEdges; + private IntArrayList changedEdgeKeys; private IntFloatBinaryHeap dijkstraHeap; // statistics to analyze performance @@ -103,7 +103,7 @@ public void initSearch(int sourceEdgeKey, int sourceNode, int centerNode, Stats weights[sourceEdgeKey] = 0; parents[sourceEdgeKey] = -1; setAdjNodeAndPathToCenter(sourceEdgeKey, sourceNode, true); - changedEdges.add(sourceEdgeKey); + changedEdgeKeys.add(sourceEdgeKey); dijkstraHeap.insert(0, sourceEdgeKey); } @@ -160,7 +160,7 @@ public double runSearch(int targetNode, int targetEdgeKey, double acceptedWeight weights[key] = weight; parents[key] = currKey; setAdjNodeAndPathToCenter(key, iter.getAdjNode(), isPathToCenter); - changedEdges.add(key); + changedEdgeKeys.add(key); dijkstraHeap.insert(weight, key); if (iter.getAdjNode() == targetNode && (!isPathToCenter(currKey) || parents[currKey] < 0)) foundWeight = Math.min(foundWeight, weight + calcTurnWeight(key, targetNode, targetEdgeKey)); @@ -191,8 +191,8 @@ public void finishSearch() { // update stats using values of last search stats.numPolls += numPolls; stats.maxPolls = Math.max(stats.maxPolls, numPolls); - stats.numExplored += changedEdges.size(); - stats.maxExplored = Math.max(stats.maxExplored, changedEdges.size()); + stats.numExplored += changedEdgeKeys.size(); + stats.maxExplored = Math.max(stats.maxExplored, changedEdgeKeys.size()); stats.numUpdates += numUpdates; stats.maxUpdates = Math.max(stats.maxUpdates, numUpdates); reset(); @@ -217,7 +217,7 @@ public void close() { weights = null; parents = null; adjNodesAndIsPathToCenters = null; - changedEdges.release(); + changedEdgeKeys.release(); dijkstraHeap = null; } @@ -234,7 +234,7 @@ private void initStorage(int numEntries) { } private void initCollections() { - changedEdges = new IntArrayList(1000); + changedEdgeKeys = new IntArrayList(1000); dijkstraHeap = new IntFloatBinaryHeap(1000); } @@ -245,9 +245,9 @@ private void reset() { } private void resetShortestPathTree() { - for (int i = 0; i < changedEdges.size(); ++i) - resetEntry(changedEdges.get(i)); - changedEdges.elementsCount = 0; + for (int i = 0; i < changedEdgeKeys.size(); ++i) + resetEntry(changedEdgeKeys.get(i)); + changedEdgeKeys.elementsCount = 0; dijkstraHeap.clear(); } diff --git a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java index c5d799eb082..5ae5a388097 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java +++ b/core/src/main/java/com/graphhopper/routing/ch/PrepareContractionHierarchies.java @@ -124,7 +124,7 @@ public Result doWork() { throw new IllegalStateException("Call doWork only once!"); prepared = true; if (!graph.isFrozen()) { - throw new IllegalStateException("Given GraphHopperStorage has not been frozen yet"); + throw new IllegalStateException("Given BaseGraph has not been frozen yet"); } if (chStore.getShortcuts() > 0) { throw new IllegalStateException("Given CHStore already contains shortcuts"); diff --git a/core/src/main/java/com/graphhopper/routing/ch/ShortcutUnpacker.java b/core/src/main/java/com/graphhopper/routing/ch/ShortcutUnpacker.java index a32e0d07881..d55ac53a31a 100644 --- a/core/src/main/java/com/graphhopper/routing/ch/ShortcutUnpacker.java +++ b/core/src/main/java/com/graphhopper/routing/ch/ShortcutUnpacker.java @@ -3,6 +3,7 @@ import com.graphhopper.storage.RoutingCHEdgeIteratorState; import com.graphhopper.storage.RoutingCHGraph; import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.GHUtility; import java.util.Locale; @@ -108,11 +109,10 @@ private void expandSkippedEdgesNodeBased(int skippedEdge1, int skippedEdge2, int private int getOppositeEdge(RoutingCHEdgeIteratorState edgeState, int adjNode) { assert edgeState.getBaseNode() == adjNode || edgeState.getAdjNode() == adjNode : "adjNode " + adjNode + " must be one of adj/base of edgeState: " + edgeState; - // since the first/last orig edge is not stateful (just like skipped1/2) we have to find out which one + // since the first/last orig edge key is not stateful (just like skipped1/2) we have to find out which one // is attached to adjNode, similar as we do for skipped1/2. - return graph.getBaseGraph().isAdjacentToNode(edgeState.getOrigEdgeLast(), adjNode) - ? edgeState.getOrigEdgeFirst() - : edgeState.getOrigEdgeLast(); + boolean adjacentToNode = graph.getBaseGraph().isAdjacentToNode(GHUtility.getEdgeFromEdgeKey(edgeState.getOrigEdgeKeyLast()), adjNode); + return GHUtility.getEdgeFromEdgeKey(adjacentToNode ? edgeState.getOrigEdgeKeyFirst() : edgeState.getOrigEdgeKeyLast()); } private RoutingCHEdgeIteratorState getEdge(int edgeId, int adjNode) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/ArrayEdgeIntAccess.java b/core/src/main/java/com/graphhopper/routing/ev/ArrayEdgeIntAccess.java new file mode 100644 index 00000000000..6de724fd94b --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/ArrayEdgeIntAccess.java @@ -0,0 +1,45 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +import com.carrotsearch.hppc.IntArrayList; + +public class ArrayEdgeIntAccess implements EdgeIntAccess { + private final int intsPerEdge; + private final IntArrayList arr = new IntArrayList(); + + public ArrayEdgeIntAccess(int intsPerEdge) { + this.intsPerEdge = intsPerEdge; + } + + @Override + public int getInt(int edgeId, int index) { + int arrIndex = edgeId * intsPerEdge + index; + return arrIndex >= arr.size() ? 0 : arr.get(arrIndex); + } + + @Override + public void setInt(int edgeId, int index, int value) { + int arrIndex = edgeId * intsPerEdge + index; + if (arrIndex >= arr.size()) + arr.resize(arrIndex + 1); + arr.set(arrIndex, value); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/ev/AverageSlope.java b/core/src/main/java/com/graphhopper/routing/ev/AverageSlope.java new file mode 100644 index 00000000000..236f4630cf1 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/AverageSlope.java @@ -0,0 +1,13 @@ +package com.graphhopper.routing.ev; + +/** + * Average elevation. Will be negated in reverse direction. + */ +public class AverageSlope { + public static final String KEY = "average_slope"; + + public static DecimalEncodedValue create() { + return new DecimalEncodedValueImpl(KEY, 5, 0, 1, + true, false, false); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/BooleanEncodedValue.java b/core/src/main/java/com/graphhopper/routing/ev/BooleanEncodedValue.java index b1636aaf6d1..24f7e888c35 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/BooleanEncodedValue.java +++ b/core/src/main/java/com/graphhopper/routing/ev/BooleanEncodedValue.java @@ -1,12 +1,10 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; - /** * This interface defines access to an edge property of type boolean. The default value is false. */ public interface BooleanEncodedValue extends EncodedValue { - void setBool(boolean reverse, IntsRef ref, boolean value); + void setBool(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, boolean value); - boolean getBool(boolean reverse, IntsRef ref); + boolean getBool(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess); } diff --git a/core/src/main/java/com/graphhopper/routing/ev/Country.java b/core/src/main/java/com/graphhopper/routing/ev/Country.java index 0569995f858..c618412cba9 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/Country.java +++ b/core/src/main/java/com/graphhopper/routing/ev/Country.java @@ -21,7 +21,7 @@ * The enum constants correspond to the the ISO3166-1:alpha3 code of the corresponding country */ public enum Country { - // values were taken from `countries.geojson`: + // ISO3166-1:alpha3(name:en, ISO3166-1:alpha2) MISSING("missing", "--"), AFG("Afghanistan", "AF"), @@ -242,28 +242,28 @@ public enum Country { ZMB("Zambia", "ZM"), ZWE("Zimbabwe", "ZW"); - public static final String KEY = "country"; + public static final String KEY = "country", ISO_ALPHA3 = "ISO3166-1:alpha3"; - private final String name; - private final String twoLetterCode; + private final String countryName; + private final String alpha2; - Country(String name, String twoLetterCode) { - this.name = name; - this.twoLetterCode = twoLetterCode; + Country(String countryName, String alpha2) { + this.countryName = countryName; + this.alpha2 = alpha2; } /** - * @return the name:en field of this country + * @return the name of this country. Avoids clash with name() method of this enum. */ - public String getName() { - return name; + public String getCountryName() { + return countryName; } /** * @return the ISO3166-1:alpha2 code of this country */ - public String getTwoLetterCode() { - return twoLetterCode; + public String getAlpha2() { + return alpha2; } public static EnumEncodedValue create() { diff --git a/core/src/main/java/com/graphhopper/routing/ev/Crossing.java b/core/src/main/java/com/graphhopper/routing/ev/Crossing.java new file mode 100644 index 00000000000..e5ddba670a5 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/Crossing.java @@ -0,0 +1,30 @@ +package com.graphhopper.routing.ev; + +import com.graphhopper.util.Helper; + +public enum Crossing { + MISSING, // no information + RAILWAY_BARRIER, // railway crossing with barrier + RAILWAY, // railway crossing with road + TRAFFIC_SIGNALS, // with light signals + UNCONTROLLED, // with crosswalk, without traffic lights + MARKED, // with crosswalk, with or without traffic lights + UNMARKED, // without markings or traffic lights + NO; // crossing is impossible or illegal + public static final String KEY = "crossing"; + + @Override + public String toString() { + return Helper.toLowerCase(name()); + } + + public static Crossing find(String name) { + if (name == null) + return MISSING; + try { + return Crossing.valueOf(Helper.toUpperCase(name)); + } catch (IllegalArgumentException ex) { + return MISSING; + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/Curvature.java b/core/src/main/java/com/graphhopper/routing/ev/Curvature.java new file mode 100644 index 00000000000..1d3c57c26f4 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/Curvature.java @@ -0,0 +1,11 @@ +package com.graphhopper.routing.ev; + +public class Curvature { + public static final String KEY = "curvature"; + + public static DecimalEncodedValue create() { + // for now save a bit: ignore all too low values and set them to the minimum value instead + return new DecimalEncodedValueImpl(KEY, 4, 0.25, 0.05, + false, false, false); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/DecimalEncodedValue.java b/core/src/main/java/com/graphhopper/routing/ev/DecimalEncodedValue.java index 7eac24edaa9..87d8367965c 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/DecimalEncodedValue.java +++ b/core/src/main/java/com/graphhopper/routing/ev/DecimalEncodedValue.java @@ -1,7 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; - /** * This class defines how and where to store an unsigned decimal value. It is important to note that: * 1. the range of the number is highly limited (unlike the Java 32bit float or 64bit double values) @@ -15,16 +13,26 @@ public interface DecimalEncodedValue extends EncodedValue { /** * This method stores the specified double value (rounding with a previously defined factor) into the IntsRef. * - * @see #getMaxDecimal() + * @see #getMaxStorableDecimal() */ - void setDecimal(boolean reverse, IntsRef ref, double value); + void setDecimal(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, double value); - double getDecimal(boolean reverse, IntsRef ref); + double getDecimal(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess); /** * The maximum double value this EncodedValue accepts for setDecimal without throwing an exception. */ - double getMaxDecimal(); + double getMaxStorableDecimal(); + + /** + * The minimum double value this EncodedValue accepts for setDecimal without throwing an exception. + */ + double getMinStorableDecimal(); + + /** + * @see IntEncodedValue#getMaxOrMaxStorableInt() + */ + double getMaxOrMaxStorableDecimal(); /** * @return the smallest decimal value that is larger or equal to the given value and that can be stored exactly, @@ -35,4 +43,5 @@ public interface DecimalEncodedValue extends EncodedValue { double getNextStorableValue(double value); double getSmallestNonZeroValue(); + } diff --git a/core/src/main/java/com/graphhopper/routing/ev/DecimalEncodedValueImpl.java b/core/src/main/java/com/graphhopper/routing/ev/DecimalEncodedValueImpl.java index 450afe71eff..ee9b5b3aa31 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/DecimalEncodedValueImpl.java +++ b/core/src/main/java/com/graphhopper/routing/ev/DecimalEncodedValueImpl.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.graphhopper.storage.IntsRef; /** * This class holds a signed decimal value and stores it as an integer value via a conversion factor and a certain @@ -27,105 +26,98 @@ */ public final class DecimalEncodedValueImpl extends IntEncodedValueImpl implements DecimalEncodedValue { private final double factor; - private final boolean defaultIsInfinity; private final boolean useMaximumAsInfinity; /** - * @see #DecimalEncodedValueImpl(String, int, double, double, boolean, boolean, boolean, boolean) + * @see #DecimalEncodedValueImpl(String, int, double, double, boolean, boolean, boolean) */ public DecimalEncodedValueImpl(String name, int bits, double factor, boolean storeTwoDirections) { - this(name, bits, factor, false, storeTwoDirections); - } - - /** - * @see #DecimalEncodedValueImpl(String, int, double, double, boolean, boolean, boolean, boolean) - */ - public DecimalEncodedValueImpl(String name, int bits, double factor, boolean defaultIsInfinity, boolean storeTwoDirections) { - this(name, bits, 0, factor, defaultIsInfinity, false, storeTwoDirections, false); + this(name, bits, 0, factor, false, storeTwoDirections, false); } /** * @param name the key to identify this EncodedValue * @param bits the bits that should be reserved for storing the integer value. This determines the * maximum value. - * @param minValue the minimum value. Use e.g. 0 if no negative values are needed. + * @param minStorableValue the minimum storable value. Use e.g. 0 if no negative values are needed. * @param factor the precision factor, i.e. store = (int) Math.round(value / factor) - * @param defaultIsInfinity true if default should be Double.Infinity. False if 0 should be default. * @param negateReverseDirection true if the reverse direction should be always negative of the forward direction. * This is used to reduce space and store the value only once. * @param storeTwoDirections true if forward and backward direction of the edge should get two independent values. * @param useMaximumAsInfinity true if the maximum value should be treated as Double.Infinity */ - public DecimalEncodedValueImpl(String name, int bits, double minValue, double factor, boolean defaultIsInfinity, + public DecimalEncodedValueImpl(String name, int bits, double minStorableValue, double factor, boolean negateReverseDirection, boolean storeTwoDirections, boolean useMaximumAsInfinity) { - super(name, bits, (int) Math.round(minValue / factor), negateReverseDirection, storeTwoDirections); - if (!negateReverseDirection && super.minValue * factor != minValue) - throw new IllegalArgumentException("minValue " + minValue + " is not a multiple of the specified factor " + factor); + super(name, bits, (int) Math.round(minStorableValue / factor), negateReverseDirection, storeTwoDirections); + if (!negateReverseDirection && super.minStorableValue * factor != minStorableValue) + throw new IllegalArgumentException("minStorableValue " + minStorableValue + " is not a multiple of the specified factor " + + factor + " (" + super.minStorableValue * factor + ")"); this.factor = factor; - this.defaultIsInfinity = defaultIsInfinity; this.useMaximumAsInfinity = useMaximumAsInfinity; - if (useMaximumAsInfinity && defaultIsInfinity) - throw new IllegalArgumentException("defaultIsInfinity and useMaximumAsInfinity cannot be both true"); - if (defaultIsInfinity && minValue < 0) - throw new IllegalArgumentException("defaultIsInfinity cannot be true when minValue is negative"); } @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) DecimalEncodedValueImpl(@JsonProperty("name") String name, @JsonProperty("bits") int bits, - @JsonProperty("min_value") int minValue, + @JsonProperty("min_storable_value") int minStorableValue, + @JsonProperty("max_storable_value") int maxStorableValue, @JsonProperty("max_value") int maxValue, @JsonProperty("negate_reverse_direction") boolean negateReverseDirection, @JsonProperty("store_two_directions") boolean storeTwoDirections, + @JsonProperty("fwd_data_index") int fwdDataIndex, + @JsonProperty("bwd_data_index") int bwdDataIndex, + @JsonProperty("fwd_shift") int fwdShift, + @JsonProperty("bwd_shift") int bwdShift, + @JsonProperty("fwd_mask") int fwdMask, + @JsonProperty("bwd_mask") int bwdMask, @JsonProperty("factor") double factor, - @JsonProperty("default_is_infinity") boolean defaultIsInfinity, @JsonProperty("use_maximum_as_infinity") boolean useMaximumAsInfinity) { // we need this constructor for Jackson - super(name, bits, minValue, maxValue, negateReverseDirection, storeTwoDirections); + super(name, bits, minStorableValue, maxStorableValue, maxValue, negateReverseDirection, storeTwoDirections, fwdDataIndex, bwdDataIndex, fwdShift, bwdShift, fwdMask, bwdMask); this.factor = factor; - this.defaultIsInfinity = defaultIsInfinity; this.useMaximumAsInfinity = useMaximumAsInfinity; } @Override - public void setDecimal(boolean reverse, IntsRef ref, double value) { + public void setDecimal(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, double value) { if (!isInitialized()) - throw new IllegalStateException("Call init before usage for EncodedValue " + toString()); - if (Double.isInfinite(value)) { - if (useMaximumAsInfinity) { - super.setInt(reverse, ref, maxValue); + throw new IllegalStateException("Call init before using EncodedValue " + getName()); + if (useMaximumAsInfinity) { + if (Double.isInfinite(value)) { + super.setInt(reverse, edgeId, edgeIntAccess, maxStorableValue); return; - } else if (defaultIsInfinity) { - super.setInt(reverse, ref, 0); + } else if (value >= maxStorableValue * factor) { // equality is important as maxStorableValue is reserved for infinity + super.uncheckedSet(reverse, edgeId, edgeIntAccess, maxStorableValue - 1); return; } + } else if (Double.isInfinite(value)) throw new IllegalArgumentException("Value cannot be infinite if useMaximumAsInfinity is false"); - } + if (Double.isNaN(value)) throw new IllegalArgumentException("NaN value for " + getName() + " not allowed!"); value /= factor; - if (value > maxValue) - throw new IllegalArgumentException(getName() + " value too large for encoding: " + value + ", maxValue:" + maxValue + ", factor: " + factor); - if (value < minValue) - throw new IllegalArgumentException(getName() + " value too small for encoding " + value + ", minValue:" + minValue + ", factor: " + factor); + if (value > maxStorableValue) + throw new IllegalArgumentException(getName() + " value too large for encoding: " + value + ", maxValue:" + maxStorableValue + ", factor: " + factor); + if (value < minStorableValue) + throw new IllegalArgumentException(getName() + " value too small for encoding " + value + ", minValue:" + minStorableValue + ", factor: " + factor); - super.uncheckedSet(reverse, ref, (int) Math.round(value)); + super.uncheckedSet(reverse, edgeId, edgeIntAccess, (int) Math.round(value)); } @Override - public double getDecimal(boolean reverse, IntsRef ref) { - int value = getInt(reverse, ref); - if (useMaximumAsInfinity && value == maxValue || defaultIsInfinity && value == 0) + public double getDecimal(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess) { + int value = getInt(reverse, edgeId, edgeIntAccess); + if (useMaximumAsInfinity && value == maxStorableValue) return Double.POSITIVE_INFINITY; return value * factor; } @Override public double getNextStorableValue(double value) { - if (!useMaximumAsInfinity && value > getMaxDecimal()) - throw new IllegalArgumentException(getName() + ": There is no next storable value for " + value + ". max:" + getMaxDecimal()); - else if (useMaximumAsInfinity && value > getMaxDecimal()) + if (!useMaximumAsInfinity && value > getMaxStorableDecimal()) + throw new IllegalArgumentException(getName() + ": There is no next storable value for " + value + ". max:" + getMaxStorableDecimal()); + else if (useMaximumAsInfinity && value > (maxStorableValue - 1) * factor) return Double.POSITIVE_INFINITY; else return (factor * (int) Math.ceil(value / factor)); @@ -133,29 +125,26 @@ else if (useMaximumAsInfinity && value > getMaxDecimal()) @Override public double getSmallestNonZeroValue() { - if (minValue != 0 || negateReverseDirection) + if (minStorableValue != 0 || negateReverseDirection) throw new IllegalStateException("getting the smallest non-zero value is not possible if minValue!=0 or negateReverseDirection"); return factor; } @Override - public double getMaxDecimal() { - return maxValue * factor; + public double getMaxStorableDecimal() { + if (useMaximumAsInfinity) return Double.POSITIVE_INFINITY; + return maxStorableValue * factor; } @Override - public boolean equals(Object o) { - if (!super.equals(o)) return false; - DecimalEncodedValueImpl that = (DecimalEncodedValueImpl) o; - return Double.compare(that.factor, factor) == 0 && useMaximumAsInfinity == that.useMaximumAsInfinity - && defaultIsInfinity == that.defaultIsInfinity; + public double getMinStorableDecimal() { + return minStorableValue * factor; } @Override - public int getVersion() { - int version = 31 * super.getVersion() + staticHashCode(factor); - if (useMaximumAsInfinity) return 31 * version + 13; - if (defaultIsInfinity) return 31 * version + 17; - return version; + public double getMaxOrMaxStorableDecimal() { + int maxOrMaxStorable = getMaxOrMaxStorableInt(); + if (useMaximumAsInfinity && maxOrMaxStorable == maxStorableValue) return Double.POSITIVE_INFINITY; + return maxOrMaxStorable * factor; } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/DefaultEncodedValueFactory.java b/core/src/main/java/com/graphhopper/routing/ev/DefaultEncodedValueFactory.java index c9c53e17595..dec908b6b35 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/DefaultEncodedValueFactory.java +++ b/core/src/main/java/com/graphhopper/routing/ev/DefaultEncodedValueFactory.java @@ -17,78 +17,80 @@ */ package com.graphhopper.routing.ev; -import com.graphhopper.util.Helper; +import com.graphhopper.util.PMap; public class DefaultEncodedValueFactory implements EncodedValueFactory { - @Override - public EncodedValue create(String string) { - if (Helper.isEmpty(string)) - throw new IllegalArgumentException("No string provided to load EncodedValue"); - - final EncodedValue enc; - String name = string.split("\\|")[0]; - if (name.isEmpty()) - throw new IllegalArgumentException("To load EncodedValue a name is required. " + string); + @Override + public EncodedValue create(String name, PMap properties) { if (Roundabout.KEY.equals(name)) { - enc = Roundabout.create(); - } else if ("car_access".equals(name)) { - enc = new SimpleBooleanEncodedValue("car_access", true); - } else if ("bike_access".equals(name)) { - enc = new SimpleBooleanEncodedValue("bike_access", true); + return Roundabout.create(); } else if (GetOffBike.KEY.equals(name)) { - enc = GetOffBike.create(); + return GetOffBike.create(); } else if (RoadClass.KEY.equals(name)) { - enc = new EnumEncodedValue<>(RoadClass.KEY, RoadClass.class); + return RoadClass.create(); } else if (RoadClassLink.KEY.equals(name)) { - enc = new SimpleBooleanEncodedValue(RoadClassLink.KEY); + return RoadClassLink.create(); } else if (RoadEnvironment.KEY.equals(name)) { - enc = new EnumEncodedValue<>(RoadEnvironment.KEY, RoadEnvironment.class); + return RoadEnvironment.create(); } else if (RoadAccess.KEY.equals(name)) { - enc = new EnumEncodedValue<>(RoadAccess.KEY, RoadAccess.class); + return RoadAccess.create(); } else if (MaxSpeed.KEY.equals(name)) { - enc = MaxSpeed.create(); + return MaxSpeed.create(); } else if (MaxWeight.KEY.equals(name)) { - enc = MaxWeight.create(); + return MaxWeight.create(); } else if (MaxHeight.KEY.equals(name)) { - enc = MaxHeight.create(); + return MaxHeight.create(); } else if (MaxWidth.KEY.equals(name)) { - enc = MaxWidth.create(); + return MaxWidth.create(); } else if (MaxAxleLoad.KEY.equals(name)) { - enc = MaxAxleLoad.create(); + return MaxAxleLoad.create(); } else if (MaxLength.KEY.equals(name)) { - enc = MaxLength.create(); + return MaxLength.create(); + } else if (Hgv.KEY.equals(name)) { + return Hgv.create(); } else if (Surface.KEY.equals(name)) { - enc = new EnumEncodedValue<>(Surface.KEY, Surface.class); + return Surface.create(); } else if (Smoothness.KEY.equals(name)) { - enc = new EnumEncodedValue<>(Smoothness.KEY, Smoothness.class); + return Smoothness.create(); } else if (Toll.KEY.equals(name)) { - enc = new EnumEncodedValue<>(Toll.KEY, Toll.class); + return Toll.create(); } else if (TrackType.KEY.equals(name)) { - enc = new EnumEncodedValue<>(TrackType.KEY, TrackType.class); + return TrackType.create(); } else if (BikeNetwork.KEY.equals(name) || FootNetwork.KEY.equals(name)) { - enc = new EnumEncodedValue<>(name, RouteNetwork.class); + return RouteNetwork.create(name); } else if (Hazmat.KEY.equals(name)) { - enc = new EnumEncodedValue<>(Hazmat.KEY, Hazmat.class); + return Hazmat.create(); } else if (HazmatTunnel.KEY.equals(name)) { - enc = new EnumEncodedValue<>(HazmatTunnel.KEY, HazmatTunnel.class); + return HazmatTunnel.create(); } else if (HazmatWater.KEY.equals(name)) { - enc = new EnumEncodedValue<>(HazmatWater.KEY, HazmatWater.class); + return HazmatWater.create(); } else if (Lanes.KEY.equals(name)) { - enc = Lanes.create(); + return Lanes.create(); + } else if (Footway.KEY.equals(name)) { + return Footway.create(); + } else if (OSMWayID.KEY.equals(name)) { + return OSMWayID.create(); } else if (MtbRating.KEY.equals(name)) { - enc = MtbRating.create(); + return MtbRating.create(); } else if (HikeRating.KEY.equals(name)) { - enc = HikeRating.create(); + return HikeRating.create(); } else if (HorseRating.KEY.equals(name)) { - enc = HorseRating.create(); + return HorseRating.create(); } else if (Country.KEY.equals(name)) { - enc = Country.create(); + return Country.create(); } else if (name.endsWith(Subnetwork.key(""))) { - enc = new SimpleBooleanEncodedValue(name); + return Subnetwork.create(name); + } else if (MaxSlope.KEY.equals(name)) { + return MaxSlope.create(); + } else if (AverageSlope.KEY.equals(name)) { + return AverageSlope.create(); + } else if (Curvature.KEY.equals(name)) { + return Curvature.create(); + } else if (Crossing.KEY.equals(name)) { + return new EnumEncodedValue<>(Crossing.KEY, Crossing.class); } else { throw new IllegalArgumentException("DefaultEncodedValueFactory cannot find EncodedValue " + name); } - return enc; } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/EdgeIntAccess.java b/core/src/main/java/com/graphhopper/routing/ev/EdgeIntAccess.java new file mode 100644 index 00000000000..3bae8da4b80 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/EdgeIntAccess.java @@ -0,0 +1,31 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +public interface EdgeIntAccess { + /** + * Gets the int value at the given index for the given edgeId + */ + int getInt(int edgeId, int index); + + /** + * Sets the int value at the given index for the given edgeId + */ + void setInt(int edgeId, int index, int value); +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/EncodedValue.java b/core/src/main/java/com/graphhopper/routing/ev/EncodedValue.java index 063747c6652..6f8040b00af 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/EncodedValue.java +++ b/core/src/main/java/com/graphhopper/routing/ev/EncodedValue.java @@ -41,13 +41,6 @@ public interface EncodedValue { */ String getName(); - /** - * The return value represents the state of this EncodedValue and it can be assumed that two JVMs return the - * same version when the EncodedValue has the same state unlike the hashCode method. Same version ensures - * compatibility when reading values. - */ - int getVersion(); - /** * @return true if this EncodedValue can store a different value for its reverse direction */ @@ -61,8 +54,6 @@ class InitializerConfig { /** * This method determines a space of the specified bits and sets shift and dataIndex accordingly - * - * @param usedBits */ void next(int usedBits) { shift = nextShift; diff --git a/core/src/main/java/com/graphhopper/routing/ev/EncodedValueFactory.java b/core/src/main/java/com/graphhopper/routing/ev/EncodedValueFactory.java index 032408cc80c..c8fcbcc993e 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/EncodedValueFactory.java +++ b/core/src/main/java/com/graphhopper/routing/ev/EncodedValueFactory.java @@ -17,10 +17,8 @@ */ package com.graphhopper.routing.ev; +import com.graphhopper.util.PMap; + public interface EncodedValueFactory { - /** - * This method assumes a string value with the key of an EncodedValue like "road_class" and returns an instance - * of it. - */ - EncodedValue create(String encodedValueString); + EncodedValue create(String name, PMap properties); } diff --git a/core/src/main/java/com/graphhopper/routing/ev/EncodedValueSerializer.java b/core/src/main/java/com/graphhopper/routing/ev/EncodedValueSerializer.java index d34c4086803..82d5bd6185e 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/EncodedValueSerializer.java +++ b/core/src/main/java/com/graphhopper/routing/ev/EncodedValueSerializer.java @@ -24,7 +24,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.node.ObjectNode; public class EncodedValueSerializer { private final static ObjectMapper MAPPER = new ObjectMapper(); @@ -38,7 +37,6 @@ public class EncodedValueSerializer { public static String serializeEncodedValue(EncodedValue encodedValue) { try { JsonNode tree = MAPPER.valueToTree(encodedValue); - ((ObjectNode) tree).put("version", encodedValue.getVersion()); return MAPPER.writeValueAsString(tree); } catch (JsonProcessingException e) { throw new IllegalStateException("Could not serialize encoded value: " + encodedValue + ", error: " + e.getMessage()); @@ -48,15 +46,10 @@ public static String serializeEncodedValue(EncodedValue encodedValue) { public static EncodedValue deserializeEncodedValue(String serializedEncodedValue) { try { JsonNode jsonNode = MAPPER.readTree(serializedEncodedValue); - int storedVersion = jsonNode.get("version").asInt(); - ((ObjectNode) jsonNode).remove("version"); - EncodedValue encodedValue = MAPPER.treeToValue(jsonNode, EncodedValue.class); - if (storedVersion != encodedValue.getVersion()) - throw new IllegalStateException("Version does not match. Cannot properly read encoded value: " + encodedValue.getName() + ". " + - "You need to use the same version of GraphHopper you used to import the data"); - return encodedValue; + return MAPPER.treeToValue(jsonNode, EncodedValue.class); } catch (JsonProcessingException e) { throw new IllegalStateException("Could not deserialize encoded value: " + serializedEncodedValue + ", error: " + e.getMessage()); } } + } diff --git a/core/src/main/java/com/graphhopper/routing/ev/EnumEncodedValue.java b/core/src/main/java/com/graphhopper/routing/ev/EnumEncodedValue.java index f5d8bc19a04..b1097acc1a2 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/EnumEncodedValue.java +++ b/core/src/main/java/com/graphhopper/routing/ev/EnumEncodedValue.java @@ -20,9 +20,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.graphhopper.storage.IntsRef; - -import java.util.Arrays; /** * This class allows to store distinct values via an enum. I.e. it stores just the indices @@ -46,13 +43,20 @@ public EnumEncodedValue(String name, Class enumType, boolean storeTwoDirectio @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) EnumEncodedValue(@JsonProperty("name") String name, @JsonProperty("bits") int bits, - @JsonProperty("min_value") int minValue, + @JsonProperty("min_storable_value") int minStorableValue, + @JsonProperty("max_storable_value") int maxStorableValue, @JsonProperty("max_value") int maxValue, @JsonProperty("negate_reverse_direction") boolean negateReverseDirection, @JsonProperty("store_two_directions") boolean storeTwoDirections, + @JsonProperty("fwd_data_index") int fwdDataIndex, + @JsonProperty("bwd_data_index") int bwdDataIndex, + @JsonProperty("fwd_shift") int fwdShift, + @JsonProperty("bwd_shift") int bwdShift, + @JsonProperty("fwd_mask") int fwdMask, + @JsonProperty("bwd_mask") int bwdMask, @JsonProperty("enum_type") Class enumType) { // we need this constructor for Jackson - super(name, bits, minValue, maxValue, negateReverseDirection, storeTwoDirections); + super(name, bits, minStorableValue, maxStorableValue, maxValue, negateReverseDirection, storeTwoDirections, fwdDataIndex, bwdDataIndex, fwdShift, bwdShift, fwdMask, bwdMask); this.enumType = enumType; arr = enumType.getEnumConstants(); } @@ -61,25 +65,14 @@ public E[] getValues() { return arr; } - public final void setEnum(boolean reverse, IntsRef ref, E value) { + public final void setEnum(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, E value) { int intValue = value.ordinal(); - super.setInt(reverse, ref, intValue); + super.setInt(reverse, edgeId, edgeIntAccess, intValue); } - public final E getEnum(boolean reverse, IntsRef ref) { - int value = super.getInt(reverse, ref); + public final E getEnum(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess) { + int value = super.getInt(reverse, edgeId, edgeIntAccess); return arr[value]; } - @Override - public boolean equals(Object o) { - if (!super.equals(o)) return false; - EnumEncodedValue that = (EnumEncodedValue) o; - return Arrays.equals(arr, that.arr); - } - - @Override - public int getVersion() { - return 31 * super.getVersion() + staticHashCode(arr); - } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/Footway.java b/core/src/main/java/com/graphhopper/routing/ev/Footway.java new file mode 100644 index 00000000000..bddfb017721 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/Footway.java @@ -0,0 +1,52 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +public enum Footway { + MISSING("missing"), SIDEWALK("sidewalk"), CROSSING("crossing"), ACCESS_AISLE("access_aisle"), + LINK("link"), TRAFFIC_ISLAND("traffic_island"), ALLEY("alley"); + + public static final String KEY = "footway"; + + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, Footway.class); + } + + private final String name; + + Footway(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public static Footway find(String name) { + if (name == null || name.isEmpty()) + return MISSING; + + for (Footway footway : values()) + if (footway.name().equalsIgnoreCase(name)) + return footway; + + return MISSING; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/GetOffBike.java b/core/src/main/java/com/graphhopper/routing/ev/GetOffBike.java index ae29ef92354..457964e0af7 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/GetOffBike.java +++ b/core/src/main/java/com/graphhopper/routing/ev/GetOffBike.java @@ -4,6 +4,6 @@ public class GetOffBike { public static final String KEY = "get_off_bike"; public static BooleanEncodedValue create() { - return new SimpleBooleanEncodedValue(KEY, false); + return new SimpleBooleanEncodedValue(KEY, true); } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/Hazmat.java b/core/src/main/java/com/graphhopper/routing/ev/Hazmat.java index 0bf56e29758..9fef11dfb66 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/Hazmat.java +++ b/core/src/main/java/com/graphhopper/routing/ev/Hazmat.java @@ -9,6 +9,10 @@ public enum Hazmat { public static final String KEY = "hazmat"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, Hazmat.class); + } + private final String name; Hazmat(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/HazmatTunnel.java b/core/src/main/java/com/graphhopper/routing/ev/HazmatTunnel.java index 2422be9379a..202e5e25746 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/HazmatTunnel.java +++ b/core/src/main/java/com/graphhopper/routing/ev/HazmatTunnel.java @@ -3,23 +3,37 @@ /** * Defines the degree of restriction for the transport of hazardous goods through tunnels.
* If not tagged it will be {@link #A} - * + * * @see Hazmat Tunnel restrictions */ public enum HazmatTunnel { - /** driving with any dangerous goods allowed */ + /** + * driving with any dangerous goods allowed + */ A("A"), - /** no goods with very large explosion range */ + /** + * no goods with very large explosion range + */ B("B"), - /** no goods with large explosion or poisoning range */ + /** + * no goods with large explosion or poisoning range + */ C("C"), - /** no goods which threaten a large explosion, poisoning or fire */ + /** + * no goods which threaten a large explosion, poisoning or fire + */ D("D"), - /** forbids all dangerous goods except: UN 2919,3291, 3331, 3359, 3373 */ + /** + * forbids all dangerous goods except: UN 2919,3291, 3331, 3359, 3373 + */ E("E"); public static final String KEY = "hazmat_tunnel"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, HazmatTunnel.class); + } + private final String name; HazmatTunnel(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/HazmatWater.java b/core/src/main/java/com/graphhopper/routing/ev/HazmatWater.java index d128147147c..d91cf98d915 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/HazmatWater.java +++ b/core/src/main/java/com/graphhopper/routing/ev/HazmatWater.java @@ -9,6 +9,10 @@ public enum HazmatWater { public static final String KEY = "hazmat_water"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, HazmatWater.class); + } + private final String name; HazmatWater(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/Hgv.java b/core/src/main/java/com/graphhopper/routing/ev/Hgv.java new file mode 100644 index 00000000000..7284f6acb33 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/Hgv.java @@ -0,0 +1,57 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.ev; + +import com.graphhopper.util.Helper; + +/** + * This enum defines the road access of an edge. Most edges are accessible from everyone and so the default value is + * YES. But some have restrictions like "accessible only for customers" or when delivering. Unknown tags will get the + * value OTHER. The NO value does not permit any access. + */ +public enum Hgv { + MISSING("missing"), YES("yes"), DESIGNATED("designated"), DESTINATION("destination"), + DELIVERY("delivery"), DISCOURAGED("discouraged"), AGRICULTURAL("agricultural"), NO("no"); + + public static final String KEY = "hgv"; + + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(Hgv.KEY, Hgv.class); + } + + private final String name; + + Hgv(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public static Hgv find(String name) { + if (name == null) + return MISSING; + try { + return Hgv.valueOf(Helper.toUpperCase(name)); + } catch (IllegalArgumentException ex) { + return MISSING; + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/IntEncodedValue.java b/core/src/main/java/com/graphhopper/routing/ev/IntEncodedValue.java index 8c0731e96eb..c6a0875d796 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/IntEncodedValue.java +++ b/core/src/main/java/com/graphhopper/routing/ev/IntEncodedValue.java @@ -1,7 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; - /** * This class defines how and where to store an unsigned integer. It is important to note that: 1. the range of the * integer is highly limited (unlike the Java 32bit integer values) so that the storable part of it fits into the @@ -14,17 +12,30 @@ public interface IntEncodedValue extends EncodedValue { /** * This method restores the integer value from the specified 'flags' taken from the storage. */ - int getInt(boolean reverse, IntsRef ref); + int getInt(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess); /** * This method stores the specified integer value in the specified IntsRef. */ - void setInt(boolean reverse, IntsRef ref, int value); + void setInt(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, int value); + + /** + * The maximum int value this EncodedValue accepts for setInt without throwing an exception. + */ + int getMaxStorableInt(); + + /** + * The minimum int value this EncodedValue accepts for setInt without throwing an exception. + */ + int getMinStorableInt(); /** - * The int value this EncodedValue accepts for setInt without throwing an exception. + * Returns the maximum value set using this encoded value or the physical storage limit if no value has been set + * at all yet. Note that even when some values were set this is not equal to the global maximum across all values in + * the graph if values are set multiple times for the same edge and they are decreasing. However, the returned value + * will always be equal to or larger than the global maximum. */ - int getMaxInt(); + int getMaxOrMaxStorableInt(); /** * @return true if this EncodedValue can store a different value for its reverse direction diff --git a/core/src/main/java/com/graphhopper/routing/ev/IntEncodedValueImpl.java b/core/src/main/java/com/graphhopper/routing/ev/IntEncodedValueImpl.java index 09f7114fb39..d2111737b47 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/IntEncodedValueImpl.java +++ b/core/src/main/java/com/graphhopper/routing/ev/IntEncodedValueImpl.java @@ -18,15 +18,9 @@ package com.graphhopper.routing.ev; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.Helper; -import java.util.Arrays; -import java.util.Collection; -import java.util.Objects; +import javax.lang.model.SourceVersion; /** * Implementation of the IntEncodedValue via a certain number of bits (that determines the maximum value) and @@ -42,25 +36,18 @@ public class IntEncodedValueImpl implements IntEncodedValue { private final boolean storeTwoDirections; final int bits; final boolean negateReverseDirection; - final int minValue; - final int maxValue; + final int minStorableValue; + final int maxStorableValue; + int maxValue; - // the following fields will be set by the init() method and we do not store them on disk, because they will be - // set again when we create the EncodingManager /** * There are multiple int values possible per edge. Here we specify the index into this integer array. */ - @JsonIgnore private int fwdDataIndex; - @JsonIgnore private int bwdDataIndex; - @JsonIgnore int fwdShift = -1; - @JsonIgnore int bwdShift = -1; - @JsonIgnore int fwdMask; - @JsonIgnore int bwdMask; /** @@ -76,29 +63,32 @@ public IntEncodedValueImpl(String name, int bits, boolean storeTwoDirections) { * @param name the key to identify this EncodedValue * @param bits the bits that should be reserved for storing the value. This determines the * maximum value. - * @param minValue the minimum value. Use e.g. 0 if no negative values are needed. + * @param minStorableValue the minimum value. Use e.g. 0 if no negative values are needed. * @param negateReverseDirection true if the reverse direction should be always negative of the forward direction. * This is used to reduce space and store the value only once. If this option is used * you cannot use storeTwoDirections or a minValue different to 0. * @param storeTwoDirections true if forward and backward direction of the edge should get two independent values. */ - public IntEncodedValueImpl(String name, int bits, int minValue, boolean negateReverseDirection, boolean storeTwoDirections) { - if (!EncodingManager.isValidEncodedValue(name)) + public IntEncodedValueImpl(String name, int bits, int minStorableValue, boolean negateReverseDirection, boolean storeTwoDirections) { + if (!isValidEncodedValue(name)) throw new IllegalArgumentException("EncodedValue name wasn't valid: " + name + ". Use lower case letters, underscore and numbers only."); if (bits <= 0) throw new IllegalArgumentException(name + ": bits cannot be zero or negative"); if (bits > 31) throw new IllegalArgumentException(name + ": at the moment the number of reserved bits cannot be more than 31"); - if (negateReverseDirection && (minValue != 0 || storeTwoDirections)) + if (negateReverseDirection && (minStorableValue != 0 || storeTwoDirections)) throw new IllegalArgumentException(name + ": negating value for reverse direction only works for minValue == 0 " + - "and !storeTwoDirections but was minValue=" + minValue + ", storeTwoDirections=" + storeTwoDirections); - + "and !storeTwoDirections but was minValue=" + minStorableValue + ", storeTwoDirections=" + storeTwoDirections); this.name = name; this.storeTwoDirections = storeTwoDirections; int max = (1 << bits) - 1; // negateReverseDirection: store the negative value only once, but for that we need the same range as maxValue for negative values - this.minValue = negateReverseDirection ? -max : minValue; - this.maxValue = max + minValue; + this.minStorableValue = negateReverseDirection ? -max : minStorableValue; + this.maxStorableValue = max + minStorableValue; + if (minStorableValue == Integer.MIN_VALUE) + // we do not allow this because we use this value to represent maxValue = untouched, i.e. no value has been set yet + throw new IllegalArgumentException(Integer.MIN_VALUE + " is not allowed for minValue"); + this.maxValue = Integer.MIN_VALUE; // negateReverseDirection: we need twice the integer range, i.e. 1 more bit this.bits = negateReverseDirection ? bits + 1 : bits; this.negateReverseDirection = negateReverseDirection; @@ -107,17 +97,32 @@ public IntEncodedValueImpl(String name, int bits, int minValue, boolean negateRe @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) IntEncodedValueImpl(@JsonProperty("name") String name, @JsonProperty("bits") int bits, - @JsonProperty("min_value") int minValue, + @JsonProperty("min_storable_value") int minStorableValue, + @JsonProperty("max_storable_value") int maxStorableValue, @JsonProperty("max_value") int maxValue, @JsonProperty("negate_reverse_direction") boolean negateReverseDirection, - @JsonProperty("store_two_directions") boolean storeTwoDirections) { + @JsonProperty("store_two_directions") boolean storeTwoDirections, + @JsonProperty("fwd_data_index") int fwdDataIndex, + @JsonProperty("bwd_data_index") int bwdDataIndex, + @JsonProperty("fwd_shift") int fwdShift, + @JsonProperty("bwd_shift") int bwdShift, + @JsonProperty("fwd_mask") int fwdMask, + @JsonProperty("bwd_mask") int bwdMask + ) { // we need this constructor for Jackson this.name = name; this.storeTwoDirections = storeTwoDirections; this.bits = bits; this.negateReverseDirection = negateReverseDirection; - this.minValue = minValue; + this.minStorableValue = minStorableValue; + this.maxStorableValue = maxStorableValue; this.maxValue = maxValue; + this.fwdDataIndex = fwdDataIndex; + this.bwdDataIndex = bwdDataIndex; + this.fwdShift = fwdShift; + this.bwdShift = bwdShift; + this.fwdMask = fwdMask; + this.bwdMask = bwdMask; } @Override @@ -144,21 +149,21 @@ boolean isInitialized() { } @Override - public final void setInt(boolean reverse, IntsRef ref, int value) { + public final void setInt(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, int value) { checkValue(value); - uncheckedSet(reverse, ref, value); + uncheckedSet(reverse, edgeId, edgeIntAccess, value); } private void checkValue(int value) { if (!isInitialized()) throw new IllegalStateException("EncodedValue " + getName() + " not initialized"); - if (value > maxValue) - throw new IllegalArgumentException(name + " value too large for encoding: " + value + ", maxValue:" + maxValue); - if (value < minValue) - throw new IllegalArgumentException(name + " value too small for encoding " + value + ", minValue:" + minValue); + if (value > maxStorableValue) + throw new IllegalArgumentException(name + " value too large for encoding: " + value + ", maxValue:" + maxStorableValue); + if (value < minStorableValue) + throw new IllegalArgumentException(name + " value too small for encoding " + value + ", minValue:" + minStorableValue); } - final void uncheckedSet(boolean reverse, IntsRef ref, int value) { + final void uncheckedSet(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, int value) { if (negateReverseDirection) { if (reverse) { reverse = false; @@ -167,145 +172,86 @@ final void uncheckedSet(boolean reverse, IntsRef ref, int value) { } else if (reverse && !storeTwoDirections) throw new IllegalArgumentException(getName() + ": value for reverse direction would overwrite forward direction. Enable storeTwoDirections for this EncodedValue or don't use setReverse"); - value -= minValue; + maxValue = Math.max(maxValue, value); + + value -= minStorableValue; if (reverse) { - int flags = ref.ints[bwdDataIndex + ref.offset]; + int flags = edgeIntAccess.getInt(edgeId, bwdDataIndex); // clear value bits flags &= ~bwdMask; - ref.ints[bwdDataIndex + ref.offset] = flags | (value << bwdShift); + edgeIntAccess.setInt(edgeId, bwdDataIndex, flags | (value << bwdShift)); } else { - int flags = ref.ints[fwdDataIndex + ref.offset]; + int flags = edgeIntAccess.getInt(edgeId, fwdDataIndex); flags &= ~fwdMask; - ref.ints[fwdDataIndex + ref.offset] = flags | (value << fwdShift); + edgeIntAccess.setInt(edgeId, fwdDataIndex, flags | (value << fwdShift)); } } @Override - public final int getInt(boolean reverse, IntsRef ref) { + public final int getInt(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess) { int flags; // if we do not store both directions ignore reverse == true for convenient reading if (storeTwoDirections && reverse) { - flags = ref.ints[bwdDataIndex + ref.offset]; - return minValue + (flags & bwdMask) >>> bwdShift; + flags = edgeIntAccess.getInt(edgeId, bwdDataIndex); + return minStorableValue + ((flags & bwdMask) >>> bwdShift); } else { - flags = ref.ints[fwdDataIndex + ref.offset]; + flags = edgeIntAccess.getInt(edgeId, fwdDataIndex); if (negateReverseDirection && reverse) - return -(minValue + (flags & fwdMask) >>> fwdShift); - return minValue + (flags & fwdMask) >>> fwdShift; + return -(minStorableValue + ((flags & fwdMask) >>> fwdShift)); + return minStorableValue + ((flags & fwdMask) >>> fwdShift); } } @Override - public int getMaxInt() { - return maxValue; + public int getMaxStorableInt() { + return maxStorableValue; } @Override - public final boolean isStoreTwoDirections() { - return storeTwoDirections; + public int getMinStorableInt() { + return minStorableValue; } @Override - public final String getName() { - return name; + public int getMaxOrMaxStorableInt() { + return maxValue == Integer.MIN_VALUE ? getMaxStorableInt() : maxValue; } @Override - public final String toString() { - return getName() + "|version=" + getVersion() + "|bits=" + bits + "|min_value=" + minValue - + "|negate_reverse_direction" + negateReverseDirection + "|index=" + fwdDataIndex - + "|shift=" + fwdShift + "|store_both_directions=" + storeTwoDirections; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - IntEncodedValueImpl that = (IntEncodedValueImpl) o; - return fwdDataIndex == that.fwdDataIndex && - bwdDataIndex == that.bwdDataIndex && - bits == that.bits && - maxValue == that.maxValue && - minValue == that.minValue && - fwdShift == that.fwdShift && - bwdShift == that.bwdShift && - fwdMask == that.fwdMask && - bwdMask == that.bwdMask && - negateReverseDirection == that.negateReverseDirection && - storeTwoDirections == that.storeTwoDirections && - Objects.equals(name, that.name); + public final boolean isStoreTwoDirections() { + return storeTwoDirections; } @Override - public final int hashCode() { - return getVersion(); + public final String getName() { + return name; } @Override - public int getVersion() { - int val = Helper.staticHashCode(name); - val = 31 * val + (storeTwoDirections ? 1231 : 1237); - val = 31 * val + (negateReverseDirection ? 13 : 17); - return staticHashCode(val, fwdDataIndex, bwdDataIndex, bits, minValue, maxValue, fwdShift, bwdShift, fwdMask, bwdMask); - } - - /** - * Produces a static hashcode for an integer arrays that is platform independent and still compatible to the default - * of openjdk. - * - * @see Arrays#hashCode(int[]) - */ - static int staticHashCode(int... vals) { - if (vals == null) - return 0; - int len = vals.length; - int val = 1; - for (int idx = 0; idx < len; ++idx) { - val = 31 * val + vals[idx]; - } - - return val; + public final String toString() { + return getName(); } - /** - * Produces a static hashcode for an Enum arrays that is platform independent and still compatible to the default - * of openjdk. - */ - static int staticHashCode(Enum... vals) { - if (vals == null) - return 0; - int len = vals.length; - int val = 1; - for (int idx = 0; idx < len; ++idx) { - val = 31 * val + vals[idx].ordinal(); - } + static boolean isValidEncodedValue(String name) { + if (name.length() < 2 || name.startsWith("in_") || !isLowerLetter(name.charAt(0)) || SourceVersion.isKeyword(name)) + return false; - return val; - } - - /** - * Produces a static hashcode for a collection of Strings that is platform independent and still compatible to the default - * of openjdk. - */ - static int staticHashCode(Collection vals) { - if (vals == null) - return 0; - int val = 1; - for (String str : vals) { - val = 31 * val + Helper.staticHashCode(str); + int underscoreCount = 0; + for (int i = 1; i < name.length(); i++) { + char c = name.charAt(i); + if (c == '_') { + if (underscoreCount > 0) return false; + underscoreCount++; + } else if (!isLowerLetter(c) && !Character.isDigit(c)) { + return false; + } else { + underscoreCount = 0; + } } - - return val; + return true; } - /** - * Produces a static hashcode for an integer arrays that is platform independent and still compatible to the default - * of openjdk - * - * @see Double#hashCode - */ - static int staticHashCode(double val) { - long var2 = Double.doubleToLongBits(val); - return (int) (var2 ^ var2 >>> 32); + private static boolean isLowerLetter(char c) { + return c >= 'a' && c <= 'z'; } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/IntsRefEdgeIntAccess.java b/core/src/main/java/com/graphhopper/routing/ev/IntsRefEdgeIntAccess.java new file mode 100644 index 00000000000..1b964092fc5 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/IntsRefEdgeIntAccess.java @@ -0,0 +1,39 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +import com.graphhopper.storage.IntsRef; + +public class IntsRefEdgeIntAccess implements EdgeIntAccess { + private final IntsRef intsRef; + + public IntsRefEdgeIntAccess(IntsRef intsRef) { + this.intsRef = intsRef; + } + + @Override + public int getInt(int edgeId, int index) { + return intsRef.ints[index]; + } + + @Override + public void setInt(int edgeId, int index, int value) { + intsRef.ints[index] = value; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/MaxAxleLoad.java b/core/src/main/java/com/graphhopper/routing/ev/MaxAxleLoad.java index 6279960a8d4..5b7e3631dcb 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/MaxAxleLoad.java +++ b/core/src/main/java/com/graphhopper/routing/ev/MaxAxleLoad.java @@ -29,6 +29,6 @@ public class MaxAxleLoad { * it was done with the MappedDecimalEncodedValue still handling (or rounding) of unknown values is unclear. */ public static DecimalEncodedValue create() { - return new DecimalEncodedValueImpl(KEY, 7, 0.5, true, false); + return new DecimalEncodedValueImpl(KEY, 7, 0, 0.5, false, false, true); } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/MaxHeight.java b/core/src/main/java/com/graphhopper/routing/ev/MaxHeight.java index 7fe15711e6a..6ac26936238 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/MaxHeight.java +++ b/core/src/main/java/com/graphhopper/routing/ev/MaxHeight.java @@ -32,6 +32,6 @@ public class MaxHeight { * it is assumed to use the maximum value. */ public static DecimalEncodedValue create() { - return new DecimalEncodedValueImpl(KEY, 7, 0.1, true, false); + return new DecimalEncodedValueImpl(KEY, 7, 0, 0.1, false, false, true); } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/MaxLength.java b/core/src/main/java/com/graphhopper/routing/ev/MaxLength.java index 03b52d8176b..5848d239ea2 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/MaxLength.java +++ b/core/src/main/java/com/graphhopper/routing/ev/MaxLength.java @@ -32,6 +32,6 @@ public class MaxLength { * between the maximum and infinity it is assumed to use the maximum value. */ public static DecimalEncodedValue create() { - return new DecimalEncodedValueImpl(KEY, 7, 0.1, true, false); + return new DecimalEncodedValueImpl(KEY, 7, 0, 0.1, false, false, true); } } \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/ev/MaxSlope.java b/core/src/main/java/com/graphhopper/routing/ev/MaxSlope.java new file mode 100644 index 00000000000..26282b45704 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/MaxSlope.java @@ -0,0 +1,12 @@ +package com.graphhopper.routing.ev; + +/** + * Maximum elevation change in m/100m. + */ +public class MaxSlope { + public static final String KEY = "max_slope"; + + public static DecimalEncodedValue create() { + return new DecimalEncodedValueImpl(KEY, 5, 1, false); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/MaxSpeed.java b/core/src/main/java/com/graphhopper/routing/ev/MaxSpeed.java index ec2e970385e..f0343e412ac 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/MaxSpeed.java +++ b/core/src/main/java/com/graphhopper/routing/ev/MaxSpeed.java @@ -35,6 +35,6 @@ public class MaxSpeed { public static final double UNSET_SPEED = Double.POSITIVE_INFINITY; public static DecimalEncodedValue create() { - return new DecimalEncodedValueImpl(KEY, 5, 5, true, true); + return new DecimalEncodedValueImpl(KEY, 5, 0, 5, false, true, true); } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/MaxWeight.java b/core/src/main/java/com/graphhopper/routing/ev/MaxWeight.java index 9a77e191e20..5f0cca0a205 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/MaxWeight.java +++ b/core/src/main/java/com/graphhopper/routing/ev/MaxWeight.java @@ -33,6 +33,6 @@ public class MaxWeight { * it was done with the MappedDecimalEncodedValue still handling (or rounding) of unknown values is unclear. */ public static DecimalEncodedValue create() { - return new DecimalEncodedValueImpl(KEY, 8, 0.1, true, false); + return new DecimalEncodedValueImpl(KEY, 8, 0, 0.1, false, false, true); } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/MaxWidth.java b/core/src/main/java/com/graphhopper/routing/ev/MaxWidth.java index 172c94a2829..a7a16d86256 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/MaxWidth.java +++ b/core/src/main/java/com/graphhopper/routing/ev/MaxWidth.java @@ -32,6 +32,6 @@ public class MaxWidth { * it is assumed to use the maximum value. */ public static DecimalEncodedValue create() { - return new DecimalEncodedValueImpl(KEY, 7, 0.1, true, false); + return new DecimalEncodedValueImpl(KEY, 7, 0, 0.1, false, false, true); } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/OSMWayID.java b/core/src/main/java/com/graphhopper/routing/ev/OSMWayID.java new file mode 100644 index 00000000000..3d7038312a0 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/OSMWayID.java @@ -0,0 +1,27 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +public class OSMWayID { + public static final String KEY = "osm_way_id"; + + public static IntEncodedValue create() { + return new IntEncodedValueImpl(KEY, 31, false); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/RoadAccess.java b/core/src/main/java/com/graphhopper/routing/ev/RoadAccess.java index 32593f78df8..ebcaf71a4a8 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/RoadAccess.java +++ b/core/src/main/java/com/graphhopper/routing/ev/RoadAccess.java @@ -31,6 +31,10 @@ public enum RoadAccess { public static final String KEY = "road_access"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(RoadAccess.KEY, RoadAccess.class); + } + private final String name; RoadAccess(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/RoadClass.java b/core/src/main/java/com/graphhopper/routing/ev/RoadClass.java index 827b0ceaf45..88f56c9b5f9 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/RoadClass.java +++ b/core/src/main/java/com/graphhopper/routing/ev/RoadClass.java @@ -32,6 +32,10 @@ public enum RoadClass { public static final String KEY = "road_class"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(RoadClass.KEY, RoadClass.class); + } + private final String name; RoadClass(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/RoadClassLink.java b/core/src/main/java/com/graphhopper/routing/ev/RoadClassLink.java index 0ae5be8d1d6..17acde4a7bd 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/RoadClassLink.java +++ b/core/src/main/java/com/graphhopper/routing/ev/RoadClassLink.java @@ -19,4 +19,8 @@ public class RoadClassLink { public static final String KEY = "road_class_link"; + + public static BooleanEncodedValue create() { + return new SimpleBooleanEncodedValue(KEY); + } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/RoadEnvironment.java b/core/src/main/java/com/graphhopper/routing/ev/RoadEnvironment.java index a743414bdda..6c60eff74b2 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/RoadEnvironment.java +++ b/core/src/main/java/com/graphhopper/routing/ev/RoadEnvironment.java @@ -29,6 +29,10 @@ public enum RoadEnvironment { public static final String KEY = "road_environment"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(RoadEnvironment.KEY, RoadEnvironment.class); + } + private final String name; RoadEnvironment(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/RouteNetwork.java b/core/src/main/java/com/graphhopper/routing/ev/RouteNetwork.java index 9c2a7fd6331..2fc739e9c86 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/RouteNetwork.java +++ b/core/src/main/java/com/graphhopper/routing/ev/RouteNetwork.java @@ -32,6 +32,10 @@ public static String key(String prefix) { return prefix + "_network"; } + public static EnumEncodedValue create(String name) { + return new EnumEncodedValue<>(name, RouteNetwork.class); + } + private final String name; RouteNetwork(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/SimpleBooleanEncodedValue.java b/core/src/main/java/com/graphhopper/routing/ev/SimpleBooleanEncodedValue.java index 095f1507cb8..eec079cdcab 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/SimpleBooleanEncodedValue.java +++ b/core/src/main/java/com/graphhopper/routing/ev/SimpleBooleanEncodedValue.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.graphhopper.storage.IntsRef; /** * This class implements a simple boolean storage via an UnsignedIntEncodedValue with 1 bit. @@ -37,22 +36,29 @@ public SimpleBooleanEncodedValue(String name, boolean storeBothDirections) { SimpleBooleanEncodedValue( @JsonProperty("name") String name, @JsonProperty("bits") int bits, - @JsonProperty("minValue") int minValue, - @JsonProperty("maxValue") int maxValue, - @JsonProperty("negateReverseDirection") boolean negateReverseDirection, - @JsonProperty("storeTwoDirections") boolean storeTwoDirections + @JsonProperty("min_storable_value") int minStorableValue, + @JsonProperty("max_storable_value") int maxStorableValue, + @JsonProperty("max_value") int maxValue, + @JsonProperty("negate_reverse_direction") boolean negateReverseDirection, + @JsonProperty("store_two_directions") boolean storeTwoDirections, + @JsonProperty("fwd_data_index") int fwdDataIndex, + @JsonProperty("bwd_data_index") int bwdDataIndex, + @JsonProperty("fwd_shift") int fwdShift, + @JsonProperty("bwd_shift") int bwdShift, + @JsonProperty("fwd_mask") int fwdMask, + @JsonProperty("bwd_mask") int bwdMask ) { // we need this constructor for Jackson - super(name, bits, minValue, maxValue, negateReverseDirection, storeTwoDirections); + super(name, bits, minStorableValue, maxStorableValue, maxValue, negateReverseDirection, storeTwoDirections, fwdDataIndex, bwdDataIndex, fwdShift, bwdShift, fwdMask, bwdMask); } @Override - public final void setBool(boolean reverse, IntsRef ref, boolean value) { - setInt(reverse, ref, value ? 1 : 0); + public final void setBool(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, boolean value) { + setInt(reverse, edgeId, edgeIntAccess, value ? 1 : 0); } @Override - public final boolean getBool(boolean reverse, IntsRef ref) { - return getInt(reverse, ref) == 1; + public final boolean getBool(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess) { + return getInt(reverse, edgeId, edgeIntAccess) == 1; } } diff --git a/core/src/main/java/com/graphhopper/routing/ev/Smoothness.java b/core/src/main/java/com/graphhopper/routing/ev/Smoothness.java index 7ca39c05df5..29e41106503 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/Smoothness.java +++ b/core/src/main/java/com/graphhopper/routing/ev/Smoothness.java @@ -32,6 +32,10 @@ public enum Smoothness { public static final String KEY = "smoothness"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, Smoothness.class); + } + private final String name; Smoothness(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/StringEncodedValue.java b/core/src/main/java/com/graphhopper/routing/ev/StringEncodedValue.java index c70c207fa37..cf687207c17 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/StringEncodedValue.java +++ b/core/src/main/java/com/graphhopper/routing/ev/StringEncodedValue.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.graphhopper.storage.IntsRef; import java.util.*; @@ -50,15 +49,22 @@ public StringEncodedValue(String name, int bits, List values, boolean st StringEncodedValue( @JsonProperty("name") String name, @JsonProperty("bits") int bits, - @JsonProperty("min_value") int minValue, + @JsonProperty("min_storable_value") int minStorableValue, + @JsonProperty("max_storable_value") int maxStorableValue, @JsonProperty("max_value") int maxValue, @JsonProperty("negate_reverse_direction") boolean negateReverseDirection, @JsonProperty("store_two_directions") boolean storeTwoDirections, + @JsonProperty("fwd_data_index") int fwdDataIndex, + @JsonProperty("bwd_data_index") int bwdDataIndex, + @JsonProperty("fwd_shift") int fwdShift, + @JsonProperty("bwd_shift") int bwdShift, + @JsonProperty("fwd_mask") int fwdMask, + @JsonProperty("bwd_mask") int bwdMask, @JsonProperty("max_values") int maxValues, @JsonProperty("values") List values, @JsonProperty("index_map") HashMap indexMap) { // we need this constructor for Jackson - super(name, bits, minValue, maxValue, negateReverseDirection, storeTwoDirections); + super(name, bits, minStorableValue, maxStorableValue, maxValue, negateReverseDirection, storeTwoDirections, fwdDataIndex, bwdDataIndex, fwdShift, bwdShift, fwdMask, bwdMask); if (values.size() > maxValues) throw new IllegalArgumentException("Number of values is higher than the maximum value count: " + values.size() + " > " + maxValues); @@ -67,9 +73,9 @@ public StringEncodedValue(String name, int bits, List values, boolean st this.indexMap = indexMap; } - public final void setString(boolean reverse, IntsRef ref, String value) { + public final void setString(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, String value) { if (value == null) { - super.setInt(reverse, ref, 0); + super.setInt(reverse, edgeId, edgeIntAccess, 0); return; } int index = indexMap.getOrDefault(value, 0); @@ -81,11 +87,11 @@ public final void setString(boolean reverse, IntsRef ref, String value) { index = values.size(); indexMap.put(value, index); } - super.setInt(reverse, ref, index); + super.setInt(reverse, edgeId, edgeIntAccess, index); } - public final String getString(boolean reverse, IntsRef ref) { - int value = super.getInt(reverse, ref); + public final String getString(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess) { + int value = super.getInt(reverse, edgeId, edgeIntAccess); if (value == 0) { return null; } @@ -115,23 +121,4 @@ public List getValues() { return Collections.unmodifiableList(values); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof StringEncodedValue)) { - return false; - } - StringEncodedValue other = (StringEncodedValue) obj; - if (this.bits != other.bits) { - return false; - } - return Objects.equals(values, other.values); - } - - @Override - public int getVersion() { - return 31 * super.getVersion() + staticHashCode(values); - } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/ev/Surface.java b/core/src/main/java/com/graphhopper/routing/ev/Surface.java index 2252aa998f1..a76be0c4b03 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/Surface.java +++ b/core/src/main/java/com/graphhopper/routing/ev/Surface.java @@ -34,6 +34,10 @@ public enum Surface { public static final String KEY = "surface"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, Surface.class); + } + private final String name; Surface(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/Toll.java b/core/src/main/java/com/graphhopper/routing/ev/Toll.java index a01bc548063..4a4eff5c6ad 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/Toll.java +++ b/core/src/main/java/com/graphhopper/routing/ev/Toll.java @@ -26,6 +26,10 @@ public enum Toll { public static final String KEY = "toll"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, Toll.class); + } + private final String name; Toll(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/TrackType.java b/core/src/main/java/com/graphhopper/routing/ev/TrackType.java index 1fb61cff2ae..bc178222923 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/TrackType.java +++ b/core/src/main/java/com/graphhopper/routing/ev/TrackType.java @@ -32,6 +32,10 @@ public enum TrackType { public static final String KEY = "track_type"; + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, TrackType.class); + } + private final String name; TrackType(String name) { diff --git a/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java b/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java index e480f2624b7..49df15bd817 100644 --- a/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java +++ b/core/src/main/java/com/graphhopper/routing/ev/TurnCost.java @@ -16,7 +16,7 @@ public static String key(String prefix) { */ public static DecimalEncodedValue create(String name, int maxTurnCosts) { int turnBits = Helper.countBitValue(maxTurnCosts); - return new DecimalEncodedValueImpl(key(name), turnBits, 0, 1, false, false, false, true); + return new DecimalEncodedValueImpl(key(name), turnBits, 0, 1, false, false, true); } public static IntsRef createFlags() { diff --git a/core/src/main/java/com/graphhopper/routing/ev/UrbanDensity.java b/core/src/main/java/com/graphhopper/routing/ev/UrbanDensity.java new file mode 100644 index 00000000000..b5500259eeb --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/UrbanDensity.java @@ -0,0 +1,40 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +public enum UrbanDensity { + RURAL("rural"), RESIDENTIAL("residential"), CITY("city"); + + public static EnumEncodedValue create() { + return new EnumEncodedValue<>(KEY, UrbanDensity.class); + } + + public static final String KEY = "urban_density"; + + private final String name; + + UrbanDensity(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/VehicleAccess.java b/core/src/main/java/com/graphhopper/routing/ev/VehicleAccess.java new file mode 100644 index 00000000000..a7f76d9cbab --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/VehicleAccess.java @@ -0,0 +1,32 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +import com.graphhopper.routing.util.EncodingManager; + +public class VehicleAccess { + + public static String key(String name) { + return EncodingManager.getKey(name, "access"); + } + + public static BooleanEncodedValue create(String name) { + return new SimpleBooleanEncodedValue(key(name), true); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/VehiclePriority.java b/core/src/main/java/com/graphhopper/routing/ev/VehiclePriority.java new file mode 100644 index 00000000000..c727997f6f3 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/VehiclePriority.java @@ -0,0 +1,32 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +import com.graphhopper.routing.util.EncodingManager; + +public class VehiclePriority { + + public static String key(String name) { + return EncodingManager.getKey(name, "priority"); + } + + public static DecimalEncodedValue create(String name, int speedBits, double speedFactor, boolean storeTwoDirections) { + return new DecimalEncodedValueImpl(key(name), speedBits, speedFactor, storeTwoDirections); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/ev/VehicleSpeed.java b/core/src/main/java/com/graphhopper/routing/ev/VehicleSpeed.java new file mode 100644 index 00000000000..a95f09c8754 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/ev/VehicleSpeed.java @@ -0,0 +1,32 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.ev; + +import com.graphhopper.routing.util.EncodingManager; + +public class VehicleSpeed { + + public static String key(String name) { + return EncodingManager.getKey(name, "average_speed"); + } + + public static DecimalEncodedValue create(String name, int speedBits, double speedFactor, boolean storeTwoDirections) { + return new DecimalEncodedValueImpl(key(name), speedBits, speedFactor, storeTwoDirections); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMApproximator.java b/core/src/main/java/com/graphhopper/routing/lm/LMApproximator.java index 4a6ace35c63..da195ee4212 100644 --- a/core/src/main/java/com/graphhopper/routing/lm/LMApproximator.java +++ b/core/src/main/java/com/graphhopper/routing/lm/LMApproximator.java @@ -34,10 +34,13 @@ public class LMApproximator implements WeightApproximator { private final LandmarkStorage lms; - private final Weighting weighting; - private int[] activeLandmarkIndices; - private int[] weightsFromActiveLandmarksToT; - private int[] weightsFromTToActiveLandmarks; + // the weighting used for the LM preparation + private final Weighting lmWeighting; + // the weighting used for the current path calculation + private final Weighting routingWeighting; + private final int[] activeLandmarkIndices; + private final int[] weightsFromActiveLandmarksToT; + private final int[] weightsFromTToActiveLandmarks; private double epsilon = 1; private int towerNodeNextToT = -1; private double weightFromTToTowerNode; @@ -47,13 +50,18 @@ public class LMApproximator implements WeightApproximator { private final int maxBaseNodes; private final Graph graph; private final WeightApproximator fallBackApproximation; + private final WeightApproximator beelineApproximation; private boolean fallback = false; - public static LMApproximator forLandmarks(Graph g, LandmarkStorage lms, int activeLM) { - return new LMApproximator(g, lms.getWeighting(), lms.getBaseNodes(), lms, activeLM, lms.getFactor(), false); + /** + * @param weighting the weighting used for the current path calculation, not necessarily the same that we used for the LM preparation. + * All edge weights must be larger or equal compared to those used for the preparation. + */ + public static LMApproximator forLandmarks(Graph g, Weighting weighting, LandmarkStorage lms, int activeLM) { + return new LMApproximator(g, lms.getWeighting(), weighting, lms.getBaseNodes(), lms, activeLM, lms.getFactor(), false); } - public LMApproximator(Graph graph, Weighting weighting, int maxBaseNodes, LandmarkStorage lms, int activeCount, + public LMApproximator(Graph graph, Weighting lmWeighting, Weighting routingWeighting, int maxBaseNodes, LandmarkStorage lms, int activeCount, double factor, boolean reverse) { this.reverse = reverse; this.lms = lms; @@ -68,8 +76,10 @@ public LMApproximator(Graph graph, Weighting weighting, int maxBaseNodes, Landma weightsFromTToActiveLandmarks = new int[activeCount]; this.graph = graph; - this.weighting = weighting; - this.fallBackApproximation = new BeelineWeightApproximator(graph.getNodeAccess(), weighting); + this.lmWeighting = lmWeighting; + this.routingWeighting = routingWeighting; + this.fallBackApproximation = new BeelineWeightApproximator(graph.getNodeAccess(), lmWeighting); + this.beelineApproximation = new BeelineWeightApproximator(graph.getNodeAccess(), routingWeighting); this.maxBaseNodes = maxBaseNodes; } @@ -109,7 +119,11 @@ public double approximate(final int v) { return fallBackApproximation.approximate(v); } } - return Math.max(0.0, (getRemainingWeightUnderestimationUpToTowerNode(v) - weightFromTToTowerNode) * epsilon); + double lmApproximation = Math.max(0.0, (getRemainingWeightUnderestimationUpToTowerNode(v) - weightFromTToTowerNode) * epsilon); + // Since both the LM and the beeline approximations underestimate the real remaining weight the larger one is + // more accurate. For example when the speed is reduced for all roads the beeline approximation adjusts automatically + // to the reduced global maximum speed, while the LM approximation becomes worse. + return Math.max(lmApproximation, beelineApproximation.approximate(v)); } private double getRemainingWeightUnderestimationUpToTowerNode(int v) { @@ -153,26 +167,25 @@ private int approximateForLandmark(int i, int v) { // // ...and we can get the right-hand sides of III) and IV) by multiplying those of II) and I) by -1. - int rhs1Int = weightsFromActiveLandmarksToT[i] - lms.getFromWeight(activeLandmarkIndices[i], v); - int rhs2Int = lms.getToWeight(activeLandmarkIndices[i], v) - weightsFromTToActiveLandmarks[i]; + int rhs1Int = lms.getToWeight(activeLandmarkIndices[i], v) - weightsFromTToActiveLandmarks[i]; + int rhs2Int = weightsFromActiveLandmarksToT[i] - lms.getFromWeight(activeLandmarkIndices[i], v); - int resultInt; if (reverse) { - resultInt = Math.max(-rhs2Int, -rhs1Int); - } else { - resultInt = Math.max(rhs1Int, rhs2Int); + rhs1Int *= -1; + rhs2Int *= -1; } - return resultInt; + return Math.max(rhs1Int, rhs2Int); } @Override public void setTo(int t) { this.fallBackApproximation.setTo(t); + this.beelineApproximation.setTo(t); findClosestRealNode(t); } private void findClosestRealNode(int t) { - Dijkstra dijkstra = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) { + Dijkstra dijkstra = new Dijkstra(graph, lmWeighting, TraversalMode.NODE_BASED) { @Override protected boolean finished() { towerNodeNextToT = currEdge.adjNode; @@ -191,7 +204,7 @@ protected void initCollections(int size) { @Override public WeightApproximator reverse() { - return new LMApproximator(graph, weighting, maxBaseNodes, lms, activeLandmarkIndices.length, factor, !reverse); + return new LMApproximator(graph, lmWeighting, routingWeighting, maxBaseNodes, lms, activeLandmarkIndices.length, factor, !reverse); } @Override diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java b/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java index 1ff78c3e4cc..b821d506b42 100644 --- a/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java +++ b/core/src/main/java/com/graphhopper/routing/lm/LMPreparationHandler.java @@ -23,8 +23,9 @@ import com.graphhopper.config.LMProfile; import com.graphhopper.routing.ev.EncodedValueLookup; import com.graphhopper.routing.util.AreaIndex; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.storage.StorableProperties; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.util.GHUtility; import com.graphhopper.util.JsonFeatureCollection; @@ -41,6 +42,7 @@ import java.util.*; import java.util.concurrent.Callable; import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.graphhopper.util.Helper.*; @@ -145,8 +147,8 @@ public List getLMProfiles() { */ public List load(List lmConfigs, BaseGraph baseGraph, EncodedValueLookup encodedValueLookup) { List loaded = Collections.synchronizedList(new ArrayList<>()); - List> loadingCallables = lmConfigs.stream() - .map(lmConfig -> (Callable) () -> { + Stream> loadingCallables = lmConfigs.stream() + .map(lmConfig -> () -> { // todo: specifying ghStorage and landmarkCount should not be necessary, because all we want to do // is load the landmark data and these parameters are only needed to calculate the landmarks. // we should also work towards a separation of the storage and preparation related code in @@ -163,8 +165,7 @@ public List load(List lmConfigs, BaseGraph baseGraph, baseGraph.getDirectory().remove("landmarks_subnetwork_" + lmConfig.getName()); } return lmConfig.getName(); - }) - .collect(Collectors.toList()); + }); GHUtility.runConcurrently(loadingCallables, preparationThreads); return loaded; } @@ -172,8 +173,8 @@ public List load(List lmConfigs, BaseGraph baseGraph, /** * Prepares the landmark data for all given configs */ - public List prepare(List lmConfigs, GraphHopperStorage ghStorage, LocationIndex locationIndex, final boolean closeEarly) { - List preparations = createPreparations(lmConfigs, ghStorage.getBaseGraph(), ghStorage.getEncodingManager(), locationIndex); + public List prepare(List lmConfigs, BaseGraph baseGraph, EncodingManager encodingManager, StorableProperties properties, LocationIndex locationIndex, final boolean closeEarly) { + List preparations = createPreparations(lmConfigs, baseGraph, encodingManager, locationIndex); List> prepareCallables = new ArrayList<>(); for (int i = 0; i < preparations.size(); i++) { PrepareLandmarks prepare = preparations.get(i); @@ -186,11 +187,11 @@ public List prepare(List lmConfigs, GraphHopperStora if (closeEarly) prepare.close(); LOGGER.info("LM {} finished {}", name, getMemInfo()); - ghStorage.getProperties().put(Landmark.PREPARE + "date." + name, createFormatter().format(new Date())); + properties.put(Landmark.PREPARE + "date." + name, createFormatter().format(new Date())); return name; }); } - GHUtility.runConcurrently(prepareCallables, preparationThreads); + GHUtility.runConcurrently(prepareCallables.stream(), preparationThreads); LOGGER.info("Finished LM preparation, {}", getMemInfo()); return preparations; } diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMRoutingAlgorithmFactory.java b/core/src/main/java/com/graphhopper/routing/lm/LMRoutingAlgorithmFactory.java index 11a3bc5948e..5944a5207de 100644 --- a/core/src/main/java/com/graphhopper/routing/lm/LMRoutingAlgorithmFactory.java +++ b/core/src/main/java/com/graphhopper/routing/lm/LMRoutingAlgorithmFactory.java @@ -26,7 +26,6 @@ import com.graphhopper.util.Parameters; import static com.graphhopper.util.Parameters.Algorithms.*; -import static com.graphhopper.util.Parameters.Algorithms.AltRoute.*; public class LMRoutingAlgorithmFactory implements RoutingAlgorithmFactory { private final LandmarkStorage lms; @@ -52,26 +51,19 @@ public RoutingAlgorithm createAlgo(Graph g, Weighting w, AlgorithmOptions opts) if (ASTAR.equalsIgnoreCase(algoStr)) { double epsilon = opts.getHints().getDouble(Parameters.Algorithms.AStar.EPSILON, 1); AStar algo = new AStar(g, weighting, opts.getTraversalMode()); - algo.setApproximation(getApproximator(g, activeLM, epsilon)); + algo.setApproximation(getApproximator(g, weighting, activeLM, epsilon)); algo.setMaxVisitedNodes(opts.getMaxVisitedNodes()); return algo; } else if (ASTAR_BI.equalsIgnoreCase(algoStr) || Helper.isEmpty(algoStr)) { double epsilon = opts.getHints().getDouble(Parameters.Algorithms.AStarBi.EPSILON, 1); AStarBidirection algo = new AStarBidirection(g, weighting, opts.getTraversalMode()); - algo.setApproximation(getApproximator(g, activeLM, epsilon)); + algo.setApproximation(getApproximator(g, weighting, activeLM, epsilon)); algo.setMaxVisitedNodes(opts.getMaxVisitedNodes()); return algo; } else if (ALT_ROUTE.equalsIgnoreCase(algoStr)) { double epsilon = opts.getHints().getDouble(Parameters.Algorithms.AStarBi.EPSILON, 1); - AlternativeRoute algo = new AlternativeRoute(g, weighting, opts.getTraversalMode()); - algo.setMaxPaths(opts.getHints().getInt(MAX_PATHS, 2)); - algo.setMaxWeightFactor(opts.getHints().getDouble(MAX_WEIGHT, 1.4)); - algo.setMaxShareFactor(opts.getHints().getDouble(MAX_SHARE, 0.6)); - algo.setMinPlateauFactor(opts.getHints().getDouble("alternative_route.min_plateau_factor", 0.2)); - algo.setApproximation(getApproximator(g, activeLM, epsilon)); - // landmark algorithm follows good compromise between fast response and exploring 'interesting' paths so we - // can decrease this exploration factor further (1->dijkstra, 0.8->bidir. A*) - algo.setMaxExplorationFactor(0.6); + AlternativeRoute algo = new AlternativeRoute(g, weighting, opts.getTraversalMode(), opts.getHints()); + algo.setApproximation(getApproximator(g, weighting, activeLM, epsilon)); algo.setMaxVisitedNodes(opts.getMaxVisitedNodes()); return algo; } else { @@ -80,7 +72,7 @@ public RoutingAlgorithm createAlgo(Graph g, Weighting w, AlgorithmOptions opts) } } - private LMApproximator getApproximator(Graph g, int activeLM, double epsilon) { - return LMApproximator.forLandmarks(g, lms, activeLM).setEpsilon(epsilon); + private LMApproximator getApproximator(Graph g, Weighting weighting, int activeLM, double epsilon) { + return LMApproximator.forLandmarks(g, weighting, lms, activeLM).setEpsilon(epsilon); } } diff --git a/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java b/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java index bcb97fcf62c..1ab2556aeff 100644 --- a/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java +++ b/core/src/main/java/com/graphhopper/routing/lm/LandmarkStorage.java @@ -224,7 +224,7 @@ boolean isInitialized() { } /** - * This method calculates the landmarks and initial weightings to & from them. + * This method calculates the landmarks and initial weightings to & from them. */ public void createLandmarks() { if (isInitialized()) diff --git a/core/src/main/java/com/graphhopper/routing/matrix/algorithm/AbstractManyToMany.java b/core/src/main/java/com/graphhopper/routing/matrix/algorithm/AbstractManyToMany.java index 67f8056a810..d431f643cc0 100644 --- a/core/src/main/java/com/graphhopper/routing/matrix/algorithm/AbstractManyToMany.java +++ b/core/src/main/java/com/graphhopper/routing/matrix/algorithm/AbstractManyToMany.java @@ -263,7 +263,7 @@ protected void backwardSearch( Snap targetSnap, int targetIdx){ protected abstract int getTraversalId(RoutingCHEdgeIteratorState edge, int origEdgeId, Boolean reverse); protected int getOrigEdgeId(RoutingCHEdgeIteratorState edge, boolean reverse) { - return reverse ? edge.getOrigEdgeFirst() : edge.getOrigEdgeLast(); + return reverse ? edge.getOrigEdgeKeyFirst() : edge.getOrigEdgeKeyLast(); } protected boolean accept(RoutingCHEdgeIteratorState edge, MatrixEntry currEdge) { diff --git a/core/src/main/java/com/graphhopper/routing/matrix/algorithm/ManyToManyEdge.java b/core/src/main/java/com/graphhopper/routing/matrix/algorithm/ManyToManyEdge.java index b1794ba880a..1456cb10c54 100644 --- a/core/src/main/java/com/graphhopper/routing/matrix/algorithm/ManyToManyEdge.java +++ b/core/src/main/java/com/graphhopper/routing/matrix/algorithm/ManyToManyEdge.java @@ -11,7 +11,7 @@ public class ManyToManyEdge extends AbstractManyToMany { private TraversalMode traversalMode = TraversalMode.EDGE_BASED; - public ManyToManyEdge(QueryRoutingCHGraph graph){ + public ManyToManyEdge(QueryRoutingCHGraph graph) { super(graph); if (!graph.isEdgeBased()) { @@ -20,16 +20,16 @@ public ManyToManyEdge(QueryRoutingCHGraph graph){ } @Override - protected int getTraversalId(RoutingCHEdgeIteratorState edge, int origEdgeId, Boolean reverse){ + protected int getTraversalId(RoutingCHEdgeIteratorState edge, int origEdgeId, Boolean reverse) { - return traversalMode.createTraversalId(edge.getBaseNode(),edge.getAdjNode(),origEdgeId,reverse); + return traversalMode.createTraversalId(edge, reverse); } - private double calcWeight(RoutingCHEdgeIteratorState edgeState, Boolean reverse, int prevOrNextEdgeId){ + private double calcWeight(RoutingCHEdgeIteratorState edgeState, Boolean reverse, int prevOrNextEdgeId) { double edgeWeight = edgeState.getWeight(reverse); - final int origEdgeId = reverse ? edgeState.getOrigEdgeLast() : edgeState.getOrigEdgeFirst(); + final int origEdgeId = getOrigEdgeId(edgeState, reverse); double turnCosts = reverse ? graph.getTurnWeight(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId) : graph.getTurnWeight(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId); @@ -38,32 +38,27 @@ private double calcWeight(RoutingCHEdgeIteratorState edgeState, Boolean reverse, } @Override - protected double calcWeight(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge, boolean reverse){ - return calcWeight(iter,reverse,getIncomingEdge(currEdge)) + currEdge.getWeightOfVisitedPath(); + protected double calcWeight(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge, boolean reverse) { + return calcWeight(iter, reverse, getIncomingEdge(currEdge)) + currEdge.getWeightOfVisitedPath(); } - private long calcTime(RoutingCHEdgeIteratorState edgeState, Boolean reverse, int prevOrNextEdgeId){ + private long calcTime(RoutingCHEdgeIteratorState edgeState, Boolean reverse, int prevOrNextEdgeId) { long time = edgeState.getTime(reverse); - int origEdgeId; - if(reverse){ - origEdgeId = edgeState.getOrigEdgeLast(); - }else{ - origEdgeId = edgeState.getOrigEdgeFirst(); - } + int origEdgeId = getOrigEdgeId(edgeState, reverse); long turnCost; - if(reverse){ - turnCost = weighting.calcTurnMillis(origEdgeId,edgeState.getBaseNode(),prevOrNextEdgeId); - }else{ - turnCost = weighting.calcTurnMillis(prevOrNextEdgeId,edgeState.getBaseNode(),origEdgeId); + if (reverse) { + turnCost = weighting.calcTurnMillis(origEdgeId, edgeState.getBaseNode(), prevOrNextEdgeId); + } else { + turnCost = weighting.calcTurnMillis(prevOrNextEdgeId, edgeState.getBaseNode(), origEdgeId); } return time + turnCost; } @Override - protected long calcTime(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge, boolean reverse){ - return calcTime(iter,reverse,getIncomingEdge(currEdge)) + currEdge.time; + protected long calcTime(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge, boolean reverse) { + return calcTime(iter, reverse, getIncomingEdge(currEdge)) + currEdge.time; } protected int getIncomingEdge(MatrixEntry entry) { @@ -71,17 +66,12 @@ protected int getIncomingEdge(MatrixEntry entry) { } @Override - protected int getOrigEdgeId(RoutingCHEdgeIteratorState edge, boolean reverse) { - return reverse ? edge.getOrigEdgeFirst() : edge.getOrigEdgeLast(); - } - - @Override - protected double calcDistance(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge){ + protected double calcDistance(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge) { return iter.getDistance() + currEdge.distance; } @Override - public String getName(){ + public String getName() { return getClass().getSimpleName(); } } \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/matrix/algorithm/ManyToManyNode.java b/core/src/main/java/com/graphhopper/routing/matrix/algorithm/ManyToManyNode.java index d60f9974ae3..16e25aef588 100644 --- a/core/src/main/java/com/graphhopper/routing/matrix/algorithm/ManyToManyNode.java +++ b/core/src/main/java/com/graphhopper/routing/matrix/algorithm/ManyToManyNode.java @@ -10,7 +10,7 @@ public class ManyToManyNode extends AbstractManyToMany { private TraversalMode traversalMode = TraversalMode.NODE_BASED; - public ManyToManyNode(QueryRoutingCHGraph graph){ + public ManyToManyNode(QueryRoutingCHGraph graph) { super(graph); @@ -19,35 +19,35 @@ public ManyToManyNode(QueryRoutingCHGraph graph){ } @Override - protected int getTraversalId(RoutingCHEdgeIteratorState state, int origEdgeId,Boolean reverse){ - return traversalMode.createTraversalId(state.getBaseNode(),state.getAdjNode(),state.getEdge(),reverse); + protected int getTraversalId(RoutingCHEdgeIteratorState state, int origEdgeId, Boolean reverse) { + return traversalMode.createTraversalId(state, reverse); } @Override protected boolean accept(RoutingCHEdgeIteratorState edge, MatrixEntry currEdge) { - if(edge.getEdge() == getIncomingEdge(currEdge)) + if (edge.getEdge() == getIncomingEdge(currEdge)) return false; else return levelEdgeFilter == null || levelEdgeFilter.accept(edge); } - @Override - protected double calcWeight(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge, boolean reverse){ + @Override + protected double calcWeight(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge, boolean reverse) { return iter.getWeight(reverse) + currEdge.getWeightOfVisitedPath(); } @Override - protected long calcTime(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge, boolean reverse){ + protected long calcTime(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge, boolean reverse) { return iter.getTime(reverse) + currEdge.time; } @Override - protected double calcDistance(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge){ + protected double calcDistance(RoutingCHEdgeIteratorState iter, MatrixEntry currEdge) { return iter.getDistance() + currEdge.distance; } @Override - public String getName(){ + public String getName() { return getClass().getSimpleName(); } } \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraph.java b/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraph.java index 8a66b14695f..f8b84fab864 100644 --- a/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraph.java +++ b/core/src/main/java/com/graphhopper/routing/querygraph/QueryGraph.java @@ -41,7 +41,7 @@ * introducing virtual nodes and edges. It is lightweight in order to be created every time a new * query comes in, which makes the behaviour thread safe. *

- * Calling any create method creates virtual edges between the tower nodes of the existing + * Calling any create method creates virtual edges between the tower nodes of the existing * graph and new virtual tower nodes. Every virtual node has two adjacent nodes and is connected * to each adjacent nodes via 2 virtual edges with opposite base node / adjacent node encoding. * However, the edge explorer returned by {@link #createEdgeExplorer()} only returns two @@ -67,28 +67,10 @@ public static QueryGraph create(BaseGraph graph, Snap snap) { return QueryGraph.create(graph, Collections.singletonList(snap)); } - /** - * @deprecated currently we use this only for easier GraphHopperStorage -> BaseGraph migration - * instead of just calling graph.getBaseGraph() better try to convert graph to a BaseGraph - */ - @Deprecated - public static QueryGraph create(Graph graph, Snap snap) { - return create(graph.getBaseGraph(), snap); - } - public static QueryGraph create(BaseGraph graph, Snap fromSnap, Snap toSnap) { return QueryGraph.create(graph.getBaseGraph(), Arrays.asList(fromSnap, toSnap)); } - /** - * @deprecated currently we use this only for easier GraphHopperStorage -> BaseGraph migration - * instead of just calling graph.getBaseGraph() better try to convert graph to a BaseGraph - */ - @Deprecated - public static QueryGraph create(Graph graph, Snap fromSnap, Snap toSnap) { - return create(graph.getBaseGraph(), fromSnap, toSnap); - } - public static QueryGraph create(BaseGraph graph, List snaps) { return new QueryGraph(graph, snaps); } diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/QueryOverlayBuilder.java b/core/src/main/java/com/graphhopper/routing/querygraph/QueryOverlayBuilder.java index a15daab548f..557c09ca5ae 100644 --- a/core/src/main/java/com/graphhopper/routing/querygraph/QueryOverlayBuilder.java +++ b/core/src/main/java/com/graphhopper/routing/querygraph/QueryOverlayBuilder.java @@ -20,6 +20,7 @@ import com.carrotsearch.hppc.predicates.IntObjectPredicate; import com.graphhopper.coll.GHIntObjectHashMap; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.Graph; import com.graphhopper.storage.IntsRef; import com.graphhopper.storage.index.Snap; @@ -32,6 +33,8 @@ import java.util.Comparator; import java.util.List; +import static com.graphhopper.util.DistancePlaneProjection.DIST_PLANE; + class QueryOverlayBuilder { private final int firstVirtualNodeId; private final int firstVirtualEdgeId; @@ -147,7 +150,7 @@ private double distanceOfSnappedPointToPillarNode(Snap o) { GHPoint3D prevPoint = fullPL.get(0); int adjNode = closestEdge.getAdjNode(); int origEdgeKey = closestEdge.getEdgeKey(); - int origRevEdgeKey = GHUtility.reverseEdgeKey(origEdgeKey); + int origRevEdgeKey = closestEdge.getReverseEdgeKey(); int prevWayIndex = 1; int prevNodeId = baseNode; int virtNodeId = queryOverlay.getVirtualNodes().size() + firstVirtualNodeId; @@ -156,15 +159,20 @@ private double distanceOfSnappedPointToPillarNode(Snap o) { // Create base and adjacent PointLists for all non-equal virtual nodes. // We do so via inserting them at the correct position of fullPL and cutting the // fullPL into the right pieces. - for (Snap res : results) { + for (int i = 0; i < results.size(); i++) { + Snap res = results.get(i); if (res.getClosestEdge().getBaseNode() != baseNode) throw new IllegalStateException("Base nodes have to be identical but were not: " + closestEdge + " vs " + res.getClosestEdge()); GHPoint3D currSnapped = res.getSnappedPoint(); - // no new virtual nodes if exactly the same snapped point - if (prevPoint.equals(currSnapped)) { + // no new virtual nodes if very close ("snap" together) + if (Snap.considerEqual(prevPoint.lat, prevPoint.lon, currSnapped.lat, currSnapped.lon)) { res.setClosestNode(prevNodeId); + res.setSnappedPoint(prevPoint); + res.setWayIndex(i == 0 ? 0 : results.get(i - 1).getWayIndex()); + res.setSnappedPosition(i == 0 ? Snap.Position.TOWER : results.get(i - 1).getSnappedPosition()); + res.setQueryDistance(DIST_PLANE.calcDist(prevPoint.lat, prevPoint.lon, res.getQueryPoint().lat, res.getQueryPoint().lon)); continue; } @@ -225,10 +233,11 @@ private void createEdges(int origEdgeKey, int origRevEdgeKey, boolean reverse = closestEdge.get(EdgeIteratorState.REVERSE_STATE); // edges between base and snapped point - VirtualEdgeIteratorState baseEdge = new VirtualEdgeIteratorState(origEdgeKey, GHUtility.createEdgeKey(virtEdgeId, false), - prevNodeId, nodeId, baseDistance, closestEdge.getFlags(), closestEdge.getName(), basePoints, reverse); - VirtualEdgeIteratorState baseReverseEdge = new VirtualEdgeIteratorState(origRevEdgeKey, GHUtility.createEdgeKey(virtEdgeId, true), - nodeId, prevNodeId, baseDistance, IntsRef.deepCopyOf(closestEdge.getFlags()), closestEdge.getName(), baseReversePoints, !reverse); + List keyValues = closestEdge.getKeyValues(); + VirtualEdgeIteratorState baseEdge = new VirtualEdgeIteratorState(origEdgeKey, GHUtility.createEdgeKey(virtEdgeId, prevNodeId == nodeId, false), + prevNodeId, nodeId, baseDistance, closestEdge.getFlags(), keyValues, basePoints, reverse); + VirtualEdgeIteratorState baseReverseEdge = new VirtualEdgeIteratorState(origRevEdgeKey, GHUtility.createEdgeKey(virtEdgeId, prevNodeId == nodeId, true), + nodeId, prevNodeId, baseDistance, IntsRef.deepCopyOf(closestEdge.getFlags()), keyValues, baseReversePoints, !reverse); baseEdge.setReverseEdge(baseReverseEdge); baseReverseEdge.setReverseEdge(baseEdge); diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/QueryRoutingCHGraph.java b/core/src/main/java/com/graphhopper/routing/querygraph/QueryRoutingCHGraph.java index 5c7ca3ff3e9..9d89b22f27f 100644 --- a/core/src/main/java/com/graphhopper/routing/querygraph/QueryRoutingCHGraph.java +++ b/core/src/main/java/com/graphhopper/routing/querygraph/QueryRoutingCHGraph.java @@ -201,14 +201,14 @@ public void apply(int node, QueryOverlay.EdgeChanges edgeChanges) { // shortcuts cannot be in the removed edge set because this was determined on the (base) query graph if (iter.isShortcut()) { virtualEdges.add(new VirtualCHEdgeIteratorState(iter.getEdge(), NO_EDGE, - iter.getBaseNode(), iter.getAdjNode(), iter.getOrigEdgeFirst(), iter.getOrigEdgeLast(), + iter.getBaseNode(), iter.getAdjNode(), iter.getOrigEdgeKeyFirst(), iter.getOrigEdgeKeyLast(), iter.getSkippedEdge1(), iter.getSkippedEdge2(), iter.getWeight(false), iter.getWeight(true), iter.getTime(false),iter.getTime(true), iter.getDistance() )); } else if (!edgeChanges.getRemovedEdges().contains(iter.getOrigEdge())) { virtualEdges.add(new VirtualCHEdgeIteratorState(iter.getEdge(), iter.getOrigEdge(), - iter.getBaseNode(), iter.getAdjNode(), iter.getOrigEdgeFirst(), iter.getOrigEdgeLast(), + iter.getBaseNode(), iter.getAdjNode(), iter.getOrigEdgeKeyFirst(), iter.getOrigEdgeKeyLast(), NO_EDGE, NO_EDGE, iter.getWeight(false), iter.getWeight(true), iter.getTime(false),iter.getTime(true),iter.getDistance() )); @@ -239,7 +239,6 @@ private VirtualCHEdgeIteratorState buildVirtualCHEdgeState(VirtualEdgeIteratorSt } private VirtualCHEdgeIteratorState buildVirtualCHEdgeState(EdgeIteratorState edgeState, int edgeID) { - int origEdge = edgeState.getEdge(); double fwdWeight = weighting.calcEdgeWeightWithAccess(edgeState, false); double bwdWeight = weighting.calcEdgeWeightWithAccess(edgeState, true); @@ -249,8 +248,8 @@ private VirtualCHEdgeIteratorState buildVirtualCHEdgeState(EdgeIteratorState edg double distance = edgeState.getDistance(); - return new VirtualCHEdgeIteratorState(edgeID, origEdge, edgeState.getBaseNode(), edgeState.getAdjNode(), - origEdge, origEdge, NO_EDGE, NO_EDGE, fwdWeight, bwdWeight, fwdTime,bwdTime,distance); + return new VirtualCHEdgeIteratorState(edgeID, edgeState.getEdge(), edgeState.getBaseNode(), edgeState.getAdjNode(), + edgeState.getEdgeKey(), edgeState.getEdgeKey(), NO_EDGE, NO_EDGE, fwdWeight, bwdWeight, fwdTime,bwdTime,distance); } private int shiftVirtualEdgeIDForCH(int edge) { @@ -274,8 +273,8 @@ private static class VirtualCHEdgeIteratorState implements RoutingCHEdgeIterator private final int origEdge; private final int baseNode; private final int adjNode; - private final int origEdgeFirst; - private final int origEdgeLast; + private final int origEdgeKeyFirst; + private final int origEdgeKeyLast; private final int skippedEdge1; private final int skippedEdge2; private final double weightFwd; @@ -285,8 +284,8 @@ private static class VirtualCHEdgeIteratorState implements RoutingCHEdgeIterator private final long timeBwd; private final double distance; - public VirtualCHEdgeIteratorState(int edge, int origEdge, int baseNode, int adjNode, int origEdgeFirst, - int origEdgeLast, int skippedEdge1, int skippedEdge2, + public VirtualCHEdgeIteratorState(int edge, int origEdge, int baseNode, int adjNode, int origEdgeKeyFirst, + int origEdgeKeyLast, int skippedEdge1, int skippedEdge2, double weightFwd, double weightBwd, long timeFwd, long timeBwd, double distance @@ -295,8 +294,8 @@ public VirtualCHEdgeIteratorState(int edge, int origEdge, int baseNode, int adjN this.origEdge = origEdge; this.baseNode = baseNode; this.adjNode = adjNode; - this.origEdgeFirst = origEdgeFirst; - this.origEdgeLast = origEdgeLast; + this.origEdgeKeyFirst = origEdgeKeyFirst; + this.origEdgeKeyLast = origEdgeKeyLast; this.skippedEdge1 = skippedEdge1; this.skippedEdge2 = skippedEdge2; this.weightFwd = weightFwd; @@ -317,13 +316,13 @@ public int getOrigEdge() { } @Override - public int getOrigEdgeFirst() { - return origEdgeFirst; + public int getOrigEdgeKeyFirst() { + return origEdgeKeyFirst; } @Override - public int getOrigEdgeLast() { - return origEdgeLast; + public int getOrigEdgeKeyLast() { + return origEdgeKeyLast; } @Override @@ -399,13 +398,13 @@ public int getOrigEdge() { } @Override - public int getOrigEdgeFirst() { - return getCurrent().getOrigEdgeFirst(); + public int getOrigEdgeKeyFirst() { + return getCurrent().getOrigEdgeKeyFirst(); } @Override - public int getOrigEdgeLast() { - return getCurrent().getOrigEdgeLast(); + public int getOrigEdgeKeyLast() { + return getCurrent().getOrigEdgeKeyLast(); } @Override diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIterator.java b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIterator.java index aa561b468a5..9541130fdeb 100644 --- a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIterator.java +++ b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIterator.java @@ -19,6 +19,7 @@ import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.EdgeIteratorState; @@ -73,6 +74,11 @@ public int getEdgeKey() { return getCurrentEdge().getEdgeKey(); } + @Override + public int getReverseEdgeKey() { + return getCurrentEdge().getReverseEdgeKey(); + } + @Override public int getBaseNode() { return getCurrentEdge().getBaseNode(); @@ -256,8 +262,18 @@ public String getName() { } @Override - public EdgeIteratorState setName(String name) { - return getCurrentEdge().setName(name); + public List getKeyValues() { + return getCurrentEdge().getKeyValues(); + } + + @Override + public EdgeIteratorState setKeyValues(List list) { + return getCurrentEdge().setKeyValues(list); + } + + @Override + public Object getValue(String key) { + return getCurrentEdge().getValue(key); } @Override diff --git a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java index c5144d268a2..2aabb20c8d1 100644 --- a/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java +++ b/core/src/main/java/com/graphhopper/routing/querygraph/VirtualEdgeIteratorState.java @@ -18,12 +18,15 @@ package com.graphhopper.routing.querygraph; import com.graphhopper.routing.ev.*; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.FetchMode; import com.graphhopper.util.GHUtility; import com.graphhopper.util.PointList; +import java.util.List; + /** * Creates an edge state decoupled from a graph where nodes, pointList, etc are kept in memory. *

@@ -38,21 +41,23 @@ public class VirtualEdgeIteratorState implements EdgeIteratorState { private final int originalEdgeKey; private double distance; private IntsRef edgeFlags; - private String name; + private EdgeIntAccess edgeIntAccess; + private List keyValues; // true if edge should be avoided as start/stop private boolean unfavored; private EdgeIteratorState reverseEdge; private final boolean reverse; public VirtualEdgeIteratorState(int originalEdgeKey, int edgeKey, int baseNode, int adjNode, double distance, - IntsRef edgeFlags, String name, PointList pointList, boolean reverse) { + IntsRef edgeFlags, List keyValues, PointList pointList, boolean reverse) { this.originalEdgeKey = originalEdgeKey; this.edgeKey = edgeKey; this.baseNode = baseNode; this.adjNode = adjNode; this.distance = distance; this.edgeFlags = edgeFlags; - this.name = name; + this.edgeIntAccess = new IntsRefEdgeIntAccess(edgeFlags); + this.keyValues = keyValues; this.pointList = pointList; this.reverse = reverse; } @@ -77,6 +82,11 @@ public int getEdgeKey() { return edgeKey; } + @Override + public int getReverseEdgeKey() { + return baseNode == adjNode ? edgeKey : GHUtility.reverseEdgeKey(edgeKey); + } + @Override public int getBaseNode() { return baseNode; @@ -137,6 +147,7 @@ public IntsRef getFlags() { @Override public EdgeIteratorState setFlags(IntsRef flags) { this.edgeFlags = flags; + this.edgeIntAccess = new IntsRefEdgeIntAccess(flags); return this; } @@ -145,12 +156,12 @@ public boolean get(BooleanEncodedValue property) { if (property == EdgeIteratorState.UNFAVORED_EDGE) return unfavored; - return property.getBool(reverse, edgeFlags); + return property.getBool(reverse, -1, edgeIntAccess); } @Override public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { - property.setBool(reverse, edgeFlags, value); + property.setBool(reverse, -1, edgeIntAccess, value); return this; } @@ -158,12 +169,12 @@ public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { public boolean getReverse(BooleanEncodedValue property) { if (property == EdgeIteratorState.UNFAVORED_EDGE) return unfavored; - return property.getBool(!reverse, edgeFlags); + return property.getBool(!reverse, -1, edgeIntAccess); } @Override public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) { - property.setBool(!reverse, edgeFlags, value); + property.setBool(!reverse, -1, edgeIntAccess, value); return this; } @@ -171,30 +182,30 @@ public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) public EdgeIteratorState set(BooleanEncodedValue property, boolean fwd, boolean bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setBool(reverse, edgeFlags, fwd); - property.setBool(!reverse, edgeFlags, bwd); + property.setBool(reverse, -1, edgeIntAccess, fwd); + property.setBool(!reverse, -1, edgeIntAccess, bwd); return this; } @Override public int get(IntEncodedValue property) { - return property.getInt(reverse, edgeFlags); + return property.getInt(reverse, -1, edgeIntAccess); } @Override public EdgeIteratorState set(IntEncodedValue property, int value) { - property.setInt(reverse, edgeFlags, value); + property.setInt(reverse, -1, edgeIntAccess, value); return this; } @Override public int getReverse(IntEncodedValue property) { - return property.getInt(!reverse, edgeFlags); + return property.getInt(!reverse, -1, edgeIntAccess); } @Override public EdgeIteratorState setReverse(IntEncodedValue property, int value) { - property.setInt(!reverse, edgeFlags, value); + property.setInt(!reverse, -1, edgeIntAccess, value); return this; } @@ -202,30 +213,30 @@ public EdgeIteratorState setReverse(IntEncodedValue property, int value) { public EdgeIteratorState set(IntEncodedValue property, int fwd, int bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setInt(reverse, edgeFlags, fwd); - property.setInt(!reverse, edgeFlags, bwd); + property.setInt(reverse, -1, edgeIntAccess, fwd); + property.setInt(!reverse, -1, edgeIntAccess, bwd); return this; } @Override public double get(DecimalEncodedValue property) { - return property.getDecimal(reverse, edgeFlags); + return property.getDecimal(reverse, -1, edgeIntAccess); } @Override public EdgeIteratorState set(DecimalEncodedValue property, double value) { - property.setDecimal(reverse, edgeFlags, value); + property.setDecimal(reverse, -1, edgeIntAccess, value); return this; } @Override public double getReverse(DecimalEncodedValue property) { - return property.getDecimal(!reverse, edgeFlags); + return property.getDecimal(!reverse, -1, edgeIntAccess); } @Override public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) { - property.setDecimal(!reverse, edgeFlags, value); + property.setDecimal(!reverse, -1, edgeIntAccess, value); return this; } @@ -233,30 +244,30 @@ public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) public EdgeIteratorState set(DecimalEncodedValue property, double fwd, double bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setDecimal(reverse, edgeFlags, fwd); - property.setDecimal(!reverse, edgeFlags, bwd); + property.setDecimal(reverse, -1, edgeIntAccess, fwd); + property.setDecimal(!reverse, -1, edgeIntAccess, bwd); return this; } @Override public > T get(EnumEncodedValue property) { - return property.getEnum(reverse, edgeFlags); + return property.getEnum(reverse, -1, edgeIntAccess); } @Override public > EdgeIteratorState set(EnumEncodedValue property, T value) { - property.setEnum(reverse, edgeFlags, value); + property.setEnum(reverse, -1, edgeIntAccess, value); return this; } @Override public > T getReverse(EnumEncodedValue property) { - return property.getEnum(!reverse, edgeFlags); + return property.getEnum(!reverse, -1, edgeIntAccess); } @Override public > EdgeIteratorState setReverse(EnumEncodedValue property, T value) { - property.setEnum(!reverse, edgeFlags, value); + property.setEnum(!reverse, -1, edgeIntAccess, value); return this; } @@ -264,30 +275,30 @@ public > EdgeIteratorState setReverse(EnumEncodedValue prop public > EdgeIteratorState set(EnumEncodedValue property, T fwd, T bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setEnum(reverse, edgeFlags, fwd); - property.setEnum(!reverse, edgeFlags, bwd); + property.setEnum(reverse, -1, edgeIntAccess, fwd); + property.setEnum(!reverse, -1, edgeIntAccess, bwd); return this; } @Override public String get(StringEncodedValue property) { - return property.getString(reverse, edgeFlags); + return property.getString(reverse, -1, edgeIntAccess); } @Override public EdgeIteratorState set(StringEncodedValue property, String value) { - property.setString(reverse, edgeFlags, value); + property.setString(reverse, -1, edgeIntAccess, value); return this; } @Override public String getReverse(StringEncodedValue property) { - return property.getString(!reverse, edgeFlags); + return property.getString(!reverse, -1, edgeIntAccess); } @Override public EdgeIteratorState setReverse(StringEncodedValue property, String value) { - property.setString(!reverse, edgeFlags, value); + property.setString(!reverse, -1, edgeIntAccess, value); return this; } @@ -295,22 +306,37 @@ public EdgeIteratorState setReverse(StringEncodedValue property, String value) { public EdgeIteratorState set(StringEncodedValue property, String fwd, String bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setString(reverse, edgeFlags, fwd); - property.setString(!reverse, edgeFlags, bwd); + property.setString(reverse, -1, edgeIntAccess, fwd); + property.setString(!reverse, -1, edgeIntAccess, bwd); return this; } @Override public String getName() { - return name; + String name = (String) getValue(KVStorage.KeyValue.STREET_NAME); + // preserve backward compatibility (returns empty string if name tag missing) + return name == null ? "" : name; } @Override - public EdgeIteratorState setName(String name) { - this.name = name; + public EdgeIteratorState setKeyValues(List list) { + this.keyValues = list; return this; } + @Override + public List getKeyValues() { + return keyValues; + } + + @Override + public Object getValue(String key) { + for (KVStorage.KeyValue keyValue : keyValues) { + if (keyValue.key.equals(key)) return keyValue.value; + } + return null; + } + /** * This method sets edge to unfavored status for routing from the start or to the stop location. */ @@ -329,7 +355,7 @@ public EdgeIteratorState detach(boolean reverse) { // update properties of reverse edge // TODO copy pointList (geometry) too reverseEdge.setFlags(getFlags()); - reverseEdge.setName(getName()); + reverseEdge.setKeyValues(getKeyValues()); reverseEdge.setDistance(getDistance()); return reverseEdge; } else { diff --git a/core/src/main/java/com/graphhopper/routing/subnetwork/EdgeBasedTarjanSCC.java b/core/src/main/java/com/graphhopper/routing/subnetwork/EdgeBasedTarjanSCC.java index e57f2babb88..eef48a6d167 100644 --- a/core/src/main/java/com/graphhopper/routing/subnetwork/EdgeBasedTarjanSCC.java +++ b/core/src/main/java/com/graphhopper/routing/subnetwork/EdgeBasedTarjanSCC.java @@ -21,17 +21,17 @@ import com.carrotsearch.hppc.*; import com.carrotsearch.hppc.cursors.IntCursor; import com.graphhopper.routing.util.AllEdgesIterator; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.storage.Graph; import com.graphhopper.util.BitUtil; -import com.graphhopper.util.EdgeExplorer; -import com.graphhopper.util.EdgeIterator; -import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static com.graphhopper.util.EdgeIterator.NO_EDGE; +import static com.graphhopper.util.GHUtility.getEdgeFromEdgeKey; /** * Edge-based version of Tarjan's algorithm to find strongly connected components on a directed graph. Compared @@ -148,7 +148,7 @@ private ConnectedComponents findComponentsRecursive() { private void findComponentForEdgeKey(int p, int adjNode) { setupNextEdgeKey(p); // we have to create a new explorer on each iteration because of the nested edge iterations - final int edge = getEdgeFromKey(p); + final int edge = getEdgeFromEdgeKey(p); EdgeExplorer explorer = graph.createEdgeExplorer(); EdgeIterator iter = explorer.setBaseNode(adjNode); while (iter.next()) { @@ -266,7 +266,7 @@ private void startSearch() { setupNextEdgeKey(p); // we push buildComponent first so it will run *after* we finished traversing the edges pushBuildComponent(p); - final int edge = getEdgeFromKey(p); + final int edge = getEdgeFromEdgeKey(p); EdgeIterator it = explorer.setBaseNode(adj); while (it.next()) { if (!edgeTransitionFilter.accept(edge, it)) @@ -343,19 +343,8 @@ private void pushHandleNeighbor(int p, int q, int adj) { dfsStackAdj.addLast(adj); } - /** - * Use this method to return the edge for the specified edgeKeys that get returned by {@link #findComponents()}} etc. - * The implementation is like GHUtility.getEdgeFromEdgeKey but might be different in the future. See #2152. - */ - public static int getEdgeFromKey(int edgeKey) { - return edgeKey / 2; - } - public static int createEdgeKey(EdgeIteratorState edgeState, boolean reverse) { - int edgeKey = edgeState.getEdge() << 1; - if (edgeState.get(EdgeIteratorState.REVERSE_STATE) == !reverse) - edgeKey++; - return edgeKey; + return TraversalMode.EDGE_BASED.createTraversalId(edgeState, reverse); } public static class ConnectedComponents { @@ -377,7 +366,7 @@ public static class ConnectedComponents { * A list of arrays each containing the edge keys of a strongly connected component. Components with only a single * edge key are not included here, but need to be obtained using {@link #getSingleEdgeComponents()}. * The edge key is either 2*edgeId (if the edge direction corresponds to the storage order) or 2*edgeId+1 (for - * the opposite direction). Use {@link EdgeBasedTarjanSCC#getEdgeFromKey(int)} to convert edge keys back to + * the opposite direction). Use {@link GHUtility#getEdgeFromEdgeKey(int)} to convert edge keys back to * edge IDs. */ public List getComponents() { @@ -466,7 +455,7 @@ public void set(int key, int value) { @Override public void minTo(int key, int value) { - // todonow: optimize + // todo: optimize with map.indexOf(key) etc map.put(key, Math.min(map.getOrDefault(key, -1), value)); } diff --git a/core/src/main/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworks.java b/core/src/main/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworks.java index 0ad5b3e3068..05a476baf7e 100644 --- a/core/src/main/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworks.java +++ b/core/src/main/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworks.java @@ -22,9 +22,9 @@ import com.carrotsearch.hppc.IntArrayList; import com.carrotsearch.hppc.cursors.IntCursor; import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; -import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; import com.graphhopper.util.Helper; import com.graphhopper.util.StopWatch; @@ -32,6 +32,13 @@ import org.slf4j.LoggerFactory; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static com.graphhopper.util.GHUtility.getEdgeFromEdgeKey; /** * Detects and marks 'subnetworks' with a dedicated subnetwork encoded value. Subnetworks are parts of the road network @@ -61,6 +68,7 @@ public class PrepareRoutingSubnetworks { private final BaseGraph graph; private final List prepareJobs; private int minNetworkSize = 200; + private int threads = 1; public PrepareRoutingSubnetworks(BaseGraph graph, List prepareJobs) { this.graph = graph; @@ -76,6 +84,11 @@ public PrepareRoutingSubnetworks setMinNetworkSize(int minNetworkSize) { return this; } + public PrepareRoutingSubnetworks setThreads(int threads) { + this.threads = threads; + return this; + } + /** * Finds and marks all subnetworks according to {@link #setMinNetworkSize(int)} * @@ -87,16 +100,28 @@ public int doWork() { return 0; } StopWatch sw = new StopWatch().start(); - logger.info("Start marking subnetworks, prepare.min_network_size: " + minNetworkSize + ", nodes: " + + logger.info("Start marking subnetworks, prepare.min_network_size: " + minNetworkSize + ", threads: " + threads + ", nodes: " + Helper.nf(graph.getNodes()) + ", edges: " + Helper.nf(graph.getEdges()) + ", jobs: " + prepareJobs + ", " + Helper.getMemInfo()); - int total = 0; - for (PrepareJob job : prepareJobs) - total += setSubnetworks(job.weighting, job.subnetworkEnc); + AtomicInteger total = new AtomicInteger(0); + List flags = Stream.generate(() -> new BitSet(graph.getEdges())).limit(prepareJobs.size()).collect(Collectors.toList()); + Stream> callables = IntStream.range(0, prepareJobs.size()).mapToObj(i -> () -> { + PrepareJob job = prepareJobs.get(i); + total.addAndGet(setSubnetworks(job.weighting, job.subnetworkEnc.getName().replaceAll("_subnetwork", ""), flags.get(i))); + return job.toString(); + }); + GHUtility.runConcurrently(callables, threads); + AllEdgesIterator iter = graph.getAllEdges(); + while (iter.next()) { + for (int i = 0; i < prepareJobs.size(); i++) { + PrepareJob prepareJob = prepareJobs.get(i); + iter.set(prepareJob.subnetworkEnc, flags.get(i).get(iter.getEdge())); + } + } logger.info("Finished finding and marking subnetworks for " + prepareJobs.size() + " jobs, took: " + sw.stop().getSeconds() + "s, " + Helper.getMemInfo()); - return total; + return total.get(); } - private int setSubnetworks(Weighting weighting, BooleanEncodedValue subnetworkEnc) { + private int setSubnetworks(Weighting weighting, String jobName, BitSet subnetworkFlags) { // partition graph into strongly connected components using Tarjan's algorithm StopWatch sw = new StopWatch().start(); EdgeBasedTarjanSCC.ConnectedComponents ccs = EdgeBasedTarjanSCC.findComponents(graph, @@ -105,7 +130,7 @@ private int setSubnetworks(Weighting weighting, BooleanEncodedValue subnetworkEn List components = ccs.getComponents(); BitSet singleEdgeComponents = ccs.getSingleEdgeComponents(); long numSingleEdgeComponents = singleEdgeComponents.cardinality(); - logger.info(subnetworkEnc.getName().replaceAll("_subnetwork", "") + " - Found " + ccs.getTotalComponents() + " subnetworks (" + numSingleEdgeComponents + " single edges and " + logger.info(jobName + " - Found " + ccs.getTotalComponents() + " subnetworks (" + numSingleEdgeComponents + " single edges and " + components.size() + " components with more than one edge, total nodes: " + ccs.getEdgeKeys() + "), took: " + sw.stop().getSeconds() + "s"); final int minNetworkSizeEdgeKeys = 2 * minNetworkSize; @@ -123,7 +148,7 @@ private int setSubnetworks(Weighting weighting, BooleanEncodedValue subnetworkEn if (component.size() < minNetworkSizeEdgeKeys) { for (IntCursor cursor : component) - markedEdges += setSubnetworkEdge(cursor.value, weighting, subnetworkEnc); + markedEdges += setSubnetworkEdge(cursor.value, weighting, subnetworkFlags); subnetworks++; biggestSubnetwork = Math.max(biggestSubnetwork, component.size()); } else { @@ -134,7 +159,7 @@ private int setSubnetworks(Weighting weighting, BooleanEncodedValue subnetworkEn if (minNetworkSizeEdgeKeys > 0) { BitSetIterator iter = singleEdgeComponents.iterator(); for (int edgeKey = iter.nextSetBit(); edgeKey >= 0; edgeKey = iter.nextSetBit()) { - markedEdges += setSubnetworkEdge(edgeKey, weighting, subnetworkEnc); + markedEdges += setSubnetworkEdge(edgeKey, weighting, subnetworkFlags); subnetworks++; biggestSubnetwork = Math.max(biggestSubnetwork, 1); } @@ -147,21 +172,21 @@ private int setSubnetworks(Weighting weighting, BooleanEncodedValue subnetworkEn throw new IllegalStateException("Too many total (directed) edges were marked as subnetwork edges: " + markedEdges + " out of " + (2 * graph.getEdges()) + "\n" + "The maximum number of subnetwork edges is: " + (2 * allowedMarked)); - logger.info(subnetworkEnc.getName().replaceAll("_subnetwork", "") + " - Marked " + subnetworks + " subnetworks (biggest: " + biggestSubnetwork + " edges) -> " + + logger.info(jobName + " - Marked " + subnetworks + " subnetworks (biggest: " + biggestSubnetwork + " edges) -> " + (ccs.getTotalComponents() - subnetworks) + " components(s) remain (smallest: " + smallestNonSubnetwork + ", biggest: " + ccs.getBiggestComponent().size() + " edges)" + ", total marked edges: " + markedEdges + ", took: " + sw.stop().getSeconds() + "s"); return markedEdges; } - private int setSubnetworkEdge(int edgeKey, Weighting weighting, BooleanEncodedValue subnetworkEnc) { + private int setSubnetworkEdge(int edgeKey, Weighting weighting, BitSet subnetworkFlags) { // edges that are not accessible anyway are not marked as subnetworks additionally if (!Double.isFinite(weighting.calcEdgeWeightWithAccess(graph.getEdgeIteratorStateForKey(edgeKey), false))) return 0; // now get edge again but in stored direction so that subnetwork EV is not overwritten (as it is unidirectional) - EdgeIteratorState edgeState = graph.getEdgeIteratorState(EdgeBasedTarjanSCC.getEdgeFromKey(edgeKey), Integer.MIN_VALUE); - if (!edgeState.get(subnetworkEnc)) { - edgeState.set(subnetworkEnc, true); + int edge = getEdgeFromEdgeKey(edgeKey); + if (!subnetworkFlags.get(edge)) { + subnetworkFlags.set(edge); return 1; } else { return 0; diff --git a/core/src/main/java/com/graphhopper/routing/util/AreaIndex.java b/core/src/main/java/com/graphhopper/routing/util/AreaIndex.java index 4e42c2dae4b..5fb5f6bcb85 100644 --- a/core/src/main/java/com/graphhopper/routing/util/AreaIndex.java +++ b/core/src/main/java/com/graphhopper/routing/util/AreaIndex.java @@ -21,6 +21,7 @@ import org.locationtech.jts.geom.*; import org.locationtech.jts.geom.prep.PreparedGeometry; import org.locationtech.jts.geom.prep.PreparedGeometryFactory; +import org.locationtech.jts.geom.prep.PreparedPolygon; import org.locationtech.jts.index.strtree.STRtree; import java.util.List; diff --git a/core/src/main/java/com/graphhopper/routing/util/Bike2WeightTagParser.java b/core/src/main/java/com/graphhopper/routing/util/Bike2WeightTagParser.java deleted file mode 100644 index fabff222ed6..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/Bike2WeightTagParser.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.FetchMode; -import com.graphhopper.util.PMap; -import com.graphhopper.util.PointList; - -import static com.graphhopper.util.Helper.keepIn; - -/** - * Stores two speed values into an edge to support avoiding too much incline - * - * @author Peter Karich - */ -public class Bike2WeightTagParser extends BikeTagParser { - - public Bike2WeightTagParser() { - this(new PMap()); - } - - public Bike2WeightTagParser(PMap properties) { - super(new PMap(properties).putObject("speed_two_directions", true).putObject("name", properties.getString("name", "bike2"))); - } - - @Override - public void applyWayTags(ReaderWay way, EdgeIteratorState edge) { - PointList pl = edge.fetchWayGeometry(FetchMode.ALL); - if (!pl.is3D()) - throw new IllegalStateException(getName() + " requires elevation data to improve speed calculation based on it. Please enable it in config via e.g. graph.elevation.provider: srtm"); - - IntsRef intsRef = edge.getFlags(); - if (way.hasTag("tunnel", "yes") || way.hasTag("bridge", "yes") || way.hasTag("highway", "steps")) - // do not change speed - // note: although tunnel can have a difference in elevation it is very unlikely that the elevation data is correct for a tunnel - return; - - // Decrease the speed for ele increase (incline), and decrease the speed for ele decrease (decline). The speed-decrease - // has to be bigger (compared to the speed-increase) for the same elevation difference to simulate losing energy and avoiding hills. - // For the reverse speed this has to be the opposite but again keeping in mind that up+down difference. - double incEleSum = 0, incDist2DSum = 0, decEleSum = 0, decDist2DSum = 0; - // double prevLat = pl.getLat(0), prevLon = pl.getLon(0); - double prevEle = pl.getEle(0); - double fullDist2D = edge.getDistance(); - - // for short edges an incline makes no sense and for 0 distances could lead to NaN values for speed, see #432 - if (fullDist2D < 2) - return; - - double eleDelta = pl.getEle(pl.size() - 1) - prevEle; - if (eleDelta > 0.1) { - incEleSum = eleDelta; - incDist2DSum = fullDist2D; - } else if (eleDelta < -0.1) { - decEleSum = -eleDelta; - decDist2DSum = fullDist2D; - } - - // Calculate slop via tan(asin(height/distance)) but for rather smallish angles where we can assume tan a=a and sin a=a. - // Then calculate a factor which decreases or increases the speed. - // Do this via a simple quadratic equation where y(0)=1 and y(0.3)=1/4 for incline and y(0.3)=2 for decline - double fwdIncline = incDist2DSum > 1 ? incEleSum / incDist2DSum : 0; - double fwdDecline = decDist2DSum > 1 ? decEleSum / decDist2DSum : 0; - double restDist2D = fullDist2D - incDist2DSum - decDist2DSum; - double maxSpeed = getHighwaySpeed("cycleway"); - if (accessEnc.getBool(false, intsRef)) { - // use weighted mean so that longer incline influences speed more than shorter - double speed = avgSpeedEnc.getDecimal(false, intsRef); - double fwdFaster = 1 + 2 * keepIn(fwdDecline, 0, 0.2); - fwdFaster = fwdFaster * fwdFaster; - double fwdSlower = 1 - 5 * keepIn(fwdIncline, 0, 0.2); - fwdSlower = fwdSlower * fwdSlower; - speed = speed * (fwdSlower * incDist2DSum + fwdFaster * decDist2DSum + 1 * restDist2D) / fullDist2D; - setSpeed(false, intsRef, keepIn(speed, PUSHING_SECTION_SPEED / 2.0, maxSpeed)); - } - - if (accessEnc.getBool(true, intsRef)) { - double speedReverse = avgSpeedEnc.getDecimal(true, intsRef); - double bwFaster = 1 + 2 * keepIn(fwdIncline, 0, 0.2); - bwFaster = bwFaster * bwFaster; - double bwSlower = 1 - 5 * keepIn(fwdDecline, 0, 0.2); - bwSlower = bwSlower * bwSlower; - speedReverse = speedReverse * (bwFaster * incDist2DSum + bwSlower * decDist2DSum + 1 * restDist2D) / fullDist2D; - setSpeed(true, intsRef, keepIn(speedReverse, PUSHING_SECTION_SPEED / 2.0, maxSpeed)); - } - edge.setFlags(intsRef); - } - -} diff --git a/core/src/main/java/com/graphhopper/routing/util/BikeCommonTagParser.java b/core/src/main/java/com/graphhopper/routing/util/BikeCommonTagParser.java deleted file mode 100644 index 40e48264dbe..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/BikeCommonTagParser.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.weighting.PriorityWeighting; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.Helper; - -import java.util.*; - -import static com.graphhopper.routing.ev.RouteNetwork.*; -import static com.graphhopper.routing.util.EncodingManager.getKey; -import static com.graphhopper.routing.util.PriorityCode.*; - -/** - * Defines bit layout of bicycles (not motorcycles) for speed, access and relations (network). - * - * @author Peter Karich - * @author Nop - * @author ratrun - */ -abstract public class BikeCommonTagParser extends VehicleTagParser { - - protected static final int PUSHING_SECTION_SPEED = 4; - // Pushing section highways are parts where you need to get off your bike and push it (German: Schiebestrecke) - protected final HashSet pushingSectionsHighways = new HashSet<>(); - protected final HashSet oppositeLanes = new HashSet<>(); - protected final Set preferHighwayTags = new HashSet<>(); - protected final Set avoidHighwayTags = new HashSet<>(); - protected final Set unpavedSurfaceTags = new HashSet<>(); - private final Map trackTypeSpeeds = new HashMap<>(); - private final Map surfaceSpeeds = new HashMap<>(); - protected static final double smoothnessFactorPushingSectionThreshold = 0.3d; - private final Map smoothnessFactor = new HashMap<>(); - private final Map highwaySpeeds = new HashMap<>(); - protected final DecimalEncodedValue priorityEnc; - // Car speed limit which switches the preference from UNCHANGED to AVOID_IF_POSSIBLE - private int avoidSpeedLimit; - EnumEncodedValue bikeRouteEnc; - EnumEncodedValue smoothnessEnc; - Map routeMap = new HashMap<>(); - - // This is the specific bicycle class - private String classBicycleKey; - - protected BikeCommonTagParser(String name, int speedBits, double speedFactor, int maxTurnCosts, boolean speedTwoDirections) { - super(name, speedBits, speedFactor, speedTwoDirections, maxTurnCosts); - - priorityEnc = new DecimalEncodedValueImpl(getKey(name, "priority"), 4, PriorityCode.getFactor(1), false); - - restrictedValues.add("agricultural"); - restrictedValues.add("forestry"); - restrictedValues.add("no"); - restrictedValues.add("restricted"); - restrictedValues.add("delivery"); - restrictedValues.add("military"); - restrictedValues.add("emergency"); - restrictedValues.add("private"); - - intendedValues.add("yes"); - intendedValues.add("designated"); - intendedValues.add("official"); - intendedValues.add("permissive"); - - oppositeLanes.add("opposite"); - oppositeLanes.add("opposite_lane"); - oppositeLanes.add("opposite_track"); - - barriers.add("fence"); - - unpavedSurfaceTags.add("unpaved"); - unpavedSurfaceTags.add("gravel"); - unpavedSurfaceTags.add("ground"); - unpavedSurfaceTags.add("dirt"); - unpavedSurfaceTags.add("grass"); - unpavedSurfaceTags.add("compacted"); - unpavedSurfaceTags.add("earth"); - unpavedSurfaceTags.add("fine_gravel"); - unpavedSurfaceTags.add("grass_paver"); - unpavedSurfaceTags.add("ice"); - unpavedSurfaceTags.add("mud"); - unpavedSurfaceTags.add("salt"); - unpavedSurfaceTags.add("sand"); - unpavedSurfaceTags.add("wood"); - - maxPossibleSpeed = avgSpeedEnc.getNextStorableValue(30); - - setTrackTypeSpeed("grade1", 18); // paved - setTrackTypeSpeed("grade2", 12); // now unpaved ... - setTrackTypeSpeed("grade3", 8); - setTrackTypeSpeed("grade4", 6); - setTrackTypeSpeed("grade5", 4); // like sand/grass - - setSurfaceSpeed("paved", 18); - setSurfaceSpeed("asphalt", 18); - setSurfaceSpeed("cobblestone", 8); - setSurfaceSpeed("cobblestone:flattened", 10); - setSurfaceSpeed("sett", 10); - setSurfaceSpeed("concrete", 18); - setSurfaceSpeed("concrete:lanes", 16); - setSurfaceSpeed("concrete:plates", 16); - setSurfaceSpeed("paving_stones", 12); - setSurfaceSpeed("paving_stones:30", 12); - setSurfaceSpeed("unpaved", 14); - setSurfaceSpeed("compacted", 16); - setSurfaceSpeed("dirt", 10); - setSurfaceSpeed("earth", 12); - setSurfaceSpeed("fine_gravel", 18); - setSurfaceSpeed("grass", 8); - setSurfaceSpeed("grass_paver", 8); - setSurfaceSpeed("gravel", 12); - setSurfaceSpeed("ground", 12); - setSurfaceSpeed("ice", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("metal", 10); - setSurfaceSpeed("mud", 10); - setSurfaceSpeed("pebblestone", 16); - setSurfaceSpeed("salt", 6); - setSurfaceSpeed("sand", 6); - setSurfaceSpeed("wood", 6); - - setHighwaySpeed("living_street", 6); - setHighwaySpeed("steps", PUSHING_SECTION_SPEED / 2); - avoidHighwayTags.add("steps"); - - final int CYCLEWAY_SPEED = 18; // Make sure cycleway and path use same speed value, see #634 - setHighwaySpeed("cycleway", CYCLEWAY_SPEED); - setHighwaySpeed("path", 10); - setHighwaySpeed("footway", 6); - setHighwaySpeed("platform", 6); - setHighwaySpeed("pedestrian", 6); - setHighwaySpeed("track", 12); - setHighwaySpeed("service", 14); - setHighwaySpeed("residential", 18); - // no other highway applies: - setHighwaySpeed("unclassified", 16); - // unknown road: - setHighwaySpeed("road", 12); - - setHighwaySpeed("trunk", 18); - setHighwaySpeed("trunk_link", 18); - setHighwaySpeed("primary", 18); - setHighwaySpeed("primary_link", 18); - setHighwaySpeed("secondary", 18); - setHighwaySpeed("secondary_link", 18); - setHighwaySpeed("tertiary", 18); - setHighwaySpeed("tertiary_link", 18); - - // special case see tests and #191 - setHighwaySpeed("motorway", 18); - setHighwaySpeed("motorway_link", 18); - avoidHighwayTags.add("motorway"); - avoidHighwayTags.add("motorway_link"); - - setHighwaySpeed("bridleway", 6); - avoidHighwayTags.add("bridleway"); - - routeMap.put(INTERNATIONAL, BEST.getValue()); - routeMap.put(NATIONAL, BEST.getValue()); - routeMap.put(REGIONAL, VERY_NICE.getValue()); - routeMap.put(LOCAL, PREFER.getValue()); - - setSmoothnessSpeedFactor(Smoothness.MISSING, 1.0d); - setSmoothnessSpeedFactor(Smoothness.OTHER, 0.7d); - - setAvoidSpeedLimit(71); - } - - @Override - public TransportationMode getTransportationMode() { - return TransportationMode.BIKE; - } - - @Override - public void createEncodedValues(List registerNewEncodedValue) { - super.createEncodedValues(registerNewEncodedValue); - registerNewEncodedValue.add(priorityEnc); - - bikeRouteEnc = getEnumEncodedValue(RouteNetwork.key("bike"), RouteNetwork.class); - smoothnessEnc = getEnumEncodedValue(Smoothness.KEY, Smoothness.class); - } - - @Override - public EncodingManager.Access getAccess(ReaderWay way) { - String highwayValue = way.getTag("highway"); - if (highwayValue == null) { - EncodingManager.Access access = EncodingManager.Access.CAN_SKIP; - - if (way.hasTag("route", ferries)) { - // if bike is NOT explicitly tagged allow bike but only if foot is not specified either - String bikeTag = way.getTag("bicycle"); - if (bikeTag == null && !way.hasTag("foot") || intendedValues.contains(bikeTag)) - access = EncodingManager.Access.FERRY; - } - - // special case not for all acceptedRailways, only platform - if (way.hasTag("railway", "platform")) - access = EncodingManager.Access.WAY; - - if (way.hasTag("man_made", "pier")) - access = EncodingManager.Access.WAY; - - if (!access.canSkip()) { - if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) - return EncodingManager.Access.CAN_SKIP; - return access; - } - - return EncodingManager.Access.CAN_SKIP; - } - - if (!highwaySpeeds.containsKey(highwayValue)) - return EncodingManager.Access.CAN_SKIP; - - String sacScale = way.getTag("sac_scale"); - if (sacScale != null) { - if (!isSacScaleAllowed(sacScale)) - return EncodingManager.Access.CAN_SKIP; - } - - // use the way if it is tagged for bikes - if (way.hasTag("bicycle", intendedValues) || - way.hasTag("bicycle", "dismount") || - way.hasTag("highway", "cycleway")) - return EncodingManager.Access.WAY; - - // accept only if explicitly tagged for bike usage - if ("motorway".equals(highwayValue) || "motorway_link".equals(highwayValue) || "bridleway".equals(highwayValue)) - return EncodingManager.Access.CAN_SKIP; - - if (way.hasTag("motorroad", "yes")) - return EncodingManager.Access.CAN_SKIP; - - // do not use fords with normal bikes, flagged fords are in included above - if (isBlockFords() && (way.hasTag("highway", "ford") || way.hasTag("ford"))) - return EncodingManager.Access.CAN_SKIP; - - // check access restrictions - if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) - return EncodingManager.Access.CAN_SKIP; - - if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way)) - return EncodingManager.Access.CAN_SKIP; - else - return EncodingManager.Access.WAY; - } - - boolean isSacScaleAllowed(String sacScale) { - // other scales are nearly impossible by an ordinary bike, see http://wiki.openstreetmap.org/wiki/Key:sac_scale - return "hiking".equals(sacScale); - } - - /** - * @param way needed to retrieve tags - * @param speed speed guessed e.g. from the road type or other tags - * @return The assumed average speed. - */ - protected double applyMaxSpeed(ReaderWay way, double speed) { - double maxSpeed = getMaxSpeed(way); - // We strictly obey speed limits, see #600 - if (isValidSpeed(maxSpeed) && speed > maxSpeed) { - return maxSpeed; - } - if (isValidSpeed(speed) && speed > maxPossibleSpeed) - return maxPossibleSpeed; - return speed; - } - - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way) { - EncodingManager.Access access = getAccess(way); - if (access.canSkip()) - return edgeFlags; - - Integer priorityFromRelation = routeMap.get(bikeRouteEnc.getEnum(false, edgeFlags)); - double wayTypeSpeed = getSpeed(way); - if (!access.isFerry()) { - wayTypeSpeed = applyMaxSpeed(way, wayTypeSpeed); - Smoothness smoothness = smoothnessEnc.getEnum(false, edgeFlags); - if (smoothness != Smoothness.MISSING) { - // smoothness handling: Multiply speed with smoothnessFactor - double smoothnessSpeedFactor = smoothnessFactor.get(smoothness); - wayTypeSpeed = (smoothnessSpeedFactor <= smoothnessFactorPushingSectionThreshold) ? - PUSHING_SECTION_SPEED : Math.round(smoothnessSpeedFactor * wayTypeSpeed); - } - avgSpeedEnc.setDecimal(false, edgeFlags, wayTypeSpeed); - if (avgSpeedEnc.isStoreTwoDirections()) - avgSpeedEnc.setDecimal(true, edgeFlags, wayTypeSpeed); - handleAccess(edgeFlags, way); - } else { - double ferrySpeed = ferrySpeedCalc.getSpeed(way); - avgSpeedEnc.setDecimal(false, edgeFlags, ferrySpeed); - if (avgSpeedEnc.isStoreTwoDirections()) - avgSpeedEnc.setDecimal(true, edgeFlags, ferrySpeed); - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - priorityFromRelation = SLIGHT_AVOID.getValue(); - } - - priorityEnc.setDecimal(false, edgeFlags, PriorityCode.getValue(handlePriority(way, wayTypeSpeed, priorityFromRelation))); - return edgeFlags; - } - - int getSpeed(ReaderWay way) { - int speed = PUSHING_SECTION_SPEED; - String highwayTag = way.getTag("highway"); - Integer highwaySpeed = highwaySpeeds.get(highwayTag); - - // Under certain conditions we need to increase the speed of pushing sections to the speed of a "highway=cycleway" - if (way.hasTag("highway", pushingSectionsHighways) - && ((way.hasTag("foot", "yes") && way.hasTag("segregated", "yes")) - || (way.hasTag("bicycle", intendedValues)))) - highwaySpeed = getHighwaySpeed("cycleway"); - - String s = way.getTag("surface"); - Integer surfaceSpeed = 0; - if (!Helper.isEmpty(s)) { - surfaceSpeed = surfaceSpeeds.get(s); - if (surfaceSpeed != null) { - speed = surfaceSpeed; - // boost handling for good surfaces but avoid boosting if pushing section - if (highwaySpeed != null && surfaceSpeed > highwaySpeed) { - if (pushingSectionsHighways.contains(highwayTag)) - speed = highwaySpeed; - else - speed = surfaceSpeed; - } - } - } else { - String tt = way.getTag("tracktype"); - if (!Helper.isEmpty(tt)) { - Integer tInt = trackTypeSpeeds.get(tt); - if (tInt != null) - speed = tInt; - } else if (highwaySpeed != null) { - if (!way.hasTag("service")) - speed = highwaySpeed; - else - speed = highwaySpeeds.get("living_street"); - } - } - - // Until now we assumed that the way is no pushing section - // Now we check that, but only in case that our speed computed so far is bigger compared to the PUSHING_SECTION_SPEED - if (speed > PUSHING_SECTION_SPEED - && (way.hasTag("highway", pushingSectionsHighways) || way.hasTag("bicycle", "dismount"))) { - if (!way.hasTag("bicycle", intendedValues)) { - // Here we set the speed for pushing sections and set speed for steps as even lower: - speed = way.hasTag("highway", "steps") ? PUSHING_SECTION_SPEED / 2 : PUSHING_SECTION_SPEED; - } else if (way.hasTag("bicycle", "designated") || way.hasTag("bicycle", "official") || - way.hasTag("segregated", "yes") || way.hasTag("bicycle", "yes")) { - // Here we handle the cases where the OSM tagging results in something similar to "highway=cycleway" - if (way.hasTag("segregated", "yes")) - speed = highwaySpeeds.get("cycleway"); - else - speed = way.hasTag("bicycle", "yes") ? 10 : highwaySpeeds.get("cycleway"); - - // overwrite our speed again in case we have a valid surface speed and if it is smaller as computed so far - if ((surfaceSpeed > 0) && (surfaceSpeed < speed)) - speed = surfaceSpeed; - } - } - return speed; - } - - /** - * In this method we prefer cycleways or roads with designated bike access and avoid big roads - * or roads with trams or pedestrian. - * - * @return new priority based on priorityFromRelation and on the tags in ReaderWay. - */ - int handlePriority(ReaderWay way, double wayTypeSpeed, Integer priorityFromRelation) { - TreeMap weightToPrioMap = new TreeMap<>(); - if (priorityFromRelation == null) - weightToPrioMap.put(0d, UNCHANGED.getValue()); - else - weightToPrioMap.put(110d, priorityFromRelation); - - collect(way, wayTypeSpeed, weightToPrioMap); - - // pick priority with biggest order value - return weightToPrioMap.lastEntry().getValue(); - } - - // Conversion of class value to priority. See http://wiki.openstreetmap.org/wiki/Class:bicycle - private PriorityCode convertClassValueToPriority(String tagvalue) { - int classvalue; - try { - classvalue = Integer.parseInt(tagvalue); - } catch (NumberFormatException e) { - return UNCHANGED; - } - - switch (classvalue) { - case 3: - return BEST; - case 2: - return VERY_NICE; - case 1: - return PREFER; - case 0: - return UNCHANGED; - case -1: - return SLIGHT_AVOID; - case -2: - return AVOID; - case -3: - return AVOID_MORE; - default: - return UNCHANGED; - } - } - - /** - * @param weightToPrioMap associate a weight with every priority. This sorted map allows - * subclasses to 'insert' more important priorities as well as overwrite determined priorities. - */ - void collect(ReaderWay way, double wayTypeSpeed, TreeMap weightToPrioMap) { - String service = way.getTag("service"); - String highway = way.getTag("highway"); - if (way.hasTag("bicycle", "designated") || way.hasTag("bicycle", "official")) { - if ("path".equals(highway)) - weightToPrioMap.put(100d, VERY_NICE.getValue()); - else - weightToPrioMap.put(100d, PREFER.getValue()); - } - - if ("cycleway".equals(highway)) { - if (way.hasTag("foot", intendedValues) && !way.hasTag("segregated", "yes")) - weightToPrioMap.put(100d, PREFER.getValue()); - else - weightToPrioMap.put(100d, VERY_NICE.getValue()); - } - - double maxSpeed = getMaxSpeed(way); - if (preferHighwayTags.contains(highway) || (isValidSpeed(maxSpeed) && maxSpeed <= 30)) { - if (!isValidSpeed(maxSpeed) || maxSpeed < avoidSpeedLimit) { - weightToPrioMap.put(40d, PREFER.getValue()); - if (way.hasTag("tunnel", intendedValues)) - weightToPrioMap.put(40d, UNCHANGED.getValue()); - } - } else if (avoidHighwayTags.contains(highway) - || isValidSpeed(maxSpeed) && maxSpeed >= avoidSpeedLimit && !"track".equals(highway)) { - weightToPrioMap.put(50d, AVOID.getValue()); - if (way.hasTag("tunnel", intendedValues)) - weightToPrioMap.put(50d, AVOID_MORE.getValue()); - } - - String cycleway = way.getFirstPriorityTag(Arrays.asList("cycleway", "cycleway:left", "cycleway:right")); - if (Arrays.asList("lane", "shared_lane", "share_busway", "shoulder").contains(cycleway)) { - weightToPrioMap.put(100d, UNCHANGED.getValue()); - } else if ("track".equals(cycleway)) { - weightToPrioMap.put(100d, PREFER.getValue()); - } - - if (way.hasTag("bicycle", "use_sidepath")) { - weightToPrioMap.put(100d, REACH_DESTINATION.getValue()); - } - - if (pushingSectionsHighways.contains(highway) - || "parking_aisle".equals(service)) { - int pushingSectionPrio = SLIGHT_AVOID.getValue(); - if (way.hasTag("bicycle", "yes") || way.hasTag("bicycle", "permissive")) - pushingSectionPrio = PREFER.getValue(); - if (way.hasTag("bicycle", "designated") || way.hasTag("bicycle", "official")) - pushingSectionPrio = VERY_NICE.getValue(); - if (way.hasTag("foot", "yes")) { - pushingSectionPrio = Math.max(pushingSectionPrio - 1, BAD.getValue()); - if (way.hasTag("segregated", "yes")) - pushingSectionPrio = Math.min(pushingSectionPrio + 1, BEST.getValue()); - } - weightToPrioMap.put(100d, pushingSectionPrio); - } - - if (way.hasTag("railway", "tram")) - weightToPrioMap.put(50d, AVOID_MORE.getValue()); - - String classBicycleValue = way.getTag(classBicycleKey); - if (classBicycleValue != null) { - // We assume that humans are better in classifying preferences compared to our algorithm above -> weight = 100 - weightToPrioMap.put(100d, convertClassValueToPriority(classBicycleValue).getValue()); - } else { - String classBicycle = way.getTag("class:bicycle"); - if (classBicycle != null) - weightToPrioMap.put(100d, convertClassValueToPriority(classBicycle).getValue()); - } - - // Increase the priority for scenic routes or in case that maxspeed limits our average speed as compensation. See #630 - if (way.hasTag("scenic", "yes") || maxSpeed > 0 && maxSpeed < wayTypeSpeed) { - if (weightToPrioMap.lastEntry().getValue() < BEST.getValue()) - // Increase the prio by one step - weightToPrioMap.put(110d, weightToPrioMap.lastEntry().getValue() + 1); - } - } - - protected void handleAccess(IntsRef edgeFlags, ReaderWay way) { - // handle oneways - // oneway=-1 requires special handling - boolean isOneway = way.hasTag("oneway", oneways) && !way.hasTag("oneway", "-1") && !way.hasTag("bicycle:backward", intendedValues) - || way.hasTag("oneway", "-1") && !way.hasTag("bicycle:forward", intendedValues) - || way.hasTag("oneway:bicycle", oneways) - || way.hasTag("vehicle:backward", restrictedValues) && !way.hasTag("bicycle:forward", intendedValues) - || way.hasTag("vehicle:forward", restrictedValues) && !way.hasTag("bicycle:backward", intendedValues) - || way.hasTag("bicycle:forward", restrictedValues) - || way.hasTag("bicycle:backward", restrictedValues); - - if ((isOneway || roundaboutEnc.getBool(false, edgeFlags)) - && !way.hasTag("oneway:bicycle", "no") - && !way.hasTag("cycleway", oppositeLanes) - && !way.hasTag("cycleway:left", oppositeLanes) - && !way.hasTag("cycleway:right", oppositeLanes) - && !way.hasTag("cycleway:left:oneway", "-1") - && !way.hasTag("cycleway:right:oneway", "-1")) { - boolean isBackward = way.hasTag("oneway", "-1") - || way.hasTag("oneway:bicycle", "-1") - || way.hasTag("vehicle:forward", restrictedValues) - || way.hasTag("bicycle:forward", restrictedValues); - accessEnc.setBool(isBackward, edgeFlags, true); - - } else { - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - } - } - - void setHighwaySpeed(String highway, int speed) { - highwaySpeeds.put(highway, speed); - } - - int getHighwaySpeed(String key) { - return highwaySpeeds.get(key); - } - - void setTrackTypeSpeed(String tracktype, int speed) { - trackTypeSpeeds.put(tracktype, speed); - } - - void setSurfaceSpeed(String surface, int speed) { - surfaceSpeeds.put(surface, speed); - } - - void setSmoothnessSpeedFactor(Smoothness smoothness, double speedfactor) { - smoothnessFactor.put(smoothness, speedfactor); - } - - void addPushingSection(String highway) { - pushingSectionsHighways.add(highway); - } - - @Override - public boolean supports(Class feature) { - if (super.supports(feature)) - return true; - - return PriorityWeighting.class.isAssignableFrom(feature); - } - - void setAvoidSpeedLimit(int limit) { - avoidSpeedLimit = limit; - } - - void setSpecificClassBicycle(String subkey) { - classBicycleKey = "class:bicycle:" + subkey; - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/BikeTagParser.java b/core/src/main/java/com/graphhopper/routing/util/BikeTagParser.java deleted file mode 100644 index 07234052fc0..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/BikeTagParser.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.util.PMap; - -import static com.graphhopper.routing.ev.Smoothness.*; - -/** - * Specifies the settings for cycletouring/trekking - * - * @author ratrun - * @author Peter Karich - */ -public class BikeTagParser extends BikeCommonTagParser { - public BikeTagParser() { - this("bike"); - } - - public BikeTagParser(String name) { - this(name, 4, 2, 0, false); - } - - public BikeTagParser(PMap properties) { - this(properties.getString("name", "bike"), - properties.getInt("speed_bits", 4), - properties.getInt("speed_factor", 2), - properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0), - properties.getBool("speed_two_directions", false)); - - blockPrivate(properties.getBool("block_private", true)); - blockFords(properties.getBool("block_fords", false)); - } - - public BikeTagParser(int speedBits, double speedFactor, int maxTurnCosts, boolean speedTwoDirections) { - this("bike", speedBits, speedFactor, maxTurnCosts, speedTwoDirections); - } - - public BikeTagParser(String name, int speedBits, double speedFactor, int maxTurnCosts, boolean speedTwoDirections) { - super(name, speedBits, speedFactor, maxTurnCosts, speedTwoDirections); - addPushingSection("path"); - addPushingSection("footway"); - addPushingSection("pedestrian"); - addPushingSection("steps"); - addPushingSection("platform"); - - avoidHighwayTags.add("trunk"); - avoidHighwayTags.add("trunk_link"); - avoidHighwayTags.add("primary"); - avoidHighwayTags.add("primary_link"); - avoidHighwayTags.add("secondary"); - avoidHighwayTags.add("secondary_link"); - - // preferHighwayTags.add("road"); - preferHighwayTags.add("service"); - preferHighwayTags.add("tertiary"); - preferHighwayTags.add("tertiary_link"); - preferHighwayTags.add("residential"); - preferHighwayTags.add("unclassified"); - - setSmoothnessSpeedFactor(EXCELLENT, 1.1d); - setSmoothnessSpeedFactor(GOOD, 1.0d); - setSmoothnessSpeedFactor(INTERMEDIATE, 0.9d); - setSmoothnessSpeedFactor(BAD, 0.7d); - setSmoothnessSpeedFactor(VERY_BAD, 0.6d); - setSmoothnessSpeedFactor(HORRIBLE, 0.5d); - setSmoothnessSpeedFactor(VERY_HORRIBLE, 0.4d); - // SmoothnessSpeed <= smoothnessFactorPushingSectionThreshold gets mapped to speed PUSHING_SECTION_SPEED - setSmoothnessSpeedFactor(IMPASSABLE, smoothnessFactorPushingSectionThreshold); - - barriers.add("kissing_gate"); - barriers.add("stile"); - barriers.add("turnstile"); - - setSpecificClassBicycle("touring"); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/CarTagParser.java b/core/src/main/java/com/graphhopper/routing/util/CarTagParser.java deleted file mode 100644 index 212adbe8378..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/CarTagParser.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.Helper; -import com.graphhopper.util.PMap; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Defines bit layout for cars. (speed, access, ferries, ...) - * - * @author Peter Karich - * @author Nop - */ -public class CarTagParser extends VehicleTagParser { - protected final Map trackTypeSpeedMap = new HashMap<>(); - protected final Set badSurfaceSpeedMap = new HashSet<>(); - // This value determines the maximal possible on roads with bad surfaces - protected int badSurfaceSpeed; - - /** - * A map which associates string to speed. Get some impression: - * http://www.itoworld.com/map/124#fullscreen - * http://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Maxspeed - */ - protected final Map defaultSpeedMap = new HashMap<>(); - - public CarTagParser() { - this(new PMap()); - } - - public CarTagParser(int speedBits, double speedFactor, int maxTurnCosts) { - this("car", speedBits, speedFactor, maxTurnCosts); - } - - public CarTagParser(String name, int speedBits, double speedFactor, int maxTurnCosts) { - this(name, speedBits, speedFactor, maxTurnCosts, false); - } - - public CarTagParser(int speedBits, double speedFactor, int maxTurnCosts, boolean speedTwoDirections) { - this("car", speedBits, speedFactor, maxTurnCosts, speedTwoDirections); - } - - public CarTagParser(String name, int speedBits, double speedFactor, int maxTurnCosts, boolean speedTwoDirections) { - this(new PMap().putObject("name", name).putObject("speed_bits", speedBits).putObject("speed_factor", speedFactor). - putObject("max_turn_costs", maxTurnCosts).putObject("speed_two_directions", speedTwoDirections)); - } - - public CarTagParser(PMap properties) { - super(properties.getString("name", "car"), - properties.getInt("speed_bits", 5), - properties.getDouble("speed_factor", 5), - properties.getBool("speed_two_directions", false), - properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0)); - - restrictedValues.add("agricultural"); - restrictedValues.add("forestry"); - restrictedValues.add("no"); - restrictedValues.add("restricted"); - restrictedValues.add("delivery"); - restrictedValues.add("military"); - restrictedValues.add("emergency"); - restrictedValues.add("private"); - - blockPrivate(properties.getBool("block_private", true)); - blockFords(properties.getBool("block_fords", false)); - - intendedValues.add("yes"); - intendedValues.add("designated"); - intendedValues.add("permissive"); - - barriers.add("kissing_gate"); - barriers.add("fence"); - barriers.add("bollard"); - barriers.add("stile"); - barriers.add("turnstile"); - barriers.add("cycle_barrier"); - barriers.add("motorcycle_barrier"); - barriers.add("block"); - barriers.add("bus_trap"); - barriers.add("sump_buster"); - - badSurfaceSpeedMap.add("cobblestone"); - badSurfaceSpeedMap.add("grass_paver"); - badSurfaceSpeedMap.add("gravel"); - badSurfaceSpeedMap.add("sand"); - badSurfaceSpeedMap.add("paving_stones"); - badSurfaceSpeedMap.add("dirt"); - badSurfaceSpeedMap.add("ground"); - badSurfaceSpeedMap.add("grass"); - badSurfaceSpeedMap.add("unpaved"); - badSurfaceSpeedMap.add("compacted"); - - // autobahn - defaultSpeedMap.put("motorway", 100); - defaultSpeedMap.put("motorway_link", 70); - defaultSpeedMap.put("motorroad", 90); - // bundesstraße - defaultSpeedMap.put("trunk", 70); - defaultSpeedMap.put("trunk_link", 65); - // linking bigger town - defaultSpeedMap.put("primary", 65); - defaultSpeedMap.put("primary_link", 60); - // linking towns + villages - defaultSpeedMap.put("secondary", 60); - defaultSpeedMap.put("secondary_link", 50); - // streets without middle line separation - defaultSpeedMap.put("tertiary", 50); - defaultSpeedMap.put("tertiary_link", 40); - defaultSpeedMap.put("unclassified", 30); - defaultSpeedMap.put("residential", 30); - // spielstraße - defaultSpeedMap.put("living_street", 5); - defaultSpeedMap.put("service", 20); - // unknown road - defaultSpeedMap.put("road", 20); - // forestry stuff - defaultSpeedMap.put("track", 15); - - trackTypeSpeedMap.put("grade1", 20); // paved - trackTypeSpeedMap.put("grade2", 15); // now unpaved - gravel mixed with ... - trackTypeSpeedMap.put("grade3", 10); // ... hard and soft materials - trackTypeSpeedMap.put(null, defaultSpeedMap.get("track")); - - // limit speed on bad surfaces to 30 km/h - badSurfaceSpeed = 30; - maxPossibleSpeed = avgSpeedEnc.getNextStorableValue(properties.getDouble("max_speed", 140)); - } - - @Override - public TransportationMode getTransportationMode() { - return TransportationMode.CAR; - } - - protected double getSpeed(ReaderWay way) { - String highwayValue = way.getTag("highway"); - if (!Helper.isEmpty(highwayValue) && way.hasTag("motorroad", "yes") - && !"motorway".equals(highwayValue) && !"motorway_link".equals(highwayValue)) { - highwayValue = "motorroad"; - } - Integer speed = defaultSpeedMap.get(highwayValue); - if (speed == null) - throw new IllegalStateException(getName() + ", no speed found for: " + highwayValue + ", tags: " + way); - - if (highwayValue.equals("track")) { - String tt = way.getTag("tracktype"); - if (!Helper.isEmpty(tt)) { - Integer tInt = trackTypeSpeedMap.get(tt); - if (tInt != null) - speed = tInt; - } - } - - return speed; - } - - @Override - public EncodingManager.Access getAccess(ReaderWay way) { - // TODO: Ferries have conditionals, like opening hours or are closed during some time in the year - String highwayValue = way.getTag("highway"); - String firstValue = way.getFirstPriorityTag(restrictions); - if (highwayValue == null) { - if (way.hasTag("route", ferries)) { - if (restrictedValues.contains(firstValue)) - return EncodingManager.Access.CAN_SKIP; - if (intendedValues.contains(firstValue) || - // implied default is allowed only if foot and bicycle is not specified: - firstValue.isEmpty() && !way.hasTag("foot") && !way.hasTag("bicycle")) - return EncodingManager.Access.FERRY; - } - return EncodingManager.Access.CAN_SKIP; - } - - if ("service".equals(highwayValue) && "emergency_access".equals(way.getTag("service"))) { - return EncodingManager.Access.CAN_SKIP; - } - - if ("track".equals(highwayValue) && trackTypeSpeedMap.get(way.getTag("tracktype")) == null) - return EncodingManager.Access.CAN_SKIP; - - if (!defaultSpeedMap.containsKey(highwayValue)) - return EncodingManager.Access.CAN_SKIP; - - if (way.hasTag("impassable", "yes") || way.hasTag("status", "impassable")) - return EncodingManager.Access.CAN_SKIP; - - // multiple restrictions needs special handling compared to foot and bike, see also motorcycle - if (!firstValue.isEmpty()) { - if (restrictedValues.contains(firstValue) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) - return EncodingManager.Access.CAN_SKIP; - if (intendedValues.contains(firstValue)) - return EncodingManager.Access.WAY; - } - - // do not drive street cars into fords - if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) - return EncodingManager.Access.CAN_SKIP; - - if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way)) - return EncodingManager.Access.CAN_SKIP; - else - return EncodingManager.Access.WAY; - } - - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way) { - EncodingManager.Access access = getAccess(way); - if (access.canSkip()) - return edgeFlags; - - if (!access.isFerry()) { - // get assumed speed from highway type - double speed = getSpeed(way); - speed = applyMaxSpeed(way, speed); - - speed = applyBadSurfaceSpeed(way, speed); - - setSpeed(false, edgeFlags, speed); - if (avgSpeedEnc.isStoreTwoDirections()) - setSpeed(true, edgeFlags, speed); - - boolean isRoundabout = roundaboutEnc.getBool(false, edgeFlags); - if (isOneway(way) || isRoundabout) { - if (isForwardOneway(way)) - accessEnc.setBool(false, edgeFlags, true); - if (isBackwardOneway(way)) - accessEnc.setBool(true, edgeFlags, true); - } else { - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - } - - } else { - double ferrySpeed = ferrySpeedCalc.getSpeed(way); - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - setSpeed(false, edgeFlags, ferrySpeed); - if (avgSpeedEnc.isStoreTwoDirections()) - setSpeed(true, edgeFlags, ferrySpeed); - } - - return edgeFlags; - } - - /** - * @param way needed to retrieve tags - * @param speed speed guessed e.g. from the road type or other tags - * @return The assumed speed. - */ - protected double applyMaxSpeed(ReaderWay way, double speed) { - double maxSpeed = getMaxSpeed(way); - // We obey speed limits - if (isValidSpeed(maxSpeed)) { - // We assume that the average speed is 90% of the allowed maximum - return maxSpeed * 0.9; - } - return speed; - } - - /** - * make sure that isOneway is called before - */ - protected boolean isBackwardOneway(ReaderWay way) { - return way.hasTag("oneway", "-1") - || way.hasTag("vehicle:forward", restrictedValues) - || way.hasTag("motor_vehicle:forward", restrictedValues); - } - - /** - * make sure that isOneway is called before - */ - protected boolean isForwardOneway(ReaderWay way) { - return !way.hasTag("oneway", "-1") - && !way.hasTag("vehicle:forward", restrictedValues) - && !way.hasTag("motor_vehicle:forward", restrictedValues); - } - - protected boolean isOneway(ReaderWay way) { - return way.hasTag("oneway", oneways) - || way.hasTag("vehicle:backward", restrictedValues) - || way.hasTag("vehicle:forward", restrictedValues) - || way.hasTag("motor_vehicle:backward", restrictedValues) - || way.hasTag("motor_vehicle:forward", restrictedValues); - } - - /** - * @param way needed to retrieve tags - * @param speed speed guessed e.g. from the road type or other tags - * @return The assumed speed - */ - protected double applyBadSurfaceSpeed(ReaderWay way, double speed) { - // limit speed if bad surface - if (badSurfaceSpeed > 0 && isValidSpeed(speed) && speed > badSurfaceSpeed && way.hasTag("surface", badSurfaceSpeedMap)) - speed = badSurfaceSpeed; - return speed; - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/CurvatureCalculator.java b/core/src/main/java/com/graphhopper/routing/util/CurvatureCalculator.java new file mode 100644 index 00000000000..fdc864641d8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/CurvatureCalculator.java @@ -0,0 +1,34 @@ +package com.graphhopper.routing.util; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.util.parsers.TagParser; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.DistanceCalcEarth; +import com.graphhopper.util.PointList; + +public class CurvatureCalculator implements TagParser { + + private final DecimalEncodedValue curvatureEnc; + + public CurvatureCalculator(DecimalEncodedValue curvatureEnc) { + this.curvatureEnc = curvatureEnc; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + PointList pointList = way.getTag("point_list", null); + Double edgeDistance = way.getTag("edge_distance", null); + if (pointList != null && edgeDistance != null && !pointList.isEmpty()) { + double beeline = DistanceCalcEarth.DIST_EARTH.calcDist(pointList.getLat(0), pointList.getLon(0), + pointList.getLat(pointList.size() - 1), pointList.getLon(pointList.size() - 1)); + // For now keep the formula simple. Maybe later use quadratic value as it might improve the "resolution" + double curvature = beeline / edgeDistance; + curvatureEnc.setDecimal(false, edgeId, edgeIntAccess, Math.max(curvatureEnc.getMinStorableDecimal(), Math.min(curvatureEnc.getMaxStorableDecimal(), + curvature))); + } else { + curvatureEnc.setDecimal(false, edgeId, edgeIntAccess, 1.0); + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/CustomArea.java b/core/src/main/java/com/graphhopper/routing/util/CustomArea.java index dd26a62b8e0..9cc701de3b2 100644 --- a/core/src/main/java/com/graphhopper/routing/util/CustomArea.java +++ b/core/src/main/java/com/graphhopper/routing/util/CustomArea.java @@ -18,9 +18,7 @@ package com.graphhopper.routing.util; import com.graphhopper.util.JsonFeature; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geom.util.PolygonExtracter; +import org.locationtech.jts.geom.*; import java.util.ArrayList; import java.util.List; @@ -29,13 +27,14 @@ public class CustomArea implements AreaIndex.Area { private final Map properties; private final List borders; + private final double area; public static CustomArea fromJsonFeature(JsonFeature j) { List borders = new ArrayList<>(); for (int i = 0; i < j.getGeometry().getNumGeometries(); i++) { Geometry geometry = j.getGeometry().getGeometryN(i); if (geometry instanceof Polygon) { - PolygonExtracter.getPolygons(geometry, borders); + borders.add((Polygon) geometry); } else { throw new IllegalArgumentException("Custom area features must be of type 'Polygon', but was: " + geometry.getClass().getSimpleName()); } @@ -46,6 +45,7 @@ public static CustomArea fromJsonFeature(JsonFeature j) { public CustomArea(Map properties, List borders) { this.properties = properties; this.borders = borders; + this.area = borders.stream().map(Polygon::getArea).reduce(0d, Double::sum); } public Map getProperties() { @@ -56,4 +56,8 @@ public Map getProperties() { public List getBorders() { return borders; } + + public double getArea() { + return area; + } } diff --git a/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleEncodedValuesFactory.java b/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleEncodedValuesFactory.java new file mode 100644 index 00000000000..b8feae73b11 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleEncodedValuesFactory.java @@ -0,0 +1,65 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util; + +import com.graphhopper.util.PMap; + +/** + * This class creates vehicle encoded values that are already included in the GraphHopper distribution. + * + * @author Peter Karich + */ +public class DefaultVehicleEncodedValuesFactory implements VehicleEncodedValuesFactory { + @Override + public VehicleEncodedValues createVehicleEncodedValues(String name, PMap configuration) { + if (name.equals(ROADS)) + return VehicleEncodedValues.roads(configuration); + + if (name.equals(CAR)) + return VehicleEncodedValues.car(configuration); + + if (name.equals("car4wd")) + throw new IllegalArgumentException("Instead of car4wd use the roads vehicle and a custom_model, see custom_models/car4wd.json"); + + if (name.equals(BIKE)) + return VehicleEncodedValues.bike(configuration); + + if (name.equals("bike2")) + throw new IllegalArgumentException("Instead of bike2 use the bike vehicle and a custom model, see custom_models/bike.json and #1234"); + + if (name.equals(RACINGBIKE)) + return VehicleEncodedValues.racingbike(configuration); + + if (name.equals(MOUNTAINBIKE)) + return VehicleEncodedValues.mountainbike(configuration); + + if (name.equals(FOOT)) + return VehicleEncodedValues.foot(configuration); + + if (name.equals(HIKE)) + throw new IllegalArgumentException("Instead of hike use the foot vehicle and a custom model, see custom_models/hike.json and #2759"); + + if (name.equals(MOTORCYCLE)) + return VehicleEncodedValues.motorcycle(configuration); + + if (name.equals(WHEELCHAIR)) + return VehicleEncodedValues.wheelchair(configuration); + + throw new IllegalArgumentException("entry in vehicle list not supported: " + name); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/DefaultFlagEncoderFactory.java b/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleTagParserFactory.java similarity index 50% rename from core/src/main/java/com/graphhopper/routing/util/DefaultFlagEncoderFactory.java rename to core/src/main/java/com/graphhopper/routing/util/DefaultVehicleTagParserFactory.java index 517b91f36ea..2ecac823777 100644 --- a/core/src/main/java/com/graphhopper/routing/util/DefaultFlagEncoderFactory.java +++ b/core/src/main/java/com/graphhopper/routing/util/DefaultVehicleTagParserFactory.java @@ -15,51 +15,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.graphhopper.routing.util; +import com.graphhopper.routing.ev.EncodedValueLookup; import com.graphhopper.util.PMap; -/** - * This class creates FlagEncoders that are already included in the GraphHopper distribution. - * - * @author Peter Karich - */ -public class DefaultFlagEncoderFactory implements FlagEncoderFactory { - @Override - public FlagEncoder createFlagEncoder(String name, PMap configuration) { - if (name.equals(ROADS)) - return FlagEncoders.createRoads(); +import static com.graphhopper.routing.util.VehicleEncodedValuesFactory.*; +public class DefaultVehicleTagParserFactory implements VehicleTagParserFactory { + public VehicleTagParsers createParsers(EncodedValueLookup lookup, String name, PMap configuration) { + if (name.equals(ROADS)) + return VehicleTagParsers.roads(lookup, configuration); if (name.equals(CAR)) - return FlagEncoders.createCar(configuration); - - if (name.equals(CAR4WD)) - return FlagEncoders.createCar4wd(configuration); - + return VehicleTagParsers.car(lookup, configuration); if (name.equals(BIKE)) - return FlagEncoders.createBike(configuration); - - if (name.equals(BIKE2)) - return FlagEncoders.createBike2(configuration); - + return VehicleTagParsers.bike(lookup, configuration); if (name.equals(RACINGBIKE)) - return FlagEncoders.createRacingBike(configuration); - + return VehicleTagParsers.racingbike(lookup, configuration); if (name.equals(MOUNTAINBIKE)) - return FlagEncoders.createMountainBike(configuration); - + return VehicleTagParsers.mtb(lookup, configuration); if (name.equals(FOOT)) - return FlagEncoders.createFoot(configuration); - - if (name.equals(HIKE)) - return FlagEncoders.createHike(configuration); - + return VehicleTagParsers.foot(lookup, configuration); if (name.equals(MOTORCYCLE)) - return FlagEncoders.createMotorcycle(configuration); - + return VehicleTagParsers.motorcycle(lookup, configuration); if (name.equals(WHEELCHAIR)) - return FlagEncoders.createWheelchair(configuration); + return VehicleTagParsers.wheelchair(lookup, configuration); - throw new IllegalArgumentException("entry in encoder list not supported: " + name); + throw new IllegalArgumentException("Unknown name for vehicle tag parsers: " + name); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java b/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java index dee0bb31642..4bb878825b7 100644 --- a/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java +++ b/core/src/main/java/com/graphhopper/routing/util/EncodingManager.java @@ -17,16 +17,19 @@ */ package com.graphhopper.routing.util; -import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.graphhopper.jackson.Jackson; import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; +import com.graphhopper.storage.StorableProperties; +import com.graphhopper.util.Constants; import com.graphhopper.util.PMap; +import java.io.UncheckedIOException; import java.util.*; import java.util.stream.Collectors; -import static com.graphhopper.util.Helper.toLowerCase; - /** * Manager class to register encoder, assign their flag values and check objects with all encoders * during parsing. Create one via: @@ -37,44 +40,54 @@ * @author Nop */ public class EncodingManager implements EncodedValueLookup { - private final List edgeEncoders; - private final Map encodedValueMap; - private final EncodedValue.InitializerConfig turnCostConfig; - private final EncodedValue.InitializerConfig edgeConfig; - - /** - * Instantiate manager with the given list of encoders. The manager knows several default - * encoders using DefaultFlagEncoderFactory. - */ - public static EncodingManager create(String flagEncodersStr) { - return create(new DefaultFlagEncoderFactory(), flagEncodersStr); - } - - public static EncodingManager create(FlagEncoderFactory factory, String flagEncodersStr) { - return createBuilder(Arrays.stream(flagEncodersStr.split(",")).filter(s -> !s.trim().isEmpty()). - map(s -> parseEncoderString(factory, s)).collect(Collectors.toList())).build(); + private final LinkedHashMap encodedValueMap; + private int intsForFlags; + private int intsForTurnCostFlags; + + public static void putEncodingManagerIntoProperties(EncodingManager encodingManager, StorableProperties properties) { + properties.put("graph.em.version", Constants.VERSION_EM); + properties.put("graph.em.ints_for_flags", encodingManager.intsForFlags); + properties.put("graph.em.ints_for_turn_cost_flags", encodingManager.intsForTurnCostFlags); + properties.put("graph.encoded_values", encodingManager.toEncodedValuesAsString()); + } + + public static EncodingManager fromProperties(StorableProperties properties) { + if (properties.containsVersion()) + throw new IllegalStateException("The GraphHopper file format is not compatible with the data you are " + + "trying to load. You either need to use an older version of GraphHopper or run a new import"); + + String versionStr = properties.get("graph.em.version"); + if (versionStr.isEmpty() || !String.valueOf(Constants.VERSION_EM).equals(versionStr)) + throw new IllegalStateException("Incompatible encoding version. You need to use the same GraphHopper version you used to import the graph, or run a new import. " + + " Stored encoding version: " + (versionStr.isEmpty() ? "missing" : versionStr) + ", used encoding version: " + Constants.VERSION_EM); + String encodedValueStr = properties.get("graph.encoded_values"); + ArrayNode evList = deserializeEncodedValueList(encodedValueStr); + LinkedHashMap encodedValues = new LinkedHashMap<>(); + evList.forEach(serializedEV -> { + EncodedValue encodedValue = EncodedValueSerializer.deserializeEncodedValue(serializedEV.textValue()); + if (encodedValues.put(encodedValue.getName(), encodedValue) != null) + throw new IllegalStateException("Duplicate encoded value name: " + encodedValue.getName() + " in: graph.encoded_values=" + encodedValueStr); + }); + + return new EncodingManager(encodedValues, + getIntegerProperty(properties, "graph.em.ints_for_flags"), + getIntegerProperty(properties, "graph.em.ints_for_turn_cost_flags") + ); } - /** - * Instantiate manager with the given list of encoders. - */ - public static EncodingManager create(FlagEncoder... flagEncoders) { - return create(Arrays.asList(flagEncoders)); + private static int getIntegerProperty(StorableProperties properties, String key) { + String str = properties.get(key); + if (str.isEmpty()) + throw new IllegalStateException("Missing EncodingManager property: '" + key + "'"); + return Integer.parseInt(str); } - /** - * Instantiate manager with the given list of encoders. - */ - public static EncodingManager create(List flagEncoders) { - return createBuilder(flagEncoders).build(); - } - - private static EncodingManager.Builder createBuilder(List flagEncoders) { - Builder builder = new Builder(); - for (FlagEncoder flagEncoder : flagEncoders) { - builder.add(flagEncoder); + private static ArrayNode deserializeEncodedValueList(String encodedValueStr) { + try { + return Jackson.newObjectMapper().readValue(encodedValueStr, ArrayNode.class); + } catch (JsonProcessingException e) { + throw new UncheckedIOException(e); } - return builder; } /** @@ -84,253 +97,124 @@ public static Builder start() { return new Builder(); } - public EncodingManager( - List edgeEncoders, - Map encodedValueMap, - EncodedValue.InitializerConfig turnCostConfig, - EncodedValue.InitializerConfig edgeConfig) { - this.edgeEncoders = edgeEncoders; + public EncodingManager(LinkedHashMap encodedValueMap, int intsForFlags, int intsForTurnCostFlags) { this.encodedValueMap = encodedValueMap; - this.turnCostConfig = turnCostConfig; - this.edgeConfig = edgeConfig; + this.intsForFlags = intsForFlags; + this.intsForTurnCostFlags = intsForTurnCostFlags; } private EncodingManager() { - this( - new ArrayList<>(), new LinkedHashMap<>(), - new EncodedValue.InitializerConfig(), - new EncodedValue.InitializerConfig() - ); + this(new LinkedHashMap<>(), 0, 0); } public static class Builder { - private EncodingManager em; - private DateRangeParser dateRangeParser; - private final Map flagEncoderMap = new LinkedHashMap<>(); - private final Map encodedValueMap = new LinkedHashMap<>(); - - public Builder() { - em = new EncodingManager(); - } - - public Builder add(FlagEncoder encoder) { - check(); - if (flagEncoderMap.containsKey(encoder.toString())) - throw new IllegalArgumentException("FlagEncoder already exists: " + encoder); - flagEncoderMap.put(encoder.toString(), (VehicleTagParser) encoder); + private final EncodedValue.InitializerConfig edgeConfig = new EncodedValue.InitializerConfig(); + private final EncodedValue.InitializerConfig turnCostConfig = new EncodedValue.InitializerConfig(); + private EncodingManager em = new EncodingManager(); + + public Builder add(VehicleEncodedValues v) { + checkNotBuiltAlready(); + List list = new ArrayList<>(); + v.createEncodedValues(list); + list.forEach(this::add); + + list = new ArrayList<>(); + v.createTurnCostEncodedValues(list); + list.forEach(this::addTurnCostEncodedValue); return this; } public Builder add(EncodedValue encodedValue) { - check(); - if (encodedValueMap.containsKey(encodedValue.getName())) + checkNotBuiltAlready(); + if (em.hasEncodedValue(encodedValue.getName())) throw new IllegalArgumentException("EncodedValue already exists: " + encodedValue.getName()); - encodedValueMap.put(encodedValue.getName(), encodedValue); + encodedValue.init(edgeConfig); + em.encodedValueMap.put(encodedValue.getName(), encodedValue); return this; } - private void check() { + public Builder addTurnCostEncodedValue(EncodedValue turnCostEnc) { + checkNotBuiltAlready(); + if (em.hasEncodedValue(turnCostEnc.getName())) + throw new IllegalArgumentException("Already defined: " + turnCostEnc.getName() + ". Please note that " + + "EncodedValues for edges and turn costs are in the same namespace."); + turnCostEnc.init(turnCostConfig); + em.encodedValueMap.put(turnCostEnc.getName(), turnCostEnc); + return this; + } + + private void checkNotBuiltAlready() { if (em == null) throw new IllegalStateException("Cannot call method after Builder.build() was called"); } public EncodingManager build() { - check(); - - for (EncodedValue ev : encodedValueMap.values()) { - em.addEncodedValue(ev, false); - } - - if (!em.hasEncodedValue(Roundabout.KEY)) - em.addEncodedValue(Roundabout.create(), false); - if (!em.hasEncodedValue(RoadClass.KEY)) - em.addEncodedValue(new EnumEncodedValue<>(RoadClass.KEY, RoadClass.class), false); - if (!em.hasEncodedValue(RoadClassLink.KEY)) - em.addEncodedValue(new SimpleBooleanEncodedValue(RoadClassLink.KEY), false); - if (!em.hasEncodedValue(RoadEnvironment.KEY)) - em.addEncodedValue(new EnumEncodedValue<>(RoadEnvironment.KEY, RoadEnvironment.class), false); - if (!em.hasEncodedValue(MaxSpeed.KEY)) - em.addEncodedValue(MaxSpeed.create(), false); - if (!em.hasEncodedValue(RoadAccess.KEY)) { - em.addEncodedValue(new EnumEncodedValue<>(RoadAccess.KEY, RoadAccess.class), false); - } - - if (dateRangeParser == null) - dateRangeParser = new DateRangeParser(DateRangeParser.createCalendar()); - - for (FlagEncoder encoder : flagEncoderMap.values()) { - if (encoder instanceof RoadsTagParser) { - // TODO Later these EncodedValues can be added independently of RoadsFlagEncoder. Maybe add a foot_access and hgv_access? and remove the others "xy$access" - if (!em.hasEncodedValue("car_access")) - em.addEncodedValue(new SimpleBooleanEncodedValue("car_access"), false); - if (!em.hasEncodedValue("bike_access")) - em.addEncodedValue(new SimpleBooleanEncodedValue("bike_access"), false); - } else if (encoder instanceof BikeCommonTagParser) { - if (!em.hasEncodedValue(RouteNetwork.key("bike"))) - em.addEncodedValue(new EnumEncodedValue<>(BikeNetwork.KEY, RouteNetwork.class), false); - if (!em.hasEncodedValue(GetOffBike.KEY)) - em.addEncodedValue(GetOffBike.create(), false); - if (!em.hasEncodedValue(Smoothness.KEY)) - em.addEncodedValue(new EnumEncodedValue<>(Smoothness.KEY, Smoothness.class), false); - } else if (encoder instanceof FootTagParser) { - if (!em.hasEncodedValue(RouteNetwork.key("foot"))) - em.addEncodedValue(new EnumEncodedValue<>(FootNetwork.KEY, RouteNetwork.class), false); - } - } - - for (VehicleTagParser encoder : flagEncoderMap.values()) { - encoder.init(dateRangeParser); - em.addEncoder(encoder); - } - + checkNotBuiltAlready(); + addDefaultEncodedValues(); if (em.encodedValueMap.isEmpty()) - throw new IllegalStateException("No EncodedValues found"); - - EncodingManager tmp = em; + throw new IllegalStateException("No EncodedValues were added to the EncodingManager"); + em.intsForFlags = edgeConfig.getRequiredInts(); + em.intsForTurnCostFlags = turnCostConfig.getRequiredInts(); + EncodingManager result = em; em = null; - return tmp; + return result; } - } - - static FlagEncoder parseEncoderString(FlagEncoderFactory factory, String encoderString) { - if (!encoderString.equals(toLowerCase(encoderString))) - throw new IllegalArgumentException("An upper case name for the FlagEncoder is not allowed: " + encoderString); - encoderString = encoderString.trim(); - if (encoderString.isEmpty()) - throw new IllegalArgumentException("FlagEncoder cannot be empty. " + encoderString); + private void addDefaultEncodedValues() { + // todo: I think ultimately these should all be removed and must be added explicitly + List keys = new ArrayList<>(Arrays.asList( + Roundabout.KEY, + RoadClass.KEY, + RoadClassLink.KEY, + RoadEnvironment.KEY, + MaxSpeed.KEY, + RoadAccess.KEY + )); + if (em.getVehicles().stream().anyMatch(vehicle -> vehicle.contains("bike") || vehicle.contains("mtb") || vehicle.contains("racingbike"))) { + keys.add(BikeNetwork.KEY); + keys.add(GetOffBike.KEY); + keys.add(Smoothness.KEY); + } + if (em.getVehicles().stream().anyMatch(vehicle -> vehicle.contains("foot") || vehicle.contains("hike") || vehicle.contains("wheelchair"))) + keys.add(FootNetwork.KEY); - String entryVal = ""; - if (encoderString.contains("|")) { - entryVal = encoderString; - encoderString = encoderString.split("\\|")[0]; + DefaultEncodedValueFactory evFactory = new DefaultEncodedValueFactory(); + for (String key : keys) + if (!em.hasEncodedValue(key)) + add(evFactory.create(key, new PMap())); } - PMap configuration = new PMap(entryVal); - return factory.createFlagEncoder(encoderString, configuration); } public int getIntsForFlags() { - return edgeConfig.getRequiredInts(); - } - - private void addEncoder(VehicleTagParser encoder) { - encoder.setEncodedValueLookup(this); - List list = new ArrayList<>(); - encoder.createEncodedValues(list); - for (EncodedValue ev : list) - addEncodedValue(ev, true); - list = new ArrayList<>(); - encoder.createTurnCostEncodedValues(list); - for (EncodedValue ev : list) - addTurnCostEncodedValue(ev); - edgeEncoders.add(encoder); - } - - private void addEncodedValue(EncodedValue ev, boolean withNamespace) { - String normalizedKey = ev.getName().replaceAll(SPECIAL_SEPARATOR, "_"); - if (hasEncodedValue(normalizedKey)) - throw new IllegalStateException("EncodedValue " + ev.getName() + " collides with " + normalizedKey); - if (!withNamespace && !isSharedEncodedValues(ev)) - throw new IllegalArgumentException("EncodedValue " + ev.getName() + " must not contain namespace character '" + SPECIAL_SEPARATOR + "'"); - if (withNamespace && isSharedEncodedValues(ev)) - throw new IllegalArgumentException("EncodedValue " + ev.getName() + " must contain namespace character '" + SPECIAL_SEPARATOR + "'"); - ev.init(edgeConfig); - encodedValueMap.put(ev.getName(), ev); - } - - private void addTurnCostEncodedValue(EncodedValue turnCostEnc) { - if (encodedValueMap.containsKey(turnCostEnc.getName())) - throw new IllegalArgumentException("Already defined: " + turnCostEnc.getName() + ". Please note that " + - "EncodedValues for edges and turn cost are in the same namespace."); - turnCostEnc.init(turnCostConfig); - encodedValueMap.put(turnCostEnc.getName(), turnCostEnc); + return intsForFlags; } public boolean hasEncodedValue(String key) { return encodedValueMap.get(key) != null; } - /** - * @return true if the specified encoder is found - */ - public boolean hasEncoder(String encoder) { - return getEncoder(encoder, false) != null; - } - - public FlagEncoder getEncoder(String name) { - return getEncoder(name, true); - } - - private FlagEncoder getEncoder(String name, boolean throwExc) { - for (FlagEncoder encoder : edgeEncoders) { - if (name.equalsIgnoreCase(encoder.toString())) - return encoder; - } - if (throwExc) - throw new IllegalArgumentException("FlagEncoder for " + name + " not found. Existing: " + edgeEncoders.stream().map(FlagEncoder::toString).collect(Collectors.joining(","))); - return null; - } - - public enum Access { - WAY, FERRY, OTHER, CAN_SKIP; - - public boolean isFerry() { - return this.ordinal() == FERRY.ordinal(); - } - - public boolean isWay() { - return this.ordinal() == WAY.ordinal(); - } - - public boolean isOther() { - return this.ordinal() == OTHER.ordinal(); - } - - public boolean canSkip() { - return this.ordinal() == CAN_SKIP.ordinal(); - } - } - - public String toFlagEncodersAsString() { - StringBuilder str = new StringBuilder(); - for (FlagEncoder encoder : edgeEncoders) { - if (str.length() > 0) - str.append(","); - - str.append(encoder.toString()) - .append("|") - .append(((VehicleTagParser) encoder).getPropertiesString()); - } - - return str.toString(); + public List getVehicles() { + // we define the 'vehicles' as all the prefixes for which there is an access and speed EV + // any EVs that contain prefix_average_speed are accepted + return getEncodedValues().stream() + .filter(ev -> ev.getName().endsWith("_access")) + .map(ev -> ev.getName().replaceAll("_access", "")) + .filter(v -> getEncodedValues().stream().anyMatch(ev -> ev.getName().contains(VehicleSpeed.key(v)))) + .collect(Collectors.toList()); } public String toEncodedValuesAsString() { - StringBuilder str = new StringBuilder(); - for (EncodedValue ev : encodedValueMap.values()) { - if (!isSharedEncodedValues(ev)) - continue; - - if (str.length() > 0) - str.append(","); - - str.append(ev.toString()); + List serializedEVsList = encodedValueMap.values().stream().map(EncodedValueSerializer::serializeEncodedValue).collect(Collectors.toList()); + try { + return Jackson.newObjectMapper().writeValueAsString(serializedEVsList); + } catch (JsonProcessingException e) { + throw new UncheckedIOException(e); } - - return str.toString(); } @Override public String toString() { - StringBuilder str = new StringBuilder(); - for (FlagEncoder encoder : edgeEncoders) { - if (str.length() > 0) - str.append(","); - - str.append(encoder.toString()); - } - - return str.toString(); + return String.join(",", getVehicles()); } // TODO hide IntsRef even more in a later version: https://gist.github.com/karussell/f4c2b2b1191be978d7ee9ec8dd2cd48f @@ -343,30 +227,8 @@ public IntsRef createRelationFlags() { return new IntsRef(2); } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - EncodingManager that = (EncodingManager) o; - return edgeEncoders.equals(that.edgeEncoders) && - encodedValueMap.equals(that.encodedValueMap); - } - - @Override - public int hashCode() { - return Objects.hash(edgeEncoders, encodedValueMap); - } - - public List fetchEdgeEncoders() { - return new ArrayList<>(edgeEncoders); - } - public boolean needsTurnCostsSupport() { - for (FlagEncoder encoder : edgeEncoders) { - if (encoder.supportsTurnCosts()) - return true; - } - return false; + return intsForTurnCostFlags > 0; } @Override @@ -403,79 +265,13 @@ public StringEncodedValue getStringEncodedValue(String key) { @Override public T getEncodedValue(String key, Class encodedValueType) { EncodedValue ev = encodedValueMap.get(key); + // todo: why do we not just return null when EV is missing? just like java.util.Map? -> https://github.com/graphhopper/graphhopper/pull/2561#discussion_r859770067 if (ev == null) - throw new IllegalArgumentException("Cannot find EncodedValue " + key + " in collection: " + ev); + throw new IllegalArgumentException("Cannot find EncodedValue " + key + " in collection: " + encodedValueMap.keySet()); return (T) ev; } - private static final String SPECIAL_SEPARATOR = "$"; - - private static boolean isSharedEncodedValues(EncodedValue ev) { - return isValidEncodedValue(ev.getName()) && !ev.getName().contains(SPECIAL_SEPARATOR); - } - - /** - * All EncodedValue names that are created from a FlagEncoder should use this method to mark them as - * "none-shared" across the other FlagEncoders. - */ - public static String getKey(FlagEncoder encoder, String str) { - return getKey(encoder.toString(), str); - } - public static String getKey(String prefix, String str) { - return prefix + SPECIAL_SEPARATOR + str; - } - - // copied from janino - private static final Set KEYWORDS = new HashSet<>(Arrays.asList( - "first_match", - "abstract", "assert", - "boolean", "break", "byte", - "case", "catch", "char", "class", "const", "continue", - "default", "do", "double", - "else", "enum", "extends", - "false", "final", "finally", "float", "for", - "goto", - "if", "implements", "import", "instanceof", "int", "interface", - "long", - "native", "new", "non-sealed", "null", - "package", "permits", "private", "protected", "public", - "record", "return", - "sealed", "short", "static", "strictfp", "super", "switch", "synchronized", - "this", "throw", "throws", "transient", "true", "try", - "var", "void", "volatile", - "while", - "yield", - "_" - )); - - public static boolean isValidEncodedValue(String name) { - // first character must be a lower case letter - if (name.isEmpty() || !isLowerLetter(name.charAt(0)) || KEYWORDS.contains(name)) return false; - - int dollarCount = 0, underscoreCount = 0; - for (int i = 1; i < name.length(); i++) { - char c = name.charAt(i); - if (c == '$') { - if (dollarCount > 0) return false; - dollarCount++; - } else if (c == '_') { - if (underscoreCount > 0) return false; - underscoreCount++; - } else if (!isLowerLetter(c) && !isNumber(c)) { - return false; - } else { - underscoreCount = 0; - } - } - return true; - } - - private static boolean isNumber(char c) { - return c >= '0' && c <= '9'; - } - - private static boolean isLowerLetter(char c) { - return c >= 'a' && c <= 'z'; + return prefix + "_" + str; } } \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/util/FlagEncoder.java b/core/src/main/java/com/graphhopper/routing/util/FlagEncoder.java deleted file mode 100644 index 8840f7ed4aa..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/FlagEncoder.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EncodedValueLookup; - -/** - * This class provides methods to define how a value (like speed or direction) converts to a flag - * (currently an integer value), which is stored in an edge. - * - * @author Peter Karich - */ -public interface FlagEncoder extends EncodedValueLookup { - - default boolean isMotorVehicle() { - return getTransportationMode().isMotorVehicle(); - } - - TransportationMode getTransportationMode(); - - /** - * @return the maximum speed in km/h - */ - double getMaxSpeed(); - - /** - * This method returns the EncodedValue used for the direction-dependent access properties of this encoder. - */ - BooleanEncodedValue getAccessEnc(); - - /** - * This method returns the EncodedValue used for the average speed of this encoder. - */ - DecimalEncodedValue getAverageSpeedEnc(); - - boolean supportsTurnCosts(); - - /** - * Returns true if the feature class is supported like TurnWeighting or PriorityWeighting. - * Use support(String) instead. - */ - boolean supports(Class feature); - - /** - * @return true if already registered in an EncodingManager - */ - boolean isRegistered(); -} diff --git a/core/src/main/java/com/graphhopper/routing/util/FlagEncoders.java b/core/src/main/java/com/graphhopper/routing/util/FlagEncoders.java deleted file mode 100644 index 3689995c3c5..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/FlagEncoders.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.graphhopper.routing.util; - -import com.graphhopper.util.PMap; - -public class FlagEncoders { - public static FlagEncoder createFoot() { - return new FootTagParser(); - } - - public static FlagEncoder createFoot(PMap properties) { - return new FootTagParser(properties); - } - - public static FlagEncoder createHike() { - return new HikeTagParser(new PMap()); - } - - public static FlagEncoder createHike(PMap properties) { - return new HikeTagParser(properties); - } - - public static FlagEncoder createWheelchair() { - return new WheelchairTagParser(); - } - - public static FlagEncoder createWheelchair(PMap properties) { - return new WheelchairTagParser(properties); - } - - public static FlagEncoder createCar() { - return new CarTagParser(); - } - - public static FlagEncoder createCar(PMap properties) { - return new CarTagParser(properties); - } - - public static FlagEncoder createMotorcycle() { - return new MotorcycleTagParser(); - } - - public static FlagEncoder createMotorcycle(PMap properties) { - return new MotorcycleTagParser(properties); - } - - public static FlagEncoder createCar4wd(PMap properties) { - return new Car4WDTagParser(properties); - } - - public static FlagEncoder createRacingBike() { - return new RacingBikeTagParser(); - } - - public static FlagEncoder createRacingBike(PMap properties) { - return new RacingBikeTagParser(properties); - } - - public static FlagEncoder createBike() { - return new BikeTagParser(); - } - - public static FlagEncoder createBike(PMap properties) { - return new BikeTagParser(properties); - } - - public static FlagEncoder createBike2() { - return new Bike2WeightTagParser(); - } - - public static FlagEncoder createBike2(PMap properties) { - return new Bike2WeightTagParser(properties); - } - - public static FlagEncoder createMountainBike() { - return new MountainBikeTagParser(); - } - - public static FlagEncoder createMountainBike(PMap properties) { - return new MountainBikeTagParser(properties); - } - - public static FlagEncoder createRoads() { - return new RoadsTagParser(); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/FootTagParser.java b/core/src/main/java/com/graphhopper/routing/util/FootTagParser.java deleted file mode 100644 index 9dbd883c5d0..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/FootTagParser.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.weighting.PriorityWeighting; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.PMap; - -import java.util.*; - -import static com.graphhopper.routing.ev.RouteNetwork.*; -import static com.graphhopper.routing.util.EncodingManager.getKey; -import static com.graphhopper.routing.util.PriorityCode.*; - -/** - * Defines bit layout for pedestrians (speed, access, surface, ...). Here we put a penalty on unsafe - * roads only. If you wish to also prefer routes due to beauty like hiking routes use the - * HikeFlagEncoder instead. - *

- * - * @author Peter Karich - * @author Nop - * @author Karl Hübner - */ -public class FootTagParser extends VehicleTagParser { - static final int SLOW_SPEED = 2; - static final int MEAN_SPEED = 5; - // larger value required - ferries are faster than pedestrians - static final int FERRY_SPEED = 15; - final Set safeHighwayTags = new HashSet<>(); - final Set allowedHighwayTags = new HashSet<>(); - final Set avoidHighwayTags = new HashSet<>(); - final Set allowedSacScale = new HashSet<>(); - protected HashSet sidewalkValues = new HashSet<>(5); - protected HashSet sidewalksNoValues = new HashSet<>(5); - protected final DecimalEncodedValue priorityWayEncoder; - protected EnumEncodedValue footRouteEnc; - protected Map routeMap = new HashMap<>(); - - public FootTagParser() { - this(4, 1, false); - } - - public FootTagParser(PMap properties) { - this(properties.getString("name", "foot"), - properties.getInt("speed_bits", 4), - properties.getDouble("speed_factor", 1), - properties.getBool("speed_two_directions", false)); - - blockPrivate(properties.getBool("block_private", true)); - blockFords(properties.getBool("block_fords", false)); - } - - protected FootTagParser(int speedBits, double speedFactor, boolean speedTwoDirections) { - this("foot", speedBits, speedFactor, speedTwoDirections); - } - - protected FootTagParser(String name, int speedBits, double speedFactor, boolean speedTwoDirections) { - super(name, speedBits, speedFactor, speedTwoDirections, 0); - priorityWayEncoder = new DecimalEncodedValueImpl(getKey(name, "priority"), 4, PriorityCode.getFactor(1), false); - - restrictedValues.add("no"); - restrictedValues.add("restricted"); - restrictedValues.add("military"); - restrictedValues.add("emergency"); - restrictedValues.add("private"); - - intendedValues.add("yes"); - intendedValues.add("designated"); - intendedValues.add("official"); - intendedValues.add("permissive"); - - sidewalksNoValues.add("no"); - sidewalksNoValues.add("none"); - // see #712 - sidewalksNoValues.add("separate"); - - sidewalkValues.add("yes"); - sidewalkValues.add("both"); - sidewalkValues.add("left"); - sidewalkValues.add("right"); - - barriers.add("fence"); - - safeHighwayTags.add("footway"); - safeHighwayTags.add("path"); - safeHighwayTags.add("steps"); - safeHighwayTags.add("pedestrian"); - safeHighwayTags.add("living_street"); - safeHighwayTags.add("track"); - safeHighwayTags.add("residential"); - safeHighwayTags.add("service"); - safeHighwayTags.add("platform"); - - avoidHighwayTags.add("trunk"); - avoidHighwayTags.add("trunk_link"); - avoidHighwayTags.add("primary"); - avoidHighwayTags.add("primary_link"); - avoidHighwayTags.add("secondary"); - avoidHighwayTags.add("secondary_link"); - avoidHighwayTags.add("tertiary"); - avoidHighwayTags.add("tertiary_link"); - - allowedHighwayTags.addAll(safeHighwayTags); - allowedHighwayTags.addAll(avoidHighwayTags); - allowedHighwayTags.add("cycleway"); - allowedHighwayTags.add("unclassified"); - allowedHighwayTags.add("road"); - // disallowed in some countries - //allowedHighwayTags.add("bridleway"); - - routeMap.put(INTERNATIONAL, UNCHANGED.getValue()); - routeMap.put(NATIONAL, UNCHANGED.getValue()); - routeMap.put(REGIONAL, UNCHANGED.getValue()); - routeMap.put(LOCAL, UNCHANGED.getValue()); - - allowedSacScale.add("hiking"); - allowedSacScale.add("mountain_hiking"); - allowedSacScale.add("demanding_mountain_hiking"); - - maxPossibleSpeed = avgSpeedEnc.getNextStorableValue(FERRY_SPEED); - } - - @Override - public TransportationMode getTransportationMode() { - return TransportationMode.FOOT; - } - - @Override - public void createEncodedValues(List registerNewEncodedValue) { - super.createEncodedValues(registerNewEncodedValue); - registerNewEncodedValue.add(priorityWayEncoder); - - footRouteEnc = getEnumEncodedValue(RouteNetwork.key("foot"), RouteNetwork.class); - } - - /** - * Some ways are okay but not separate for pedestrians. - */ - @Override - public EncodingManager.Access getAccess(ReaderWay way) { - String highwayValue = way.getTag("highway"); - if (highwayValue == null) { - EncodingManager.Access acceptPotentially = EncodingManager.Access.CAN_SKIP; - - if (way.hasTag("route", ferries)) { - String footTag = way.getTag("foot"); - if (footTag == null || intendedValues.contains(footTag)) - acceptPotentially = EncodingManager.Access.FERRY; - } - - // special case not for all acceptedRailways, only platform - if (way.hasTag("railway", "platform")) - acceptPotentially = EncodingManager.Access.WAY; - - if (way.hasTag("man_made", "pier")) - acceptPotentially = EncodingManager.Access.WAY; - - if (!acceptPotentially.canSkip()) { - if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) - return EncodingManager.Access.CAN_SKIP; - return acceptPotentially; - } - - return EncodingManager.Access.CAN_SKIP; - } - - // other scales are too dangerous, see http://wiki.openstreetmap.org/wiki/Key:sac_scale - if (way.getTag("sac_scale") != null && !way.hasTag("sac_scale", allowedSacScale)) - return EncodingManager.Access.CAN_SKIP; - - // no need to evaluate ferries or fords - already included here - if (way.hasTag("foot", intendedValues)) - return EncodingManager.Access.WAY; - - // check access restrictions - if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) - return EncodingManager.Access.CAN_SKIP; - - if (way.hasTag("sidewalk", sidewalkValues)) - return EncodingManager.Access.WAY; - - if (!allowedHighwayTags.contains(highwayValue)) - return EncodingManager.Access.CAN_SKIP; - - if (way.hasTag("motorroad", "yes")) - return EncodingManager.Access.CAN_SKIP; - - // do not get our feet wet, "yes" is already included above - if (isBlockFords() && (way.hasTag("highway", "ford") || way.hasTag("ford"))) - return EncodingManager.Access.CAN_SKIP; - - if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way)) - return EncodingManager.Access.CAN_SKIP; - - return EncodingManager.Access.WAY; - } - - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way) { - EncodingManager.Access access = getAccess(way); - if (access.canSkip()) - return edgeFlags; - - Integer priorityFromRelation = routeMap.get(footRouteEnc.getEnum(false, edgeFlags)); - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - if (!access.isFerry()) { - String sacScale = way.getTag("sac_scale"); - if (sacScale != null) { - setSpeed(edgeFlags, true, true, "hiking".equals(sacScale) ? MEAN_SPEED : SLOW_SPEED); - } else { - setSpeed(edgeFlags, true, true, way.hasTag("highway", "steps") ? MEAN_SPEED - 2 : MEAN_SPEED); - } - } else { - priorityFromRelation = PriorityCode.SLIGHT_AVOID.getValue(); - double ferrySpeed = ferrySpeedCalc.getSpeed(way); - setSpeed(edgeFlags, true, true, ferrySpeed); - } - - priorityWayEncoder.setDecimal(false, edgeFlags, PriorityCode.getValue(handlePriority(way, priorityFromRelation))); - return edgeFlags; - } - - void setSpeed(IntsRef edgeFlags, boolean fwd, boolean bwd, double speed) { - if (speed > getMaxSpeed()) - speed = getMaxSpeed(); - if (fwd) - avgSpeedEnc.setDecimal(false, edgeFlags, speed); - if (bwd && avgSpeedEnc.isStoreTwoDirections()) - avgSpeedEnc.setDecimal(true, edgeFlags, speed); - } - - int handlePriority(ReaderWay way, Integer priorityFromRelation) { - TreeMap weightToPrioMap = new TreeMap<>(); - if (priorityFromRelation == null) - weightToPrioMap.put(0d, UNCHANGED.getValue()); - else - weightToPrioMap.put(110d, priorityFromRelation); - - collect(way, weightToPrioMap); - - // pick priority with biggest order value - return weightToPrioMap.lastEntry().getValue(); - } - - /** - * @param weightToPrioMap associate a weight with every priority. This sorted map allows - * subclasses to 'insert' more important priorities as well as overwrite determined priorities. - */ - void collect(ReaderWay way, TreeMap weightToPrioMap) { - String highway = way.getTag("highway"); - if (way.hasTag("foot", "designated")) - weightToPrioMap.put(100d, PREFER.getValue()); - - double maxSpeed = getMaxSpeed(way); - if (safeHighwayTags.contains(highway) || (isValidSpeed(maxSpeed) && maxSpeed <= 20)) { - weightToPrioMap.put(40d, PREFER.getValue()); - if (way.hasTag("tunnel", intendedValues)) { - if (way.hasTag("sidewalk", sidewalksNoValues)) - weightToPrioMap.put(40d, SLIGHT_AVOID.getValue()); - else - weightToPrioMap.put(40d, UNCHANGED.getValue()); - } - } else if ((isValidSpeed(maxSpeed) && maxSpeed > 50) || avoidHighwayTags.contains(highway)) { - if (!way.hasTag("sidewalk", sidewalkValues)) - weightToPrioMap.put(45d, AVOID.getValue()); - } - - if (way.hasTag("bicycle", "official") || way.hasTag("bicycle", "designated")) - weightToPrioMap.put(44d, SLIGHT_AVOID.getValue()); - } - - @Override - public boolean supports(Class feature) { - if (super.supports(feature)) - return true; - - return PriorityWeighting.class.isAssignableFrom(feature); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/HikeTagParser.java b/core/src/main/java/com/graphhopper/routing/util/HikeTagParser.java deleted file mode 100644 index 80090badc5e..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/HikeTagParser.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.weighting.PriorityWeighting; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.*; - -import java.util.TreeMap; - -import static com.graphhopper.routing.ev.RouteNetwork.*; -import static com.graphhopper.routing.util.PriorityCode.*; - -/** - * Defines bit layout for hiking - * - * @author Peter Karich - */ -public class HikeTagParser extends FootTagParser { - - public HikeTagParser() { - this(4, 1, false); - } - - public HikeTagParser(PMap properties) { - this( - properties.getString("name", "hike"), - properties.getInt("speed_bits", 4), - properties.getDouble("speed_factor", 1), - properties.getBool("speed_two_directions", false)); - - blockPrivate(properties.getBool("block_private", true)); - blockFords(properties.getBool("block_fords", false)); - } - - protected HikeTagParser(int speedBits, double speedFactor, boolean speedTwoDirections) { - this("hike", speedBits, speedFactor, speedTwoDirections); - } - - protected HikeTagParser(String name, int speedBits, double speedFactor, boolean speedTwoDirections) { - super(name, speedBits, speedFactor, speedTwoDirections); - - routeMap.put(INTERNATIONAL, BEST.getValue()); - routeMap.put(NATIONAL, BEST.getValue()); - routeMap.put(REGIONAL, VERY_NICE.getValue()); - routeMap.put(LOCAL, VERY_NICE.getValue()); - - // hiking allows all sac_scale values - allowedSacScale.add("alpine_hiking"); - allowedSacScale.add("demanding_alpine_hiking"); - allowedSacScale.add("difficult_alpine_hiking"); - } - - @Override - void collect(ReaderWay way, TreeMap weightToPrioMap) { - String highway = way.getTag("highway"); - if (way.hasTag("foot", "designated")) - weightToPrioMap.put(100d, PREFER.getValue()); - - double maxSpeed = getMaxSpeed(way); - if (safeHighwayTags.contains(highway) || (isValidSpeed(maxSpeed) && maxSpeed <= 20)) { - weightToPrioMap.put(40d, PREFER.getValue()); - if (way.hasTag("tunnel", intendedValues)) { - if (way.hasTag("sidewalk", sidewalksNoValues)) - weightToPrioMap.put(40d, AVOID.getValue()); - else - weightToPrioMap.put(40d, UNCHANGED.getValue()); - } - } else if ((isValidSpeed(maxSpeed) && maxSpeed > 50) || avoidHighwayTags.contains(highway)) { - if (way.hasTag("sidewalk", sidewalksNoValues)) - weightToPrioMap.put(45d, BAD.getValue()); - else - weightToPrioMap.put(45d, AVOID.getValue()); - } - - if (way.hasTag("bicycle", "official") || way.hasTag("bicycle", "designated")) - weightToPrioMap.put(44d, SLIGHT_AVOID.getValue()); - } - - - @Override - public void applyWayTags(ReaderWay way, EdgeIteratorState edge) { - PointList pl = edge.fetchWayGeometry(FetchMode.ALL); - if (!pl.is3D()) - return; - - if (way.hasTag("tunnel", "yes") || way.hasTag("bridge", "yes") || way.hasTag("highway", "steps")) - // do not change speed - // note: although tunnel can have a difference in elevation it is unlikely that the elevation data is correct for a tunnel - return; - - // Decrease the speed for ele increase (incline), and slightly decrease the speed for ele decrease (decline) - double prevEle = pl.getEle(0); - double fullDistance = edge.getDistance(); - - // for short edges an incline makes no sense and for 0 distances could lead to NaN values for speed, see #432 - if (fullDistance < 2) - return; - - double eleDelta = Math.abs(pl.getEle(pl.size() - 1) - prevEle); - double slope = eleDelta / fullDistance; - - IntsRef edgeFlags = edge.getFlags(); - if ((accessEnc.getBool(false, edgeFlags) || accessEnc.getBool(true, edgeFlags)) - && slope > 0.005) { - - // see #1679 => v_hor=4.5km/h for horizontal speed; v_vert=2*0.5km/h for vertical speed (assumption: elevation < edge distance/4.5) - // s_3d/v=h/v_vert + s_2d/v_hor => v = s_3d / (h/v_vert + s_2d/v_hor) = sqrt(s²_2d + h²) / (h/v_vert + s_2d/v_hor) - // slope=h/s_2d=~h/2_3d = sqrt(1+slope²)/(slope+1/4.5) km/h - // maximum slope is 0.37 (Ffordd Pen Llech) - double newSpeed = Math.sqrt(1 + slope * slope) / (slope + 1 / 5.4); - edge.set(avgSpeedEnc, Helper.keepIn(newSpeed, 1, 5)); - } - } - - @Override - public boolean supports(Class feature) { - if (super.supports(feature)) - return true; - - return PriorityWeighting.class.isAssignableFrom(feature); - } - -} diff --git a/core/src/main/java/com/graphhopper/routing/util/MotorcycleTagParser.java b/core/src/main/java/com/graphhopper/routing/util/MotorcycleTagParser.java deleted file mode 100644 index 8af5c74b7f5..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/MotorcycleTagParser.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValueImpl; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; -import com.graphhopper.routing.weighting.CurvatureWeighting; -import com.graphhopper.routing.weighting.PriorityWeighting; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.DistanceCalcEarth; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.PMap; -import com.graphhopper.util.PointList; - -import java.util.HashSet; -import java.util.List; - -import static com.graphhopper.routing.util.EncodingManager.getKey; - -/** - * Defines bit layout for motorbikes - *

- * - * @author Peter Karich - * @author boldtrn - */ -public class MotorcycleTagParser extends CarTagParser { - private final HashSet avoidSet = new HashSet<>(); - private final HashSet preferSet = new HashSet<>(); - private final DecimalEncodedValue priorityWayEncoder; - private final DecimalEncodedValue curvatureEncoder; - - public MotorcycleTagParser() { - this(new PMap()); - } - - public MotorcycleTagParser(PMap properties) { - super(properties.putObject("name", "motorcycle").putObject("speed_two_directions", true)); - - priorityWayEncoder = new DecimalEncodedValueImpl(getKey(getName(), "priority"), 4, PriorityCode.getFactor(1), false); - curvatureEncoder = new DecimalEncodedValueImpl(getKey(getName(), "curvature"), 4, 0.1, false); - - barriers.remove("bus_trap"); - barriers.remove("sump_buster"); - - trackTypeSpeedMap.clear(); - defaultSpeedMap.clear(); - - trackTypeSpeedMap.put("grade1", 20); // paved - trackTypeSpeedMap.put("grade2", 15); // now unpaved - gravel mixed with ... - trackTypeSpeedMap.put("grade3", 10); // ... hard and soft materials - trackTypeSpeedMap.put("grade4", 5); // ... some hard or compressed materials - trackTypeSpeedMap.put("grade5", 5); // ... no hard materials. soil/sand/grass - - avoidSet.add("motorway"); - avoidSet.add("trunk"); - avoidSet.add("motorroad"); - avoidSet.add("residential"); - - preferSet.add("primary"); - preferSet.add("secondary"); - preferSet.add("tertiary"); - - maxPossibleSpeed = avgSpeedEnc.getNextStorableValue(properties.getDouble("max_speed", 120)); - - // autobahn - defaultSpeedMap.put("motorway", 100); - defaultSpeedMap.put("motorway_link", 70); - defaultSpeedMap.put("motorroad", 90); - // bundesstraße - defaultSpeedMap.put("trunk", 80); - defaultSpeedMap.put("trunk_link", 75); - // linking bigger town - defaultSpeedMap.put("primary", 65); - defaultSpeedMap.put("primary_link", 60); - // linking towns + villages - defaultSpeedMap.put("secondary", 60); - defaultSpeedMap.put("secondary_link", 50); - // streets without middle line separation - defaultSpeedMap.put("tertiary", 50); - defaultSpeedMap.put("tertiary_link", 40); - defaultSpeedMap.put("unclassified", 30); - defaultSpeedMap.put("residential", 30); - // spielstraße - defaultSpeedMap.put("living_street", 5); - defaultSpeedMap.put("service", 20); - // unknown road - defaultSpeedMap.put("road", 20); - // forestry stuff - defaultSpeedMap.put("track", 15); - } - - /** - * Define the place of the speedBits in the edge flags for car. - */ - @Override - public void createEncodedValues(List registerNewEncodedValue) { - super.createEncodedValues(registerNewEncodedValue); - registerNewEncodedValue.add(priorityWayEncoder); - registerNewEncodedValue.add(curvatureEncoder); - } - - @Override - public EncodingManager.Access getAccess(ReaderWay way) { - String highwayValue = way.getTag("highway"); - String firstValue = way.getFirstPriorityTag(restrictions); - if (highwayValue == null) { - if (way.hasTag("route", ferries)) { - if (restrictedValues.contains(firstValue)) - return EncodingManager.Access.CAN_SKIP; - if (intendedValues.contains(firstValue) || - // implied default is allowed only if foot and bicycle is not specified: - firstValue.isEmpty() && !way.hasTag("foot") && !way.hasTag("bicycle")) - return EncodingManager.Access.FERRY; - } - return EncodingManager.Access.CAN_SKIP; - } - - if ("service".equals(highwayValue) && "emergency_access".equals(way.getTag("service"))) { - return EncodingManager.Access.CAN_SKIP; - } - - if ("track".equals(highwayValue)) { - String tt = way.getTag("tracktype"); - if (tt != null && !tt.equals("grade1")) - return EncodingManager.Access.CAN_SKIP; - } - - if (!defaultSpeedMap.containsKey(highwayValue)) - return EncodingManager.Access.CAN_SKIP; - - if (way.hasTag("impassable", "yes") || way.hasTag("status", "impassable")) - return EncodingManager.Access.CAN_SKIP; - - if (!firstValue.isEmpty()) { - if (restrictedValues.contains(firstValue) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) - return EncodingManager.Access.CAN_SKIP; - if (intendedValues.contains(firstValue)) - return EncodingManager.Access.WAY; - } - - // do not drive street cars into fords - if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) - return EncodingManager.Access.CAN_SKIP; - - if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way)) - return EncodingManager.Access.CAN_SKIP; - else - return EncodingManager.Access.WAY; - } - - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way) { - EncodingManager.Access access = getAccess(way); - if (access.canSkip()) - return edgeFlags; - - if (!access.isFerry()) { - // get assumed speed from highway type - double speed = getSpeed(way); - speed = applyMaxSpeed(way, speed); - - double maxMCSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed:motorcycle")); - if (isValidSpeed(maxMCSpeed) && maxMCSpeed < speed) - speed = maxMCSpeed * 0.9; - - // limit speed to max 30 km/h if bad surface - if (isValidSpeed(speed) && speed > 30 && way.hasTag("surface", badSurfaceSpeedMap)) - speed = 30; - - boolean isRoundabout = roundaboutEnc.getBool(false, edgeFlags); - if (way.hasTag("oneway", oneways) || isRoundabout) { - if (way.hasTag("oneway", "-1")) { - accessEnc.setBool(true, edgeFlags, true); - setSpeed(true, edgeFlags, speed); - } else { - accessEnc.setBool(false, edgeFlags, true); - setSpeed(false, edgeFlags, speed); - } - } else { - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - setSpeed(false, edgeFlags, speed); - setSpeed(true, edgeFlags, speed); - } - - } else { - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - double ferrySpeed = ferrySpeedCalc.getSpeed(way); - setSpeed(false, edgeFlags, ferrySpeed); - setSpeed(true, edgeFlags, ferrySpeed); - } - - priorityWayEncoder.setDecimal(false, edgeFlags, PriorityCode.getValue(handlePriority(way))); - curvatureEncoder.setDecimal(false, edgeFlags, 10.0 / 7.0); - return edgeFlags; - } - - private int handlePriority(ReaderWay way) { - String highway = way.getTag("highway", ""); - if (avoidSet.contains(highway)) { - return PriorityCode.BAD.getValue(); - } else if (preferSet.contains(highway)) { - return PriorityCode.BEST.getValue(); - } - - return PriorityCode.UNCHANGED.getValue(); - } - - @Override - public void applyWayTags(ReaderWay way, EdgeIteratorState edge) { - double speed = edge.get(avgSpeedEnc); - double roadDistance = edge.getDistance(); - double beelineDistance = getBeelineDistance(way); - double bendiness = beelineDistance / roadDistance; - - bendiness = discriminateSlowStreets(bendiness, speed); - bendiness = increaseBendinessImpact(bendiness); - bendiness = correctErrors(bendiness); - - edge.set(curvatureEncoder, bendiness); - } - - private double getBeelineDistance(ReaderWay way) { - PointList pointList = way.getTag("point_list", null); - if (pointList == null) - throw new IllegalStateException("The artificial 'point_list' tag is missing for way: " + way.getId()); - if (pointList.size() < 2) - throw new IllegalStateException("The artificial 'point_list' tag contained less than two points for way: " + way.getId()); - return DistanceCalcEarth.DIST_EARTH.calcDist(pointList.getLat(0), pointList.getLon(0), pointList.getLat(pointList.size() - 1), pointList.getLon(pointList.size() - 1)); - } - - /** - * Streets that slow are not fun and probably in a town. - */ - protected double discriminateSlowStreets(double bendiness, double speed) { - if (speed < 51) { - return 1; - } - return bendiness; - } - - /** - * A really small bendiness or a bendiness greater than 1 indicates an error in the calculation. - * Just ignore them. We use bendiness greater 1.2 since the beelineDistance is only - * approximated, therefore it can happen on straight roads, that the beeline is longer than the - * road. - */ - protected double correctErrors(double bendiness) { - if (bendiness < 0.01 || bendiness > 1) { - return 1; - } - return bendiness; - } - - /** - * A good bendiness should become a greater impact. A bendiness close to 1 should not be - * changed. - */ - protected double increaseBendinessImpact(double bendiness) { - return (Math.pow(bendiness, 2)); - } - - @Override - public TransportationMode getTransportationMode() { - return TransportationMode.MOTORCYCLE; - } - - @Override - public boolean supports(Class feature) { - if (super.supports(feature)) - return true; - - if (CurvatureWeighting.class.isAssignableFrom(feature)) { - return true; - } - - return PriorityWeighting.class.isAssignableFrom(feature); - } - -} diff --git a/core/src/main/java/com/graphhopper/routing/util/MountainBikeTagParser.java b/core/src/main/java/com/graphhopper/routing/util/MountainBikeTagParser.java deleted file mode 100644 index ce11331c8ce..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/MountainBikeTagParser.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.util.PMap; - -import java.util.TreeMap; - -import static com.graphhopper.routing.ev.RouteNetwork.*; -import static com.graphhopper.routing.util.PriorityCode.*; - -/** - * Specifies the settings for mountain biking - *

- * - * @author ratrun - * @author Peter Karich - */ -public class MountainBikeTagParser extends BikeCommonTagParser { - public MountainBikeTagParser() { - this(4, 2, 0); - } - - public MountainBikeTagParser(PMap properties) { - this(properties.getInt("speed_bits", 4), - properties.getDouble("speed_factor", 2), - properties.getBool("turn_costs", false) ? 1 : 0); - - blockPrivate(properties.getBool("block_private", true)); - blockFords(properties.getBool("block_fords", false)); - } - - protected MountainBikeTagParser(int speedBits, double speedFactor, int maxTurnCosts) { - super("mtb", speedBits, speedFactor, maxTurnCosts, false); - setTrackTypeSpeed("grade1", 18); // paved - setTrackTypeSpeed("grade2", 16); // now unpaved ... - setTrackTypeSpeed("grade3", 12); - setTrackTypeSpeed("grade4", 8); - setTrackTypeSpeed("grade5", 6); // like sand/grass - - setSurfaceSpeed("paved", 18); - setSurfaceSpeed("asphalt", 18); - setSurfaceSpeed("cobblestone", 10); - setSurfaceSpeed("cobblestone:flattened", 10); - setSurfaceSpeed("sett", 10); - setSurfaceSpeed("concrete", 14); - setSurfaceSpeed("concrete:lanes", 16); - setSurfaceSpeed("concrete:plates", 16); - setSurfaceSpeed("paving_stones", 16); - setSurfaceSpeed("paving_stones:30", 16); - setSurfaceSpeed("unpaved", 14); - setSurfaceSpeed("compacted", 14); - setSurfaceSpeed("dirt", 14); - setSurfaceSpeed("earth", 14); - setSurfaceSpeed("fine_gravel", 18); - setSurfaceSpeed("grass", 14); - setSurfaceSpeed("grass_paver", 14); - setSurfaceSpeed("gravel", 16); - setSurfaceSpeed("ground", 16); - setSurfaceSpeed("ice", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("metal", 10); - setSurfaceSpeed("mud", 12); - setSurfaceSpeed("pebblestone", 12); - setSurfaceSpeed("salt", 12); - setSurfaceSpeed("sand", 10); - setSurfaceSpeed("wood", 10); - - setHighwaySpeed("living_street", 6); - setHighwaySpeed("steps", PUSHING_SECTION_SPEED); - - setHighwaySpeed("cycleway", 18); - setHighwaySpeed("path", 18); - setHighwaySpeed("footway", 6); - setHighwaySpeed("pedestrian", 6); - setHighwaySpeed("road", 12); - setHighwaySpeed("track", 18); - setHighwaySpeed("service", 14); - setHighwaySpeed("unclassified", 16); - setHighwaySpeed("residential", 16); - - setHighwaySpeed("trunk", 18); - setHighwaySpeed("trunk_link", 18); - setHighwaySpeed("primary", 18); - setHighwaySpeed("primary_link", 18); - setHighwaySpeed("secondary", 18); - setHighwaySpeed("secondary_link", 18); - setHighwaySpeed("tertiary", 18); - setHighwaySpeed("tertiary_link", 18); - - addPushingSection("footway"); - addPushingSection("platform"); - addPushingSection("pedestrian"); - addPushingSection("steps"); - - routeMap.put(INTERNATIONAL, PREFER.getValue()); - routeMap.put(NATIONAL, PREFER.getValue()); - routeMap.put(REGIONAL, PREFER.getValue()); - routeMap.put(LOCAL, BEST.getValue()); - - avoidHighwayTags.add("primary"); - avoidHighwayTags.add("primary_link"); - avoidHighwayTags.add("secondary"); - avoidHighwayTags.add("secondary_link"); - - preferHighwayTags.add("road"); - preferHighwayTags.add("track"); - preferHighwayTags.add("path"); - preferHighwayTags.add("service"); - preferHighwayTags.add("tertiary"); - preferHighwayTags.add("tertiary_link"); - preferHighwayTags.add("residential"); - preferHighwayTags.add("unclassified"); - - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.EXCELLENT, 1.1d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.GOOD, 1.0d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.INTERMEDIATE, 0.9d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.BAD, 0.7d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.VERY_BAD, 0.6d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.HORRIBLE, 0.5d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.VERY_HORRIBLE, 0.4d); - // SmoothnessSpeed <= smoothnessFactorPushingSectionThreshold gets mapped to speed PUSHING_SECTION_SPEED - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.IMPASSABLE, smoothnessFactorPushingSectionThreshold); - - setSpecificClassBicycle("mtb"); - } - - @Override - void collect(ReaderWay way, double wayTypeSpeed, TreeMap weightToPrioMap) { - super.collect(way, wayTypeSpeed, weightToPrioMap); - - String highway = way.getTag("highway"); - if ("track".equals(highway)) { - String trackType = way.getTag("tracktype"); - if ("grade1".equals(trackType)) - weightToPrioMap.put(50d, UNCHANGED.getValue()); - else if (trackType == null) - weightToPrioMap.put(90d, PREFER.getValue()); - else if (trackType.startsWith("grade")) - weightToPrioMap.put(100d, VERY_NICE.getValue()); - } - } - - @Override - boolean isSacScaleAllowed(String sacScale) { - // other scales are too dangerous even for MTB, see http://wiki.openstreetmap.org/wiki/Key:sac_scale - return "hiking".equals(sacScale) || "mountain_hiking".equals(sacScale) - || "demanding_mountain_hiking".equals(sacScale) || "alpine_hiking".equals(sacScale); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/OSMParsers.java b/core/src/main/java/com/graphhopper/routing/util/OSMParsers.java new file mode 100644 index 00000000000..dd50f2cb0c4 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/OSMParsers.java @@ -0,0 +1,126 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util; + +import com.graphhopper.reader.ReaderRelation; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.RestrictionTagParser; +import com.graphhopper.routing.ev.EncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.util.parsers.RelationTagParser; +import com.graphhopper.routing.util.parsers.TagParser; +import com.graphhopper.storage.IntsRef; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +public class OSMParsers { + private final List ignoredHighways; + private final List wayTagParsers; + private final List relationTagParsers; + private final List restrictionTagParsers; + private final EncodedValue.InitializerConfig relConfig = new EncodedValue.InitializerConfig(); + + public OSMParsers() { + this(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + + public OSMParsers(List ignoredHighways, List wayTagParsers, + List relationTagParsers, List restrictionTagParsers) { + this.ignoredHighways = ignoredHighways; + this.wayTagParsers = wayTagParsers; + this.relationTagParsers = relationTagParsers; + this.restrictionTagParsers = restrictionTagParsers; + } + + public OSMParsers addIgnoredHighway(String highway) { + ignoredHighways.add(highway); + return this; + } + + public OSMParsers addWayTagParser(TagParser tagParser) { + wayTagParsers.add(tagParser); + return this; + } + + public OSMParsers addRelationTagParser(Function createRelationTagParser) { + relationTagParsers.add(createRelationTagParser.apply(relConfig)); + return this; + } + + public OSMParsers addRestrictionTagParser(RestrictionTagParser restrictionTagParser) { + restrictionTagParsers.add(restrictionTagParser); + return this; + } + + public boolean acceptWay(ReaderWay way) { + String highway = way.getTag("highway"); + if (highway != null) + return !ignoredHighways.contains(highway); + else if (way.getTag("route") != null) + // we accept *all* ways with a 'route' tag and no 'highway' tag, because most of them are ferries + // (route=ferry), which we want, and there aren't so many such ways we do not want + // https://github.com/graphhopper/graphhopper/pull/2702#discussion_r1038093050 + return true; + else if ("pier".equals(way.getTag("man_made"))) + return true; + else if ("platform".equals(way.getTag("railway"))) + return true; + else + return false; + } + + public IntsRef handleRelationTags(ReaderRelation relation, IntsRef relFlags) { + for (RelationTagParser relParser : relationTagParsers) { + relParser.handleRelationTags(relFlags, relation); + } + return relFlags; + } + + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + for (RelationTagParser relParser : relationTagParsers) + relParser.handleWayTags(edgeId, edgeIntAccess, way, relationFlags); + for (TagParser parser : wayTagParsers) + parser.handleWayTags(edgeId, edgeIntAccess, way, relationFlags); + } + + public IntsRef createRelationFlags() { + int requiredInts = relConfig.getRequiredInts(); + if (requiredInts > 2) + throw new IllegalStateException("More than two ints are needed for relation flags, but OSMReader does not allow this"); + return new IntsRef(2); + } + + public List getIgnoredHighways() { + return ignoredHighways; + } + + public List getWayTagParsers() { + return wayTagParsers; + } + + public List getRelationTagParsers() { + return relationTagParsers; + } + + public List getRestrictionTagParsers() { + return restrictionTagParsers; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/RacingBikeTagParser.java b/core/src/main/java/com/graphhopper/routing/util/RacingBikeTagParser.java deleted file mode 100644 index 5a5aa83c86c..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/RacingBikeTagParser.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.util.PMap; - -import java.util.TreeMap; - -import static com.graphhopper.routing.ev.RouteNetwork.*; -import static com.graphhopper.routing.util.PriorityCode.*; - -/** - * Specifies the settings for race biking - * - * @author ratrun - * @author Peter Karich - */ -public class RacingBikeTagParser extends BikeCommonTagParser { - public RacingBikeTagParser() { - this(4, 2, 0); - } - - public RacingBikeTagParser(PMap properties) { - this(properties.getInt("speed_bits", 4), - properties.getDouble("speed_factor", 2), - properties.getBool("turn_costs", false) ? 1 : 0); - - blockPrivate(properties.getBool("block_private", true)); - blockFords(properties.getBool("block_fords", false)); - } - - protected RacingBikeTagParser(int speedBits, double speedFactor, int maxTurnCosts) { - super("racingbike", speedBits, speedFactor, maxTurnCosts, false); - preferHighwayTags.add("road"); - preferHighwayTags.add("secondary"); - preferHighwayTags.add("secondary_link"); - preferHighwayTags.add("tertiary"); - preferHighwayTags.add("tertiary_link"); - preferHighwayTags.add("residential"); - - setTrackTypeSpeed("grade1", 20); // paved - setTrackTypeSpeed("grade2", 10); // now unpaved ... - setTrackTypeSpeed("grade3", PUSHING_SECTION_SPEED); - setTrackTypeSpeed("grade4", PUSHING_SECTION_SPEED); - setTrackTypeSpeed("grade5", PUSHING_SECTION_SPEED); - - setSurfaceSpeed("paved", 20); - setSurfaceSpeed("asphalt", 20); - setSurfaceSpeed("cobblestone", 10); - setSurfaceSpeed("cobblestone:flattened", 10); - setSurfaceSpeed("sett", 10); - setSurfaceSpeed("concrete", 20); - setSurfaceSpeed("concrete:lanes", 16); - setSurfaceSpeed("concrete:plates", 16); - setSurfaceSpeed("paving_stones", 10); - setSurfaceSpeed("paving_stones:30", 10); - setSurfaceSpeed("unpaved", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("compacted", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("dirt", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("earth", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("fine_gravel", PUSHING_SECTION_SPEED); - setSurfaceSpeed("grass", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("grass_paver", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("gravel", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("ground", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("ice", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("metal", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("mud", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("pebblestone", PUSHING_SECTION_SPEED); - setSurfaceSpeed("salt", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("sand", PUSHING_SECTION_SPEED / 2); - setSurfaceSpeed("wood", PUSHING_SECTION_SPEED / 2); - - setHighwaySpeed("cycleway", 18); - setHighwaySpeed("path", 8); - setHighwaySpeed("footway", 6); - setHighwaySpeed("pedestrian", 6); - setHighwaySpeed("road", 12); - setHighwaySpeed("track", PUSHING_SECTION_SPEED / 2); // assume unpaved - setHighwaySpeed("service", 12); - setHighwaySpeed("unclassified", 16); - setHighwaySpeed("residential", 16); - - setHighwaySpeed("trunk", 20); - setHighwaySpeed("trunk_link", 20); - setHighwaySpeed("primary", 20); - setHighwaySpeed("primary_link", 20); - setHighwaySpeed("secondary", 20); - setHighwaySpeed("secondary_link", 20); - setHighwaySpeed("tertiary", 20); - setHighwaySpeed("tertiary_link", 20); - - addPushingSection("path"); - addPushingSection("footway"); - addPushingSection("platform"); - addPushingSection("pedestrian"); - addPushingSection("steps"); - - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.EXCELLENT, 1.2d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.GOOD, 1.0d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.INTERMEDIATE, 0.9d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.BAD, 0.7d); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.VERY_BAD, smoothnessFactorPushingSectionThreshold); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.HORRIBLE, smoothnessFactorPushingSectionThreshold); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.VERY_HORRIBLE, smoothnessFactorPushingSectionThreshold); - setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.IMPASSABLE, smoothnessFactorPushingSectionThreshold); - - routeMap.put(INTERNATIONAL, BEST.getValue()); - routeMap.put(NATIONAL, BEST.getValue()); - routeMap.put(REGIONAL, VERY_NICE.getValue()); - routeMap.put(LOCAL, UNCHANGED.getValue()); - - barriers.add("kissing_gate"); - barriers.add("stile"); - barriers.add("turnstile"); - - setAvoidSpeedLimit(81); - setSpecificClassBicycle("roadcycling"); - } - - @Override - void collect(ReaderWay way, double wayTypeSpeed, TreeMap weightToPrioMap) { - super.collect(way, wayTypeSpeed, weightToPrioMap); - - String highway = way.getTag("highway"); - if ("service".equals(highway)) { - weightToPrioMap.put(40d, UNCHANGED.getValue()); - } else if ("track".equals(highway)) { - String trackType = way.getTag("tracktype"); - if ("grade1".equals(trackType)) - weightToPrioMap.put(110d, PREFER.getValue()); - else if (trackType == null || trackType.startsWith("grade")) - weightToPrioMap.put(110d, AVOID_MORE.getValue()); - } - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/RoadDensityCalculator.java b/core/src/main/java/com/graphhopper/routing/util/RoadDensityCalculator.java new file mode 100644 index 00000000000..8cc46690f85 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/RoadDensityCalculator.java @@ -0,0 +1,114 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util; + +import com.carrotsearch.hppc.IntArrayDeque; +import com.carrotsearch.hppc.IntScatterSet; +import com.carrotsearch.hppc.IntSet; +import com.graphhopper.storage.Graph; +import com.graphhopper.storage.NodeAccess; +import com.graphhopper.util.EdgeExplorer; +import com.graphhopper.util.EdgeIterator; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.GHUtility; +import com.graphhopper.util.shapes.GHPoint; + +import java.util.concurrent.Callable; +import java.util.function.BiConsumer; +import java.util.function.ToDoubleFunction; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static com.graphhopper.util.DistancePlaneProjection.DIST_PLANE; + +public class RoadDensityCalculator { + private final Graph graph; + private final EdgeExplorer edgeExplorer; + private final IntSet visited; + private final IntArrayDeque deque; + + public RoadDensityCalculator(Graph graph) { + this.graph = graph; + this.edgeExplorer = graph.createEdgeExplorer(); + visited = new IntScatterSet(); + deque = new IntArrayDeque(100); + } + + /** + * Loops over all edges of the graph and calls the given edgeHandler for each edge. This is done in parallel using + * the given number of threads. For every call we can calculate the road density using the provided thread local + * road density calculator. + */ + public static void calcRoadDensities(Graph graph, BiConsumer edgeHandler, int threads) { + ThreadLocal calculator = ThreadLocal.withInitial(() -> new RoadDensityCalculator(graph)); + Stream> roadDensityWorkers = IntStream.range(0, graph.getEdges()) + .mapToObj(i -> () -> { + EdgeIteratorState edge = graph.getEdgeIteratorState(i, Integer.MIN_VALUE); + edgeHandler.accept(calculator.get(), edge); + return "road_density_calc"; + }); + GHUtility.runConcurrently(roadDensityWorkers, threads); + } + + /** + * @param radius in meters + * @param calcRoadFactor weighting function. use this to define how different kinds of roads shall contribute to the calculated road density + * @return the road density in the vicinity of the given edge, i.e. the weighted road length divided by the squared radius + */ + public double calcRoadDensity(EdgeIteratorState edge, double radius, ToDoubleFunction calcRoadFactor) { + visited.clear(); + deque.head = deque.tail = 0; + double totalRoadWeight = 0; + NodeAccess na = graph.getNodeAccess(); + int baseNode = edge.getBaseNode(); + int adjNode = edge.getAdjNode(); + GHPoint center = new GHPoint(getLat(na, baseNode, adjNode), getLon(na, baseNode, adjNode)); + deque.addLast(baseNode); + deque.addLast(adjNode); + visited.add(baseNode); + visited.add(adjNode); + // we just do a BFS search and sum up all the road lengths + final double radiusNormalized = DIST_PLANE.calcNormalizedDist(radius); + while (!deque.isEmpty()) { + int node = deque.removeFirst(); + EdgeIterator iter = edgeExplorer.setBaseNode(node); + while (iter.next()) { + if (visited.contains(iter.getAdjNode())) + continue; + visited.add(iter.getAdjNode()); + double distance = DIST_PLANE.calcNormalizedDist(center.lat, center.lon, getLat(na, iter.getBaseNode(), iter.getAdjNode()), getLon(na, iter.getBaseNode(), iter.getAdjNode())); + if (distance > radiusNormalized) + continue; + double roadLength = Math.min(2 * radius, iter.getDistance()); + totalRoadWeight += roadLength * calcRoadFactor.applyAsDouble(iter); + deque.addLast(iter.getAdjNode()); + } + } + return totalRoadWeight / radius / radius; + } + + private static double getLat(NodeAccess na, int baseNode, int adjNode) { + return (na.getLat(baseNode) + na.getLat(adjNode)) / 2; + } + + private static double getLon(NodeAccess na, int baseNode, int adjNode) { + return (na.getLon(baseNode) + na.getLon(adjNode)) / 2; + } + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/RoadsTagParser.java b/core/src/main/java/com/graphhopper/routing/util/RoadsTagParser.java deleted file mode 100644 index 6e2c60c4b00..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/RoadsTagParser.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.storage.IntsRef; - -public class RoadsTagParser extends VehicleTagParser { - - public RoadsTagParser() { - super("roads", 7, 2, true, 3); - maxPossibleSpeed = avgSpeedEnc.getNextStorableValue(254); - } - - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way) { - // let's make it high and let it be reduced in the custom model - double speed = maxPossibleSpeed; - accessEnc.setBool(true, edgeFlags, true); - accessEnc.setBool(false, edgeFlags, true); - setSpeed(false, edgeFlags, speed); - if (avgSpeedEnc.isStoreTwoDirections()) - setSpeed(true, edgeFlags, speed); - return edgeFlags; - } - - @Override - public EncodingManager.Access getAccess(ReaderWay way) { - if (way.getTag("highway", "").isEmpty()) - return EncodingManager.Access.CAN_SKIP; - return EncodingManager.Access.WAY; - } - - @Override - public TransportationMode getTransportationMode() { - return TransportationMode.VEHICLE; - } - -} diff --git a/core/src/main/java/com/graphhopper/routing/util/SlopeCalculator.java b/core/src/main/java/com/graphhopper/routing/util/SlopeCalculator.java new file mode 100644 index 00000000000..d6e2b6ea37c --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/SlopeCalculator.java @@ -0,0 +1,82 @@ +package com.graphhopper.routing.util; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.util.parsers.TagParser; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.DistanceCalcEarth; +import com.graphhopper.util.PointList; + +public class SlopeCalculator implements TagParser { + private final DecimalEncodedValue maxSlopeEnc; + private final DecimalEncodedValue averageSlopeEnc; + // the elevation data fluctuates a lot and so the slope is not that precise for short edges. + private static final double MIN_LENGTH = 8; + + public SlopeCalculator(DecimalEncodedValue max, DecimalEncodedValue averageEnc) { + this.maxSlopeEnc = max; + this.averageSlopeEnc = averageEnc; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + PointList pointList = way.getTag("point_list", null); + if (pointList != null) { + if (pointList.isEmpty() || !pointList.is3D()) { + averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0); + return; + } + // Calculate 2d distance, although pointList might be 3D. + // This calculation is a bit expensive and edge_distance is available already, but this would be in 3D + double distance2D = DistanceCalcEarth.calcDistance(pointList, false); + if (distance2D < MIN_LENGTH) { + // default is minimum of average_slope is negative so we have to explicitly set it to 0 + averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, 0); + return; + } + + double towerNodeSlope = calcSlope(pointList.getEle(pointList.size() - 1) - pointList.getEle(0), distance2D); + if (Double.isNaN(towerNodeSlope)) + throw new IllegalArgumentException("average_slope was NaN for OSM way ID " + way.getId()); + + if (towerNodeSlope >= 0) + averageSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, Math.min(towerNodeSlope, averageSlopeEnc.getMaxStorableDecimal())); + else + averageSlopeEnc.setDecimal(true, edgeId, edgeIntAccess, Math.min(Math.abs(towerNodeSlope), averageSlopeEnc.getMaxStorableDecimal())); + + // max_slope is more error-prone as the shorter distances increase the fluctuation + // so apply some more filtering (here we use the average elevation delta of the previous two points) + double maxSlope = 0, prevDist = 0, prevLat = pointList.getLat(0), prevLon = pointList.getLon(0); + for (int i = 1; i < pointList.size(); i++) { + double pillarDistance2D = DistanceCalcEarth.DIST_EARTH.calcDist(prevLat, prevLon, pointList.getLat(i), pointList.getLon(i)); + if (i > 1 && prevDist > MIN_LENGTH) { + double averagedPrevEle = (pointList.getEle(i - 1) + pointList.getEle(i - 2)) / 2; + double tmpSlope = calcSlope(pointList.getEle(i) - averagedPrevEle, pillarDistance2D + prevDist / 2); + maxSlope = Math.max(maxSlope, Math.abs(tmpSlope)); + } + prevDist = pillarDistance2D; + prevLat = pointList.getLat(i); + prevLon = pointList.getLon(i); + } + + // For tunnels and bridges we cannot trust the pillar node elevation and ignore all changes. + // Probably we should somehow recalculate even the average_slope after elevation interpolation? See EdgeElevationInterpolator + if (way.hasTag("tunnel", "yes") || way.hasTag("bridge", "yes") || way.hasTag("highway", "steps")) + maxSlope = Math.abs(towerNodeSlope); + else + maxSlope = Math.max(Math.abs(towerNodeSlope), maxSlope); + + if (Double.isNaN(maxSlope)) + throw new IllegalArgumentException("max_slope was NaN for OSM way ID " + way.getId()); + + // TODO Use two independent values for both directions to store if it is a gain or loss and not just the absolute change. + // TODO To save space then it would be nice to have an encoded value that can store two different values which are swapped when the reverse direction is used + maxSlopeEnc.setDecimal(false, edgeId, edgeIntAccess, Math.min(maxSlope, maxSlopeEnc.getMaxStorableDecimal())); + } + } + + static double calcSlope(double eleDelta, double distance2D) { + return eleDelta * 100 / distance2D; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/TagParserManager.java b/core/src/main/java/com/graphhopper/routing/util/TagParserManager.java deleted file mode 100644 index e49ecb9bb59..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/TagParserManager.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.OSMTurnRelation; -import com.graphhopper.reader.ReaderRelation; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.reader.osm.conditional.DateRangeParser; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.parsers.*; -import com.graphhopper.storage.Graph; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.PMap; - -import java.util.*; -import java.util.stream.Collectors; - -import static com.graphhopper.routing.util.EncodingManager.isValidEncodedValue; -import static com.graphhopper.util.Helper.toLowerCase; -import static java.util.Collections.emptyMap; - -/** - * Manager class to register encoder, assign their flag values and check objects with all encoders - * during parsing. Create one via: - *

- * EncodingManager.start(4).add(new CarFlagEncoder()).build(); - * - * @author Peter Karich - * @author Nop - */ -public class TagParserManager implements EncodedValueLookup { - private final List edgeEncoders = new ArrayList<>(); - private final Map encodedValueMap = new LinkedHashMap<>(); - private final List relationTagParsers = new ArrayList<>(); - private final List edgeTagParsers = new ArrayList<>(); - private final Map turnCostParsers = new LinkedHashMap<>(); - private final EncodedValue.InitializerConfig turnCostConfig; - private final EncodedValue.InitializerConfig relationConfig; - private final EncodedValue.InitializerConfig edgeConfig; - private EncodingManager encodingManager; - - /** - * Instantiate manager with the given list of encoders. The manager knows several default - * encoders using DefaultFlagEncoderFactory. - */ - public static TagParserManager create(String flagEncodersStr) { - return create(new DefaultFlagEncoderFactory(), flagEncodersStr); - } - - public static TagParserManager create(FlagEncoderFactory factory, String flagEncodersStr) { - return createBuilder(Arrays.stream(flagEncodersStr.split(",")).filter(s -> !s.trim().isEmpty()). - map(s -> parseEncoderString(factory, s)).collect(Collectors.toList())).build(); - } - - /** - * Instantiate manager with the given list of encoders. - */ - public static TagParserManager create(FlagEncoder... flagEncoders) { - return create(Arrays.asList(flagEncoders)); - } - - /** - * Instantiate manager with the given list of encoders. - */ - public static TagParserManager create(List flagEncoders) { - return createBuilder(flagEncoders).build(); - } - - private static TagParserManager.Builder createBuilder(List flagEncoders) { - Builder builder = new Builder(); - for (FlagEncoder flagEncoder : flagEncoders) { - builder.add(flagEncoder); - } - return builder; - } - - /** - * Starts the build process of an EncodingManager - */ - public static Builder start() { - return new Builder(); - } - - private TagParserManager() { - this.turnCostConfig = new EncodedValue.InitializerConfig(); - this.relationConfig = new EncodedValue.InitializerConfig(); - this.edgeConfig = new EncodedValue.InitializerConfig(); - } - - public EncodingManager getEncodingManager() { - return encodingManager; - } - - public void releaseParsers() { - turnCostParsers.clear(); - edgeTagParsers.clear(); - relationTagParsers.clear(); - } - - public static class Builder { - private TagParserManager em; - private DateRangeParser dateRangeParser; - private final Map flagEncoderMap = new LinkedHashMap<>(); - private final Map encodedValueMap = new LinkedHashMap<>(); - private final Set tagParserSet = new LinkedHashSet<>(); - private final List turnCostParsers = new ArrayList<>(); - private final List relationTagParsers = new ArrayList<>(); - - public Builder() { - em = new TagParserManager(); - } - - public boolean addIfAbsent(FlagEncoderFactory factory, String flagEncoderString) { - check(); - String key = flagEncoderString.split("\\|")[0].trim(); - if (flagEncoderMap.containsKey(key)) - return false; - FlagEncoder fe = parseEncoderString(factory, flagEncoderString); - flagEncoderMap.put(fe.toString(), (VehicleTagParser) fe); - return true; - } - - public boolean addIfAbsent(EncodedValueFactory encodedValueFactory, TagParserFactory factory, String tagParserString) { - check(); - tagParserString = tagParserString.trim(); - if (tagParserString.isEmpty()) return false; - - if (!tagParserString.equals(toLowerCase(tagParserString))) - throw new IllegalArgumentException("Use lower case for TagParser: " + tagParserString); - - add(encodedValueFactory.create(tagParserString)); - - TagParser tagParser = factory.create(new EncodedValueLookup() { - @Override - public List getEncodedValues() { - return new ArrayList<>(encodedValueMap.values()); - } - - @Override - public T getEncodedValue(String key, Class encodedValueType) { - return (T) encodedValueMap.get(key); - } - - @Override - public BooleanEncodedValue getBooleanEncodedValue(String key) { - return (BooleanEncodedValue) encodedValueMap.get(key); - } - - @Override - public IntEncodedValue getIntEncodedValue(String key) { - return (IntEncodedValue) encodedValueMap.get(key); - } - - @Override - public DecimalEncodedValue getDecimalEncodedValue(String key) { - return (DecimalEncodedValue) encodedValueMap.get(key); - } - - @Override - public > EnumEncodedValue getEnumEncodedValue(String key, Class enumType) { - return (EnumEncodedValue) encodedValueMap.get(key); - } - - @Override - public StringEncodedValue getStringEncodedValue(String key) { - return (StringEncodedValue) encodedValueMap.get(key); - } - - @Override - public boolean hasEncodedValue(String key) { - return encodedValueMap.containsKey(key); - } - }, tagParserString); - return tagParserSet.add(tagParser); - } - - public Builder addTurnCostParser(TurnCostParser parser) { - check(); - turnCostParsers.add(parser); - return this; - } - - public Builder addRelationTagParser(RelationTagParser tagParser) { - check(); - relationTagParsers.add(tagParser); - return this; - } - - public Builder add(FlagEncoder encoder) { - check(); - if (flagEncoderMap.containsKey(encoder.toString())) - throw new IllegalArgumentException("FlagEncoder already exists: " + encoder); - flagEncoderMap.put(encoder.toString(), (VehicleTagParser) encoder); - return this; - } - - public Builder add(EncodedValue encodedValue) { - check(); - if (encodedValueMap.containsKey(encodedValue.getName())) - throw new IllegalArgumentException("EncodedValue already exists: " + encodedValue.getName()); - encodedValueMap.put(encodedValue.getName(), encodedValue); - return this; - } - - /** - * This method adds the specified TagParser and automatically adds EncodedValues as requested in - * createEncodedValues. - */ - public Builder add(TagParser tagParser) { - check(); - if (!tagParserSet.add(tagParser)) - throw new IllegalArgumentException("TagParser already exists: " + tagParser); - - return this; - } - - public Builder setDateRangeParser(DateRangeParser dateRangeParser) { - check(); - this.dateRangeParser = dateRangeParser; - return this; - } - - private void check() { - if (em == null) - throw new IllegalStateException("Cannot call method after Builder.build() was called"); - } - - private void _addEdgeTagParser(TagParser tagParser) { - if (!em.edgeEncoders.isEmpty()) - throw new IllegalStateException("Avoid mixing encoded values from FlagEncoder with shared encoded values until we have a more clever mechanism, see #1862"); - em.edgeTagParsers.add(tagParser); - } - - private void _addRelationTagParser(RelationTagParser tagParser) { - em.relationTagParsers.add(tagParser); - _addEdgeTagParser(tagParser); - } - - private void _addTurnCostParser(TurnCostParser parser) { - List list = new ArrayList<>(); - parser.createTurnCostEncodedValues(em, list); - for (EncodedValue ev : list) { - ev.init(em.turnCostConfig); - if (em.encodedValueMap.containsKey(ev.getName())) - throw new IllegalArgumentException("Already defined: " + ev.getName() + ". Please note that " + - "EncodedValues for edges and turn cost are in the same namespace."); - em.encodedValueMap.put(ev.getName(), ev); - } - em.turnCostParsers.put(parser.getName(), parser); - } - - public TagParserManager build() { - check(); - - for (RelationTagParser tagParser : relationTagParsers) { - _addRelationTagParser(tagParser); - } - - for (TagParser tagParser : tagParserSet) { - _addEdgeTagParser(tagParser); - } - - for (EncodedValue ev : encodedValueMap.values()) { - em.addEncodedValue(ev, false); - } - - if (!em.hasEncodedValue(Roundabout.KEY)) { - em.addEncodedValue(Roundabout.create(), false); - _addEdgeTagParser(new OSMRoundaboutParser(em.getBooleanEncodedValue(Roundabout.KEY))); - } - if (!em.hasEncodedValue(RoadClass.KEY)) { - em.addEncodedValue(new EnumEncodedValue<>(RoadClass.KEY, RoadClass.class), false); - _addEdgeTagParser(new OSMRoadClassParser(em.getEnumEncodedValue(RoadClass.KEY, RoadClass.class))); - } - if (!em.hasEncodedValue(RoadClassLink.KEY)) { - em.addEncodedValue(new SimpleBooleanEncodedValue(RoadClassLink.KEY), false); - _addEdgeTagParser(new OSMRoadClassLinkParser(em.getBooleanEncodedValue(RoadClassLink.KEY))); - } - if (!em.hasEncodedValue(RoadEnvironment.KEY)) { - em.addEncodedValue(new EnumEncodedValue<>(RoadEnvironment.KEY, RoadEnvironment.class), false); - _addEdgeTagParser(new OSMRoadEnvironmentParser(em.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class))); - } - if (!em.hasEncodedValue(MaxSpeed.KEY)) { - em.addEncodedValue(MaxSpeed.create(), false); - _addEdgeTagParser(new OSMMaxSpeedParser(em.getDecimalEncodedValue(MaxSpeed.KEY))); - } - if (!em.hasEncodedValue(RoadAccess.KEY)) { - em.addEncodedValue(new EnumEncodedValue<>(RoadAccess.KEY, RoadAccess.class), false); - // TODO introduce road_access for different vehicles? But how to create it in DefaultTagParserFactory? - _addEdgeTagParser(new OSMRoadAccessParser(em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR))); - } - - if (dateRangeParser == null) - dateRangeParser = new DateRangeParser(DateRangeParser.createCalendar()); - - for (VehicleTagParser encoder : flagEncoderMap.values()) { - if (encoder instanceof RoadsTagParser) { - // TODO Later these EncodedValues can be added independently of RoadsFlagEncoder. Maybe add a foot_access and hgv_access? and remove the others "xy$access" - if (!em.hasEncodedValue("car_access")) { - em.addEncodedValue(new SimpleBooleanEncodedValue("car_access", true), false); - _addEdgeTagParser(new OSMAccessParser(em.getBooleanEncodedValue("car_access"), em.getBooleanEncodedValue(Roundabout.KEY), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR), TransportationMode.CAR)); - } - if (!em.hasEncodedValue("bike_access")) { - em.addEncodedValue(new SimpleBooleanEncodedValue("bike_access", true), false); - _addEdgeTagParser(new OSMAccessParser(em.getBooleanEncodedValue("bike_access"), em.getBooleanEncodedValue(Roundabout.KEY), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.BIKE), TransportationMode.BIKE)); - } - } else if (encoder instanceof BikeCommonTagParser) { - if (!em.hasEncodedValue(RouteNetwork.key("bike"))) { - em.addEncodedValue(new EnumEncodedValue<>(BikeNetwork.KEY, RouteNetwork.class), false); - _addRelationTagParser(new OSMBikeNetworkTagParser(em.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class), em.relationConfig)); - } - if (!em.hasEncodedValue(GetOffBike.KEY)) { - em.addEncodedValue(GetOffBike.create(), false); - _addEdgeTagParser(new OSMGetOffBikeParser(em.getBooleanEncodedValue(GetOffBike.KEY))); - } - if (!em.hasEncodedValue(Smoothness.KEY)) { - em.addEncodedValue(new EnumEncodedValue<>(Smoothness.KEY, Smoothness.class), false); - _addEdgeTagParser(new OSMSmoothnessParser(em.getEnumEncodedValue(Smoothness.KEY, Smoothness.class))); - } - } else if (encoder instanceof FootTagParser) { - if (!em.hasEncodedValue(RouteNetwork.key("foot"))) - em.addEncodedValue(new EnumEncodedValue<>(FootNetwork.KEY, RouteNetwork.class), false); - _addRelationTagParser(new OSMFootNetworkTagParser(em.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class), em.relationConfig)); - } - } - - for (VehicleTagParser encoder : flagEncoderMap.values()) { - encoder.init(dateRangeParser); - em.addEncoder(encoder); - } - - for (TurnCostParser parser : turnCostParsers) { - _addTurnCostParser(parser); - } - - // FlagEncoder can demand TurnCostParsers => add them after the explicitly added ones - for (VehicleTagParser encoder : flagEncoderMap.values()) { - if (encoder.supportsTurnCosts() && !em.turnCostParsers.containsKey(TurnCost.key(encoder.toString()))) { - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - DecimalEncodedValue turnCostEnc = encoder.getTurnCostEnc(); - _addTurnCostParser(new OSMTurnRelationParser(accessEnc, turnCostEnc, encoder.getRestrictions())); - } - } - - if (em.encodedValueMap.isEmpty()) - throw new IllegalStateException("No EncodedValues found"); - - em.encodingManager = new EncodingManager(new ArrayList<>(em.edgeEncoders), em.encodedValueMap, - em.turnCostConfig, em.edgeConfig); - - TagParserManager tmp = em; - em = null; - return tmp; - } - } - - static FlagEncoder parseEncoderString(FlagEncoderFactory factory, String encoderString) { - if (!encoderString.equals(toLowerCase(encoderString))) - throw new IllegalArgumentException("An upper case name for the FlagEncoder is not allowed: " + encoderString); - - encoderString = encoderString.trim(); - if (encoderString.isEmpty()) - throw new IllegalArgumentException("FlagEncoder cannot be empty. " + encoderString); - - String entryVal = ""; - if (encoderString.contains("|")) { - entryVal = encoderString; - encoderString = encoderString.split("\\|")[0]; - } - PMap configuration = new PMap(entryVal); - return factory.createFlagEncoder(encoderString, configuration); - } - - public int getIntsForFlags() { - return edgeConfig.getRequiredInts(); - } - - private void addEncoder(VehicleTagParser encoder) { - encoder.setEncodedValueLookup(this); - List list = new ArrayList<>(); - encoder.createEncodedValues(list); - for (EncodedValue ev : list) - addEncodedValue(ev, true); - edgeEncoders.add(encoder); - } - - private void addEncodedValue(EncodedValue ev, boolean withNamespace) { - String normalizedKey = ev.getName().replaceAll(SPECIAL_SEPARATOR, "_"); - if (hasEncodedValue(normalizedKey)) - throw new IllegalStateException("EncodedValue " + ev.getName() + " collides with " + normalizedKey); - if (!withNamespace && !isSharedEncodedValues(ev)) - throw new IllegalArgumentException("EncodedValue " + ev.getName() + " must not contain namespace character '" + SPECIAL_SEPARATOR + "'"); - if (withNamespace && isSharedEncodedValues(ev)) - throw new IllegalArgumentException("EncodedValue " + ev.getName() + " must contain namespace character '" + SPECIAL_SEPARATOR + "'"); - ev.init(edgeConfig); - encodedValueMap.put(ev.getName(), ev); - } - - public boolean hasEncodedValue(String key) { - return encodedValueMap.get(key) != null; - } - - /** - * @return true if the specified encoder is found - */ - public boolean hasEncoder(String encoder) { - return getEncoder(encoder, false) != null; - } - - public FlagEncoder getEncoder(String name) { - return getEncoder(name, true); - } - - private FlagEncoder getEncoder(String name, boolean throwExc) { - for (FlagEncoder encoder : edgeEncoders) { - if (name.equalsIgnoreCase(encoder.toString())) - return encoder; - } - if (throwExc) - throw new IllegalArgumentException("FlagEncoder for " + name + " not found. Existing: " + edgeEncoders.stream().map(FlagEncoder::toString).collect(Collectors.toList())); - return null; - } - - /** - * Determine whether a way is routable for one of the added encoders. - * - * @return if at least one encoder consumes the specified way - */ - public boolean acceptWay(ReaderWay way) { - return edgeEncoders.stream().anyMatch(encoder -> !encoder.getAccess(way).equals(EncodingManager.Access.CAN_SKIP)); - } - - public IntsRef handleRelationTags(ReaderRelation relation, IntsRef relFlags) { - for (RelationTagParser relParser : relationTagParsers) { - relParser.handleRelationTags(relFlags, relation); - } - return relFlags; - } - - public void handleTurnRelationTags(OSMTurnRelation turnRelation, TurnCostParser.ExternalInternalMap map, Graph graph) { - for (TurnCostParser parser : turnCostParsers.values()) { - parser.handleTurnRelationTags(turnRelation, map, graph); - } - } - - /** - * Processes way properties of different kind to determine speed and direction. - * - * @param relationFlags The preprocessed relation flags is used to influence the way properties. - */ - public IntsRef handleWayTags(ReaderWay way, IntsRef relationFlags) { - IntsRef edgeFlags = createEdgeFlags(); - for (TagParser parser : edgeTagParsers) { - parser.handleWayTags(edgeFlags, way, relationFlags); - } - for (VehicleTagParser encoder : edgeEncoders) { - encoder.handleWayTags(edgeFlags, way); - if (!edgeFlags.isEmpty()) { - Map nodeTags = way.getTag("node_tags", emptyMap()); - encoder.handleNodeTags(edgeFlags, nodeTags); - } - } - return edgeFlags; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - for (FlagEncoder encoder : edgeEncoders) { - if (str.length() > 0) - str.append(","); - - str.append(encoder.toString()); - } - - return str.toString(); - } - - // TODO hide IntsRef even more in a later version: https://gist.github.com/karussell/f4c2b2b1191be978d7ee9ec8dd2cd48f - public IntsRef createEdgeFlags() { - return new IntsRef(getIntsForFlags()); - } - - public IntsRef createRelationFlags() { - // for backward compatibility use 2 ints - return new IntsRef(2); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - TagParserManager that = (TagParserManager) o; - return edgeEncoders.equals(that.edgeEncoders) && - encodedValueMap.equals(that.encodedValueMap); - } - - @Override - public int hashCode() { - return Objects.hash(edgeEncoders, encodedValueMap); - } - - public void applyWayTags(ReaderWay way, EdgeIteratorState edge) { - for (VehicleTagParser encoder : edgeEncoders) - encoder.applyWayTags(way, edge); - } - - public List fetchEdgeEncoders() { - return new ArrayList<>(edgeEncoders); - } - - public boolean needsTurnCostsSupport() { - for (FlagEncoder encoder : edgeEncoders) { - if (encoder.supportsTurnCosts()) - return true; - } - return false; - } - - @Override - public List getEncodedValues() { - return Collections.unmodifiableList(new ArrayList<>(encodedValueMap.values())); - } - - @Override - public BooleanEncodedValue getBooleanEncodedValue(String key) { - return getEncodedValue(key, BooleanEncodedValue.class); - } - - @Override - public IntEncodedValue getIntEncodedValue(String key) { - return getEncodedValue(key, IntEncodedValue.class); - } - - @Override - public DecimalEncodedValue getDecimalEncodedValue(String key) { - return getEncodedValue(key, DecimalEncodedValue.class); - } - - @SuppressWarnings("unchecked") - @Override - public > EnumEncodedValue getEnumEncodedValue(String key, Class type) { - return getEncodedValue(key, EnumEncodedValue.class); - } - - @Override - public StringEncodedValue getStringEncodedValue(String key) { - return getEncodedValue(key, StringEncodedValue.class); - } - - @Override - public T getEncodedValue(String key, Class encodedValueType) { - EncodedValue ev = encodedValueMap.get(key); - if (ev == null) - throw new IllegalArgumentException("Cannot find EncodedValue " + key + " in collection: " + ev); - return (T) ev; - } - - private static final String SPECIAL_SEPARATOR = "$"; - - private static boolean isSharedEncodedValues(EncodedValue ev) { - return isValidEncodedValue(ev.getName()) && !ev.getName().contains(SPECIAL_SEPARATOR); - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/util/TransportationMode.java b/core/src/main/java/com/graphhopper/routing/util/TransportationMode.java index 94934deed78..30f0287bfc2 100644 --- a/core/src/main/java/com/graphhopper/routing/util/TransportationMode.java +++ b/core/src/main/java/com/graphhopper/routing/util/TransportationMode.java @@ -26,7 +26,7 @@ */ public enum TransportationMode { OTHER(false), FOOT(false), VEHICLE(false), BIKE(false), - CAR(true), MOTORCYCLE(true), HGV(true), PSV(true); + CAR(true), MOTORCYCLE(true), HGV(true), PSV(true), BUS(true); private final boolean motorVehicle; diff --git a/core/src/main/java/com/graphhopper/routing/util/TraversalMode.java b/core/src/main/java/com/graphhopper/routing/util/TraversalMode.java index 53feba97f3a..8936afb9138 100644 --- a/core/src/main/java/com/graphhopper/routing/util/TraversalMode.java +++ b/core/src/main/java/com/graphhopper/routing/util/TraversalMode.java @@ -17,30 +17,15 @@ */ package com.graphhopper.routing.util; +import com.graphhopper.storage.RoutingCHEdgeIteratorState; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; -import java.util.Arrays; - -import static com.graphhopper.util.Helper.toUpperCase; - /** - * Defines how the graph can be traversed while Dijkstra or similar RoutingAlgorithm is in progress. - * Different options define how precise turn restrictions and costs are taken into account, but - * still all are without via-way support. BTW: this would not be done at runtime, this would be a - * pre-processing step to avoid performance penalties. - *

- * * @author Peter Karich */ public enum TraversalMode { - /** - * The simplest traversal mode but without turn restrictions or cost support. - */ NODE_BASED(false), - /** - * The edged-based traversal mode with turn restriction and cost support. 2 times slower than node based. - */ EDGE_BASED(true); private final boolean edgeBased; @@ -49,38 +34,34 @@ public enum TraversalMode { this.edgeBased = edgeBased; } - public static TraversalMode fromString(String name) { - try { - return valueOf(toUpperCase(name)); - } catch (Exception ex) { - throw new IllegalArgumentException("TraversalMode " + name + " not supported. " - + "Supported are: " + Arrays.asList(TraversalMode.values())); - } - } - /** * Returns the identifier to access the map of the shortest path tree according to the traversal * mode. E.g. returning the adjacent node id in node-based behavior whilst returning the edge id * in edge-based behavior *

* - * @param iterState the current {@link EdgeIteratorState} + * @param edgeState the current {@link EdgeIteratorState} * @param reverse true, if traversal in backward direction. Will be true only for * backward searches in bidirectional algorithms. * @return the identifier to access the shortest path tree */ - public final int createTraversalId(EdgeIteratorState iterState, boolean reverse) { - return createTraversalId(iterState.getBaseNode(), iterState.getAdjNode(), iterState.getEdge(), reverse); + public final int createTraversalId(EdgeIteratorState edgeState, boolean reverse) { + if (edgeBased) + return reverse ? edgeState.getReverseEdgeKey() : edgeState.getEdgeKey(); + return edgeState.getAdjNode(); } - /** - * If you have an EdgeIteratorState the other createTraversalId is preferred! - */ - public final int createTraversalId(int baseNode, int adjNode, int edgeId, boolean reverse) { + public final int createTraversalId(RoutingCHEdgeIteratorState chEdgeState, boolean reverse) { if (edgeBased) { - return GHUtility.createEdgeKey(baseNode, adjNode, edgeId, reverse); + int key = reverse ? chEdgeState.getOrigEdgeKeyFirst() : chEdgeState.getOrigEdgeKeyLast(); + // For reverse traversal we need to revert the edge key, but not for loops and not for shortcuts. + // Why not for shortcuts? Because of our definition of the first/last edge keys: they do not depend on the + // 'state' of the edge state, but are defined in terms of the direction of the (always directed) shortcut. + if (reverse && !chEdgeState.isShortcut() && chEdgeState.getBaseNode() != chEdgeState.getAdjNode()) + key = GHUtility.reverseEdgeKey(key); + return key; } - return adjNode; + return chEdgeState.getAdjNode(); } public boolean isEdgeBased() { diff --git a/core/src/main/java/com/graphhopper/routing/util/UrbanDensityCalculator.java b/core/src/main/java/com/graphhopper/routing/util/UrbanDensityCalculator.java new file mode 100644 index 00000000000..600981b4de1 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/UrbanDensityCalculator.java @@ -0,0 +1,126 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util; + +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.UrbanDensity; +import com.graphhopper.storage.Graph; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.StopWatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.function.ToDoubleFunction; + +public class UrbanDensityCalculator { + private static final Logger logger = LoggerFactory.getLogger(UrbanDensityCalculator.class); + + /** + * Calculates the urban density (rural/residential/city) for all edges of the graph. + * First a weighted road density is calculated for every edge to determine whether it belongs to a residential area. + * In a second step very dense residential areas are classified as 'city'. + * + * @param residentialAreaRadius radius used for residential area calculation in meters + * @param residentialAreaSensitivity Use higher values if there are too many residential areas that are not recognized. Use + * smaller values if there are too many non-residential areas that are classified as residential. + * @param cityAreaRadius in meters, see residentialAreaRadius + * @param cityAreaSensitivity similar to residentialAreaSensitivity, but for the city classification + * @param threads number of threads used to calculate the road densities + */ + public static void calcUrbanDensity(Graph graph, EnumEncodedValue urbanDensityEnc, + EnumEncodedValue roadClassEnc, BooleanEncodedValue roadClassLinkEnc, + double residentialAreaRadius, double residentialAreaSensitivity, + double cityAreaRadius, double cityAreaSensitivity, + int threads) { + logger.info("Calculating residential areas ..., radius={}, sensitivity={}, threads={}", residentialAreaRadius, residentialAreaSensitivity, threads); + StopWatch sw = StopWatch.started(); + calcResidential(graph, urbanDensityEnc, roadClassEnc, roadClassLinkEnc, residentialAreaRadius, residentialAreaSensitivity, threads); + logger.info("Finished calculating residential areas, took: " + sw.stop().getSeconds() + "s"); + if (cityAreaRadius > 1) { + logger.info("Calculating city areas ..., radius={}, sensitivity={}, threads={}", cityAreaRadius, cityAreaSensitivity, threads); + sw = StopWatch.started(); + calcCity(graph, urbanDensityEnc, cityAreaRadius, cityAreaSensitivity, threads); + logger.info("Finished calculating city areas, took: " + sw.stop().getSeconds() + "s"); + } + } + + private static void calcResidential(Graph graph, EnumEncodedValue urbanDensityEnc, + EnumEncodedValue roadClassEnc, BooleanEncodedValue roadClassLinkEnc, + double radius, double sensitivity, int threads) { + final ToDoubleFunction calcRoadFactor = edge -> { + if (edge.get(roadClassLinkEnc)) + // highway exits sometimes have a 'high' density. By excluding them here we try to prevent them from + // being classified as residential areas when they are in the countryside. + return 0; + RoadClass roadClass = edge.get(roadClassEnc); + switch (roadClass) { + // we're interested in the road density of urban roads, so residential areas are particularly interesting + case RESIDENTIAL: + case LIVING_STREET: + case FOOTWAY: + case CYCLEWAY: + case STEPS: + return 2; + case MOTORWAY: + case TRUNK: + case PRIMARY: + case SECONDARY: + case TERTIARY: + case SERVICE: + case OTHER: + return 1; + default: + return 0; + } + }; + // temporarily write results to an external array for thread-safety + boolean[] isResidential = new boolean[graph.getEdges()]; + RoadDensityCalculator.calcRoadDensities(graph, (calculator, edge) -> { + RoadClass roadClass = edge.get(roadClassEnc); + if (roadClass == RoadClass.RESIDENTIAL || roadClass == RoadClass.LIVING_STREET) { + isResidential[edge.getEdge()] = true; + return; + } + double roadDensity = calculator.calcRoadDensity(edge, radius, calcRoadFactor); + isResidential[edge.getEdge()] = roadDensity * sensitivity >= 1.0; + }, threads); + for (int edge = 0; edge < isResidential.length; edge++) + graph.getEdgeIteratorState(edge, Integer.MIN_VALUE).set(urbanDensityEnc, isResidential[edge] ? UrbanDensity.RESIDENTIAL : UrbanDensity.RURAL); + } + + private static void calcCity(Graph graph, EnumEncodedValue urbanDensityEnc, + double radius, double sensitivity, int threads) { + // do not modify the urban density values as long as we are still reading them -> store city flags in this array first + boolean[] isCity = new boolean[graph.getEdges()]; + final ToDoubleFunction calcRoadFactor = edge -> edge.get(urbanDensityEnc) == UrbanDensity.RESIDENTIAL ? 1 : 0; + RoadDensityCalculator.calcRoadDensities(graph, (calculator, edge) -> { + UrbanDensity urbanDensity = edge.get(urbanDensityEnc); + if (urbanDensity == UrbanDensity.RURAL) + return; + double roadDensity = calculator.calcRoadDensity(edge, radius, calcRoadFactor); + if (roadDensity * sensitivity >= 1.0) + isCity[edge.getEdge()] = true; + }, threads); + for (int edge = 0; edge < isCity.length; edge++) + if (isCity[edge]) + graph.getEdgeIteratorState(edge, Integer.MIN_VALUE).set(urbanDensityEnc, UrbanDensity.CITY); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java new file mode 100644 index 00000000000..d0a0366ca19 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValues.java @@ -0,0 +1,164 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util; + +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; + +import java.util.Arrays; +import java.util.List; + +import static com.graphhopper.routing.util.VehicleEncodedValuesFactory.*; + +public class VehicleEncodedValues { + public static final List OUTDOOR_VEHICLES = Arrays.asList(BIKE, RACINGBIKE, MOUNTAINBIKE, FOOT, HIKE, WHEELCHAIR); + + private final String name; + private final BooleanEncodedValue accessEnc; + private final DecimalEncodedValue avgSpeedEnc; + private final DecimalEncodedValue priorityEnc; + private final DecimalEncodedValue turnCostEnc; + + public static VehicleEncodedValues foot(PMap properties) { + String name = properties.getString("name", "foot"); + int speedBits = properties.getInt("speed_bits", 4); + double speedFactor = properties.getDouble("speed_factor", 1); + boolean speedTwoDirections = properties.getBool("speed_two_directions", false); + int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + BooleanEncodedValue accessEnc = VehicleAccess.create(name); + DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); + DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); + DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); + } + + public static VehicleEncodedValues wheelchair(PMap properties) { + if (properties.has("speed_two_directions")) + throw new IllegalArgumentException("wheelchair always uses two directions"); + return foot(new PMap(properties) + .putObject("name", properties.getString("name", "wheelchair")) + .putObject("speed_two_directions", true) + ); + } + + public static VehicleEncodedValues bike(PMap properties) { + String name = properties.getString("name", "bike"); + int speedBits = properties.getInt("speed_bits", 4); + double speedFactor = properties.getDouble("speed_factor", 2); + boolean speedTwoDirections = properties.getBool("speed_two_directions", false); + int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + BooleanEncodedValue accessEnc = VehicleAccess.create(name); + DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); + DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); + DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); + } + + public static VehicleEncodedValues racingbike(PMap properties) { + return bike(new PMap(properties).putObject("name", properties.getString("name", "racingbike"))); + } + + public static VehicleEncodedValues mountainbike(PMap properties) { + return bike(new PMap(properties).putObject("name", properties.getString("name", "mtb"))); + } + + public static VehicleEncodedValues car(PMap properties) { + String name = properties.getString("name", "car"); + int speedBits = properties.getInt("speed_bits", 5); + double speedFactor = properties.getDouble("speed_factor", 5); + int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + BooleanEncodedValue accessEnc = VehicleAccess.create(name); + DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, true); + DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc); + } + + public static VehicleEncodedValues motorcycle(PMap properties) { + String name = properties.getString("name", "motorcycle"); + int speedBits = properties.getInt("speed_bits", 5); + double speedFactor = properties.getDouble("speed_factor", 5); + boolean speedTwoDirections = properties.getBool("speed_two_directions", true); + int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + BooleanEncodedValue accessEnc = VehicleAccess.create(name); + DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); + DecimalEncodedValue priorityEnc = VehiclePriority.create(name, 4, PriorityCode.getFactor(1), false); + DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, priorityEnc, turnCostEnc); + } + + public static VehicleEncodedValues roads(PMap properties) { + String name = properties.getString("name", "roads"); + int speedBits = properties.getInt("speed_bits", 7); + double speedFactor = properties.getDouble("speed_factor", 2); + boolean speedTwoDirections = properties.getBool("speed_two_directions", true); + int maxTurnCosts = properties.getInt("max_turn_costs", properties.getBool("turn_costs", false) ? 1 : 0); + BooleanEncodedValue accessEnc = VehicleAccess.create(name); + DecimalEncodedValue speedEnc = VehicleSpeed.create(name, speedBits, speedFactor, speedTwoDirections); + DecimalEncodedValue turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; + return new VehicleEncodedValues(name, accessEnc, speedEnc, null, turnCostEnc); + } + + public VehicleEncodedValues(String name, BooleanEncodedValue accessEnc, DecimalEncodedValue avgSpeedEnc, + DecimalEncodedValue priorityEnc, DecimalEncodedValue turnCostEnc) { + this.name = name; + this.accessEnc = accessEnc; + this.avgSpeedEnc = avgSpeedEnc; + this.priorityEnc = priorityEnc; + this.turnCostEnc = turnCostEnc; + } + + public void createEncodedValues(List registerNewEncodedValue) { + if (accessEnc != null) + registerNewEncodedValue.add(accessEnc); + if (avgSpeedEnc != null) + registerNewEncodedValue.add(avgSpeedEnc); + if (priorityEnc != null) + registerNewEncodedValue.add(priorityEnc); + } + + public void createTurnCostEncodedValues(List registerNewTurnCostEncodedValues) { + if (turnCostEnc != null) + registerNewTurnCostEncodedValues.add(turnCostEnc); + } + + public BooleanEncodedValue getAccessEnc() { + return accessEnc; + } + + public DecimalEncodedValue getAverageSpeedEnc() { + return avgSpeedEnc; + } + + public DecimalEncodedValue getPriorityEnc() { + return priorityEnc; + } + + public DecimalEncodedValue getTurnCostEnc() { + return turnCostEnc; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return getName(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/util/FlagEncoderFactory.java b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValuesFactory.java similarity index 87% rename from core/src/main/java/com/graphhopper/routing/util/FlagEncoderFactory.java rename to core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValuesFactory.java index b6efa42a716..85780abd813 100644 --- a/core/src/main/java/com/graphhopper/routing/util/FlagEncoderFactory.java +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleEncodedValuesFactory.java @@ -22,12 +22,10 @@ /** * @author Peter Karich */ -public interface FlagEncoderFactory { +public interface VehicleEncodedValuesFactory { String ROADS = "roads"; String CAR = "car"; - String CAR4WD = "car4wd"; String BIKE = "bike"; - String BIKE2 = "bike2"; String RACINGBIKE = "racingbike"; String MOUNTAINBIKE = "mtb"; String FOOT = "foot"; @@ -35,5 +33,6 @@ public interface FlagEncoderFactory { String MOTORCYCLE = "motorcycle"; String WHEELCHAIR = "wheelchair"; - FlagEncoder createFlagEncoder(String name, PMap configuration); + VehicleEncodedValues createVehicleEncodedValues(String name, PMap configuration); + } diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParser.java b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParser.java deleted file mode 100644 index 1dda76642c6..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParser.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ConditionalTagInspector; -import com.graphhopper.reader.ReaderNode; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.reader.osm.conditional.ConditionalOSMTagInspector; -import com.graphhopper.reader.osm.conditional.DateRangeParser; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.parsers.OSMRoadAccessParser; -import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.EdgeIteratorState; - -import java.util.*; - -import static com.graphhopper.routing.util.EncodingManager.getKey; - -/** - * Abstract class which handles flag decoding and encoding. Every encoder should be registered to a - * EncodingManager to be usable. If you want the full long to be stored you need to enable this in - * the GraphHopperStorage. - * - * @author Peter Karich - * @author Nop - * @see EncodingManager - */ -public abstract class VehicleTagParser implements FlagEncoder { - private final String name; - protected final Set intendedValues = new HashSet<>(5); - // order is important - protected final List restrictions = new ArrayList<>(5); - protected final Set restrictedValues = new HashSet<>(5); - protected final Set ferries = new HashSet<>(5); - protected final Set oneways = new HashSet<>(5); - // http://wiki.openstreetmap.org/wiki/Mapfeatures#Barrier - protected final Set barriers = new HashSet<>(5); - private final String propertiesString; - protected final BooleanEncodedValue accessEnc; - protected final DecimalEncodedValue avgSpeedEnc; - private final DecimalEncodedValue turnCostEnc; - protected BooleanEncodedValue roundaboutEnc; - // This value determines the maximal possible speed of any road regardless of the maxspeed value - // lower values allow more compact representation of the routing graph - protected double maxPossibleSpeed; - private boolean blockFords = true; - private boolean registered; - protected EncodedValueLookup encodedValueLookup; - private ConditionalTagInspector conditionalTagInspector; - protected FerrySpeedCalculator ferrySpeedCalc; - - /** - * @param speedBits specify the number of bits used for speed - * @param speedFactor specify the factor to multiply the stored value (can be used to increase - * or decrease accuracy of speed value) - * @param maxTurnCosts specify the maximum value used for turn costs, if this value is reached a - * turn is forbidden and results in costs of positive infinity. - */ - protected VehicleTagParser(String name, int speedBits, double speedFactor, boolean speedTwoDirections, int maxTurnCosts) { - this.name = name; - this.accessEnc = new SimpleBooleanEncodedValue(getKey(name, "access"), true); - this.avgSpeedEnc = new DecimalEncodedValueImpl(getKey(name, "average_speed"), speedBits, speedFactor, speedTwoDirections); - this.turnCostEnc = maxTurnCosts > 0 ? TurnCost.create(name, maxTurnCosts) : null; - this.propertiesString = "speed_factor=" + speedFactor + "|speed_bits=" + speedBits + "|turn_costs=" + (maxTurnCosts > 0); - - oneways.add("yes"); - oneways.add("true"); - oneways.add("1"); - oneways.add("-1"); - - ferries.add("shuttle_train"); - ferries.add("ferry"); - - restrictions.addAll(OSMRoadAccessParser.toOSMRestrictions(getTransportationMode())); - } - - protected void init(DateRangeParser dateRangeParser) { - if (registered) - throw new IllegalStateException("You must not register a FlagEncoder (" + this + ") twice or for two EncodingManagers!"); - registered = true; - - ferrySpeedCalc = new FerrySpeedCalculator(avgSpeedEnc.getSmallestNonZeroValue(), maxPossibleSpeed, 5); - setConditionalTagInspector(new ConditionalOSMTagInspector(Collections.singletonList(dateRangeParser), - restrictions, restrictedValues, intendedValues, false)); - } - - protected void setConditionalTagInspector(ConditionalTagInspector inspector) { - conditionalTagInspector = inspector; - } - - @Override - public boolean isRegistered() { - return registered; - } - - public boolean isBlockFords() { - return blockFords; - } - - protected void blockFords(boolean blockFords) { - this.blockFords = blockFords; - } - - protected void blockPrivate(boolean blockPrivate) { - if (!blockPrivate) { - if (!restrictedValues.remove("private")) - throw new IllegalStateException("no 'private' found in restrictedValues"); - intendedValues.add("private"); - } - } - - public ConditionalTagInspector getConditionalTagInspector() { - return conditionalTagInspector; - } - - /** - * Defines bits used for edge flags used for access, speed etc. - */ - public void createEncodedValues(List registerNewEncodedValue) { - registerNewEncodedValue.add(accessEnc); - registerNewEncodedValue.add(avgSpeedEnc); - roundaboutEnc = getBooleanEncodedValue(Roundabout.KEY); - } - - public void createTurnCostEncodedValues(List registerNewTurnCostEncodedValues) { - if (supportsTurnCosts()) - registerNewTurnCostEncodedValues.add(turnCostEnc); - } - - /** - * Analyze properties of a way and create the edge flags. This method is called in the second - * parsing step. - */ - public abstract IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way); - - /** - * Updates the given edge flags based on node tags - */ - public IntsRef handleNodeTags(IntsRef edgeFlags, Map nodeTags) { - if (!nodeTags.isEmpty()) { - // for now we just create a dummy reader node, because our encoders do not make use of the coordinates anyway - ReaderNode readerNode = new ReaderNode(0, 0, 0, nodeTags); - // block access for barriers - if (isBarrier(readerNode)) { - BooleanEncodedValue accessEnc = getAccessEnc(); - accessEnc.setBool(false, edgeFlags, false); - accessEnc.setBool(true, edgeFlags, false); - } - } - return edgeFlags; - } - - /** - * Decide whether a way is routable for a given mode of travel. This skips some ways before - * handleWayTags is called. - * - * @return the encoded value to indicate if this encoder allows travel or not. - */ - public abstract EncodingManager.Access getAccess(ReaderWay way); - - /** - * @return true if the given OSM node blocks access for this vehicle, false otherwise - */ - public boolean isBarrier(ReaderNode node) { - // note that this method will be only called for certain nodes as defined by OSMReader! - String firstValue = node.getFirstPriorityTag(restrictions); - if (restrictedValues.contains(firstValue) || node.hasTag("locked", "yes")) - return true; - else if (intendedValues.contains(firstValue)) - return false; - else if (node.hasTag("barrier", barriers)) - return true; - else - return blockFords && node.hasTag("ford", "yes"); - } - - @Override - public double getMaxSpeed() { - return maxPossibleSpeed; - } - - /** - * @return {@link Double#NaN} if no maxspeed found - */ - protected static double getMaxSpeed(ReaderWay way) { - double maxSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed")); - double fwdSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed:forward")); - if (isValidSpeed(fwdSpeed) && (!isValidSpeed(maxSpeed) || fwdSpeed < maxSpeed)) - maxSpeed = fwdSpeed; - - double backSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed:backward")); - if (isValidSpeed(backSpeed) && (!isValidSpeed(maxSpeed) || backSpeed < maxSpeed)) - maxSpeed = backSpeed; - - return maxSpeed; - } - - /** - * @return true if the given speed is not {@link Double#NaN} - */ - protected static boolean isValidSpeed(double speed) { - return !Double.isNaN(speed); - } - - /** - * Second parsing step. Invoked after splitting the edges. Currently used to offer a hook to - * calculate precise speed values based on elevation data stored in the specified edge. - */ - public void applyWayTags(ReaderWay way, EdgeIteratorState edge) { - } - - public final DecimalEncodedValue getAverageSpeedEnc() { - return avgSpeedEnc; - } - - public final BooleanEncodedValue getAccessEnc() { - return accessEnc; - } - - protected void setSpeed(boolean reverse, IntsRef edgeFlags, double speed) { - if (speed < avgSpeedEnc.getSmallestNonZeroValue()) { - avgSpeedEnc.setDecimal(reverse, edgeFlags, 0); - accessEnc.setBool(reverse, edgeFlags, false); - } else { - avgSpeedEnc.setDecimal(reverse, edgeFlags, Math.min(speed, getMaxSpeed())); - } - } - - protected String getPropertiesString() { - return propertiesString; - } - - @Override - public List getEncodedValues() { - return encodedValueLookup.getEncodedValues(); - } - - @Override - public T getEncodedValue(String key, Class encodedValueType) { - return encodedValueLookup.getEncodedValue(key, encodedValueType); - } - - @Override - public BooleanEncodedValue getBooleanEncodedValue(String key) { - return encodedValueLookup.getBooleanEncodedValue(key); - } - - @Override - public IntEncodedValue getIntEncodedValue(String key) { - return encodedValueLookup.getIntEncodedValue(key); - } - - @Override - public DecimalEncodedValue getDecimalEncodedValue(String key) { - return encodedValueLookup.getDecimalEncodedValue(key); - } - - @Override - public > EnumEncodedValue getEnumEncodedValue(String key, Class enumType) { - return encodedValueLookup.getEnumEncodedValue(key, enumType); - } - - @Override - public StringEncodedValue getStringEncodedValue(String key) { - return encodedValueLookup.getStringEncodedValue(key); - } - - public void setEncodedValueLookup(EncodedValueLookup encodedValueLookup) { - this.encodedValueLookup = encodedValueLookup; - } - - @Override - public boolean supportsTurnCosts() { - return turnCostEnc != null; - } - - public DecimalEncodedValue getTurnCostEnc() { - return turnCostEnc; - } - - @Override - public boolean supports(Class feature) { - return false; - } - - @Override - public boolean hasEncodedValue(String key) { - return encodedValueLookup.hasEncodedValue(key); - } - - public final List getRestrictions() { - return restrictions; - } - - public final String getName() { - return name; - } - - @Override - public String toString() { - return getName(); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/Car4WDTagParser.java b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParserFactory.java similarity index 66% rename from core/src/main/java/com/graphhopper/routing/util/Car4WDTagParser.java rename to core/src/main/java/com/graphhopper/routing/util/VehicleTagParserFactory.java index b2eabfb9734..4998a9609ba 100644 --- a/core/src/main/java/com/graphhopper/routing/util/Car4WDTagParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParserFactory.java @@ -15,20 +15,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.graphhopper.routing.util; +import com.graphhopper.routing.ev.EncodedValueLookup; import com.graphhopper.util.PMap; -/** - * Defines bit layout for cars with four wheel drive - * - * @author zstadler - */ -public class Car4WDTagParser extends CarTagParser { - - public Car4WDTagParser(PMap properties) { - super(new PMap(properties).putObject("name", properties.getString("name", "car4wd"))); - trackTypeSpeedMap.put("grade4", 5); // ... some hard or compressed materials - trackTypeSpeedMap.put("grade5", 5); // ... no hard materials. soil/sand/grass - } +public interface VehicleTagParserFactory { + VehicleTagParsers createParsers(EncodedValueLookup lookup, String name, PMap configuration); } diff --git a/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java new file mode 100644 index 00000000000..f62e13390a8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/VehicleTagParsers.java @@ -0,0 +1,120 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util; + +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.util.parsers.*; +import com.graphhopper.util.PMap; + +import java.util.Arrays; +import java.util.List; + +public class VehicleTagParsers { + private final TagParser accessParser; + private final TagParser speedParser; + private final TagParser priorityParser; + + public static VehicleTagParsers roads(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new RoadsAccessParser(lookup, properties), + new RoadsAverageSpeedParser(lookup, properties), + null + ); + } + + public static VehicleTagParsers car(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new CarAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new CarAverageSpeedParser(lookup, properties), + null + ); + } + + public static VehicleTagParsers bike(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new BikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new BikeAverageSpeedParser(lookup, properties), + new BikePriorityParser(lookup, properties) + ); + } + + public static VehicleTagParsers racingbike(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new RacingBikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new RacingBikeAverageSpeedParser(lookup, properties), + new RacingBikePriorityParser(lookup, properties) + ); + } + + public static VehicleTagParsers mtb(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new MountainBikeAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new MountainBikeAverageSpeedParser(lookup, properties), + new MountainBikePriorityParser(lookup, properties) + ); + } + + public static VehicleTagParsers foot(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new FootAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new FootAverageSpeedParser(lookup, properties), + new FootPriorityParser(lookup, properties) + ); + } + + public static VehicleTagParsers motorcycle(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new MotorcycleAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new MotorcycleAverageSpeedParser(lookup, properties), + new MotorcyclePriorityParser(lookup, properties) + ); + } + + public static VehicleTagParsers wheelchair(EncodedValueLookup lookup, PMap properties) { + return new VehicleTagParsers( + new WheelchairAccessParser(lookup, properties).init(properties.getObject("date_range_parser", new DateRangeParser())), + new WheelchairAverageSpeedParser(lookup, properties), + new WheelchairPriorityParser(lookup, properties) + ); + } + + public VehicleTagParsers(TagParser accessParser, TagParser speedParser, TagParser priorityParser) { + this.accessParser = accessParser; + this.speedParser = speedParser; + this.priorityParser = priorityParser; + } + + public TagParser getAccessParser() { + return accessParser; + } + + public TagParser getSpeedParser() { + return speedParser; + } + + public TagParser getPriorityParser() { + return priorityParser; + } + + public List getTagParsers() { + return Arrays.asList(accessParser, speedParser, priorityParser); + } + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/WayAccess.java b/core/src/main/java/com/graphhopper/routing/util/WayAccess.java new file mode 100644 index 00000000000..de629db61ff --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/WayAccess.java @@ -0,0 +1,39 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util; + +public enum WayAccess { + WAY, FERRY, OTHER, CAN_SKIP; + + public boolean isFerry() { + return this.ordinal() == FERRY.ordinal(); + } + + public boolean isWay() { + return this.ordinal() == WAY.ordinal(); + } + + public boolean isOther() { + return this.ordinal() == OTHER.ordinal(); + } + + public boolean canSkip() { + return this.ordinal() == CAN_SKIP.ordinal(); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/WheelchairTagParser.java b/core/src/main/java/com/graphhopper/routing/util/WheelchairTagParser.java deleted file mode 100644 index 8a71e6c67dc..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/WheelchairTagParser.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.FetchMode; -import com.graphhopper.util.PMap; -import com.graphhopper.util.PointList; - -import java.util.HashSet; -import java.util.Set; -import java.util.TreeMap; - -import static com.graphhopper.routing.util.PriorityCode.AVOID; -import static com.graphhopper.routing.util.PriorityCode.VERY_NICE; - -/** - * A flag encoder for wheelchairs. - * - * @author don-philipe - */ -public class WheelchairTagParser extends FootTagParser { - private final Set excludeSurfaces = new HashSet<>(); - private final Set excludeSmoothness = new HashSet<>(); - private final int maxInclinePercent = 6; - - public WheelchairTagParser() { - this(4, 1); - } - - public WheelchairTagParser(PMap properties) { - this(properties.getInt("speed_bits", 4), properties.getDouble("speed_factor", 1)); - - blockPrivate(properties.getBool("block_private", true)); - blockFords(properties.getBool("block_fords", false)); - } - - protected WheelchairTagParser(int speedBits, double speedFactor) { - super("wheelchair", speedBits, speedFactor, true); - - restrictions.add("wheelchair"); - - barriers.add("handrail"); - barriers.add("wall"); - barriers.add("turnstile"); - barriers.add("kissing_gate"); - barriers.add("stile"); - - safeHighwayTags.add("footway"); - safeHighwayTags.add("pedestrian"); - safeHighwayTags.add("living_street"); - safeHighwayTags.add("residential"); - safeHighwayTags.add("service"); - safeHighwayTags.add("platform"); - - safeHighwayTags.remove("steps"); - safeHighwayTags.remove("track"); - - allowedHighwayTags.clear(); - allowedHighwayTags.addAll(safeHighwayTags); - allowedHighwayTags.addAll(avoidHighwayTags); - allowedHighwayTags.add("cycleway"); - allowedHighwayTags.add("unclassified"); - allowedHighwayTags.add("road"); - - excludeSurfaces.add("cobblestone"); - excludeSurfaces.add("gravel"); - excludeSurfaces.add("sand"); - - excludeSmoothness.add("bad"); - excludeSmoothness.add("very_bad"); - excludeSmoothness.add("horrible"); - excludeSmoothness.add("very_horrible"); - excludeSmoothness.add("impassable"); - - allowedSacScale.clear(); - - maxPossibleSpeed = avgSpeedEnc.getNextStorableValue(FERRY_SPEED); - } - - /** - * Avoid some more ways than for pedestrian like hiking trails. - */ - @Override - public EncodingManager.Access getAccess(ReaderWay way) { - if (way.hasTag("surface", excludeSurfaces)) { - if (!way.hasTag("sidewalk", sidewalkValues)) { - return EncodingManager.Access.CAN_SKIP; - } else { - String sidewalk = way.getTag("sidewalk"); - if (way.hasTag("sidewalk:" + sidewalk + ":surface", excludeSurfaces)) { - return EncodingManager.Access.CAN_SKIP; - } - } - } - - if (way.hasTag("smoothness", excludeSmoothness)) { - if (!way.hasTag("sidewalk", sidewalkValues)) { - return EncodingManager.Access.CAN_SKIP; - } else { - String sidewalk = way.getTag("sidewalk"); - if (way.hasTag("sidewalk:" + sidewalk + ":smoothness", excludeSmoothness)) { - return EncodingManager.Access.CAN_SKIP; - } - } - } - - if (way.hasTag("incline")) { - String tagValue = way.getTag("incline"); - if (tagValue.endsWith("%") || tagValue.endsWith("°")) { - try { - double incline = Double.parseDouble(tagValue.substring(0, tagValue.length() - 1)); - if (tagValue.endsWith("°")) { - incline = Math.tan(incline * Math.PI / 180) * 100; - } - - if (-maxInclinePercent > incline || incline > maxInclinePercent) { - return EncodingManager.Access.CAN_SKIP; - } - } catch (NumberFormatException ex) { - } - } - } - - if (way.hasTag("kerb", "raised")) - return EncodingManager.Access.CAN_SKIP; - - if (way.hasTag("kerb")) { - String tagValue = way.getTag("kerb"); - if (tagValue.endsWith("cm") || tagValue.endsWith("mm")) { - try { - float kerbHeight = Float.parseFloat(tagValue.substring(0, tagValue.length() - 2)); - if (tagValue.endsWith("mm")) { - kerbHeight /= 100; - } - - int maxKerbHeightCm = 3; - if (kerbHeight > maxKerbHeightCm) { - return EncodingManager.Access.CAN_SKIP; - } - } catch (NumberFormatException ex) { - } - } - } - - return super.getAccess(way); - } - - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way) { - EncodingManager.Access access = getAccess(way); - if (access.canSkip()) - return edgeFlags; - - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - if (!access.isFerry()) { - setSpeed(edgeFlags, true, true, MEAN_SPEED); - } else { - double ferrySpeed = ferrySpeedCalc.getSpeed(way); - setSpeed(edgeFlags, true, true, ferrySpeed); - } - - Integer priorityFromRelation = routeMap.get(footRouteEnc.getEnum(false, edgeFlags)); - priorityWayEncoder.setDecimal(false, edgeFlags, PriorityCode.getValue(handlePriority(way, priorityFromRelation))); - return edgeFlags; - } - - /** - * Calculate slopes from elevation data and set speed according to that. In-/declines between smallInclinePercent - * and maxInclinePercent will reduce speed to SLOW_SPEED. In-/declines above maxInclinePercent will result in zero - * speed. - */ - @Override - public void applyWayTags(ReaderWay way, EdgeIteratorState edge) { - PointList pl = edge.fetchWayGeometry(FetchMode.ALL); - double fullDist2D = edge.getDistance(); - if (Double.isInfinite(fullDist2D)) - throw new IllegalStateException("Infinite distance should not happen due to #435. way ID=" + way.getId()); - - // skip elevation data adjustment for too short segments, TODO improve the elevation data handling and/or use the same mechanism as in bike2 - if (fullDist2D < 20 || !pl.is3D()) - return; - - double prevEle = pl.getEle(0); - double eleDelta = pl.getEle(pl.size() - 1) - prevEle; - double elePercent = eleDelta / fullDist2D * 100; - int smallInclinePercent = 3; - double fwdSpeed = 0, bwdSpeed = 0; - if (elePercent > smallInclinePercent && elePercent < maxInclinePercent) { - fwdSpeed = SLOW_SPEED; - bwdSpeed = MEAN_SPEED; - } else if (elePercent < -smallInclinePercent && elePercent > -maxInclinePercent) { - fwdSpeed = MEAN_SPEED; - bwdSpeed = SLOW_SPEED; - } else if (elePercent > maxInclinePercent || elePercent < -maxInclinePercent) { - // it can be problematic to exclude roads due to potential bad elevation data (e.g.delta for narrow nodes could be too high) - // so exclude only when we are certain - if (fullDist2D > 50) edge.set(accessEnc, false, false); - - fwdSpeed = SLOW_SPEED; - bwdSpeed = SLOW_SPEED; - edge.set(priorityWayEncoder, PriorityCode.getValue(PriorityCode.REACH_DESTINATION.getValue())); - } - - if (fwdSpeed > 0 && edge.get(accessEnc)) - setSpeed(edge.getFlags(), true, false, fwdSpeed); - if (bwdSpeed > 0 && edge.getReverse(accessEnc)) - setSpeed(edge.getFlags(), false, true, bwdSpeed); - } - - /** - * First get priority from {@link FootTagParser#handlePriority(ReaderWay, Integer)} then evaluate wheelchair specific - * tags. - * - * @return a priority for the given way - */ - @Override - protected int handlePriority(ReaderWay way, Integer priorityFromRelation) { - TreeMap weightToPrioMap = new TreeMap<>(); - - weightToPrioMap.put(100d, super.handlePriority(way, priorityFromRelation)); - - if (way.hasTag("wheelchair", "designated")) { - weightToPrioMap.put(102d, VERY_NICE.getValue()); - } else if (way.hasTag("wheelchair", "limited")) { - weightToPrioMap.put(102d, AVOID.getValue()); - } - - return weightToPrioMap.lastEntry().getValue(); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/CountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/CountryRule.java index cf67f15761a..24f544eb737 100644 --- a/core/src/main/java/com/graphhopper/routing/util/countryrules/CountryRule.java +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/CountryRule.java @@ -35,7 +35,7 @@ default RoadAccess getAccess(ReaderWay readerWay, TransportationMode transportat return currentRoadAccess; } - default Toll getToll(ReaderWay readerWay, TransportationMode transportationMode, Toll currentToll) { + default Toll getToll(ReaderWay readerWay, Toll currentToll) { return currentToll; } } diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/CountryRuleFactory.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/CountryRuleFactory.java index 7a68a6dbfd1..f9ed9e7f0fb 100644 --- a/core/src/main/java/com/graphhopper/routing/util/countryrules/CountryRuleFactory.java +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/CountryRuleFactory.java @@ -25,14 +25,64 @@ import java.util.Map; import com.graphhopper.routing.ev.Country; +import com.graphhopper.routing.util.countryrules.europe.*; public class CountryRuleFactory { private final Map rules = new EnumMap<>(Country.class); public CountryRuleFactory() { + + // Europe + rules.put(ALB, new AlbaniaCountryRule()); + rules.put(AND, new AndorraCountryRule()); rules.put(AUT, new AustriaCountryRule()); + rules.put(BEL, new BelgiumCountryRule()); + rules.put(BGR, new BulgariaCountryRule()); + rules.put(BIH, new BosniaHerzegovinaCountryRule()); + rules.put(BLR, new BelarusCountryRule()); + rules.put(CHE, new SwitzerlandCountryRule()); + rules.put(CZE, new CzechiaCountryRule()); rules.put(DEU, new GermanyCountryRule()); + rules.put(DNK, new DenmarkCountryRule()); + rules.put(ESP, new SpainCountryRule()); + rules.put(EST, new EstoniaCountryRule()); + rules.put(FIN, new FinlandCountryRule()); + rules.put(FRA, new FranceCountryRule()); + rules.put(FRO, new FaroeIslandsCountryRule()); + rules.put(GGY, new GuernseyCountryRule()); + rules.put(GIB, new GibraltarCountryRule()); + rules.put(GBR, new UnitedKingdomCountryRule()); + rules.put(GRC, new GreeceCountryRule()); + rules.put(HRV, new CroatiaCountryRule()); + rules.put(HUN, new HungaryCountryRule()); + rules.put(IMN, new IsleOfManCountryRule()); + rules.put(IRL, new IrelandCountryRule()); + rules.put(ISL, new IcelandCountryRule()); + rules.put(ITA, new ItalyCountryRule()); + rules.put(JEY, new JerseyCountryRule()); + rules.put(LIE, new LiechtensteinCountryRule()); + rules.put(LTU, new LithuaniaCountryRule()); + rules.put(LUX, new LuxembourgCountryRule()); + rules.put(LVA, new LatviaCountryRule()); + rules.put(MCO, new MonacoCountryRule()); + rules.put(MDA, new MoldovaCountryRule()); + rules.put(MKD, new NorthMacedoniaCountryRule()); + rules.put(MLT, new MaltaCountryRule()); + rules.put(MNE, new MontenegroCountryRule()); + rules.put(NLD, new NetherlandsCountryRule()); + rules.put(NOR, new NorwayCountryRule()); + rules.put(POL, new PolandCountryRule()); + rules.put(PRT, new PortugalCountryRule()); + rules.put(ROU, new RomaniaCountryRule()); + rules.put(RUS, new RussiaCountryRule()); + rules.put(SMR, new SanMarinoCountryRule()); + rules.put(SRB, new SerbiaCountryRule()); + rules.put(SVK, new SlovakiaCountryRule()); + rules.put(SVN, new SloveniaCountryRule()); + rules.put(SWE, new SwedenCountryRule()); + rules.put(UKR, new UkraineCountryRule()); + rules.put(VAT, new VaticanCityCountryRule()); } public CountryRule getCountryRule(Country country) { diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AlbaniaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AlbaniaCountryRule.java new file mode 100644 index 00000000000..4201501ae82 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AlbaniaCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class AlbaniaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AndorraCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AndorraCountryRule.java new file mode 100644 index 00000000000..2de857f7aa1 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AndorraCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class AndorraCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/AustriaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AustriaCountryRule.java similarity index 92% rename from core/src/main/java/com/graphhopper/routing/util/countryrules/AustriaCountryRule.java rename to core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AustriaCountryRule.java index 64b0a73e87f..6fb3accdb9a 100644 --- a/core/src/main/java/com/graphhopper/routing/util/countryrules/AustriaCountryRule.java +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/AustriaCountryRule.java @@ -16,13 +16,14 @@ * limitations under the License. */ -package com.graphhopper.routing.util.countryrules; +package com.graphhopper.routing.util.countryrules.europe; import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.RoadAccess; import com.graphhopper.routing.ev.RoadClass; import com.graphhopper.routing.ev.Toll; import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.routing.util.countryrules.CountryRule; public class AustriaCountryRule implements CountryRule { @@ -74,8 +75,8 @@ public RoadAccess getAccess(ReaderWay readerWay, TransportationMode transportati } @Override - public Toll getToll(ReaderWay readerWay, TransportationMode transportationMode, Toll currentToll) { - if (!transportationMode.isMotorVehicle() || currentToll != Toll.MISSING) { + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { return currentToll; } diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BelarusCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BelarusCountryRule.java new file mode 100644 index 00000000000..9b3b11dc837 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BelarusCountryRule.java @@ -0,0 +1,41 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class BelarusCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (roadClass == RoadClass.MOTORWAY) { + return Toll.HGV; + } + + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BelgiumCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BelgiumCountryRule.java new file mode 100644 index 00000000000..ba9b30ac1f2 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BelgiumCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Belgian roads + * + * @author Thomas Butz + */ +public class BelgiumCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.HGV; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BosniaHerzegovinaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BosniaHerzegovinaCountryRule.java new file mode 100644 index 00000000000..4b3fbe23bcb --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BosniaHerzegovinaCountryRule.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class BosniaHerzegovinaCountryRule implements CountryRule { + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BulgariaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BulgariaCountryRule.java new file mode 100644 index 00000000000..a8e642a4554 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/BulgariaCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Bulgarian roads + * + * @author Thomas Butz + */ +public class BulgariaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/CroatiaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/CroatiaCountryRule.java new file mode 100644 index 00000000000..948eb5a4283 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/CroatiaCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Croatian roads + * + * @author Thomas Butz + */ +public class CroatiaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/CzechiaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/CzechiaCountryRule.java new file mode 100644 index 00000000000..a29e23d929d --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/CzechiaCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for the roads of the Czech Republic. + * + * @author Thomas Butz + */ +public class CzechiaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/DenmarkCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/DenmarkCountryRule.java new file mode 100644 index 00000000000..177cdaab890 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/DenmarkCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Polish roads + * + * @author Thomas Butz + */ +public class DenmarkCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.HGV; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/EstoniaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/EstoniaCountryRule.java new file mode 100644 index 00000000000..3d00d7278dd --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/EstoniaCountryRule.java @@ -0,0 +1,41 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class EstoniaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (roadClass == RoadClass.MOTORWAY || roadClass == RoadClass.TRUNK || roadClass == RoadClass.PRIMARY) { + return Toll.HGV; + } + + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FaroeIslandsCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FaroeIslandsCountryRule.java new file mode 100644 index 00000000000..3838ba68d67 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FaroeIslandsCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class FaroeIslandsCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FinlandCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FinlandCountryRule.java new file mode 100644 index 00000000000..687cfa95428 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FinlandCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class FinlandCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FranceCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FranceCountryRule.java new file mode 100644 index 00000000000..e35f2977e23 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/FranceCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for French roads + * + * @author Thomas Butz + */ +public class FranceCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/GermanyCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GermanyCountryRule.java similarity index 93% rename from core/src/main/java/com/graphhopper/routing/util/countryrules/GermanyCountryRule.java rename to core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GermanyCountryRule.java index 49a9d01aa39..35a59490ea8 100644 --- a/core/src/main/java/com/graphhopper/routing/util/countryrules/GermanyCountryRule.java +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GermanyCountryRule.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package com.graphhopper.routing.util.countryrules; +package com.graphhopper.routing.util.countryrules.europe; import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.MaxSpeed; @@ -24,6 +24,7 @@ import com.graphhopper.routing.ev.RoadClass; import com.graphhopper.routing.ev.Toll; import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.routing.util.countryrules.CountryRule; /** * @author Robin Boldt @@ -82,8 +83,8 @@ public RoadAccess getAccess(ReaderWay readerWay, TransportationMode transportati } @Override - public Toll getToll(ReaderWay readerWay, TransportationMode transportationMode, Toll currentToll) { - if (!transportationMode.isMotorVehicle() || currentToll != Toll.MISSING) { + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { return currentToll; } diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GibraltarCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GibraltarCountryRule.java new file mode 100644 index 00000000000..adfbcf0b741 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GibraltarCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class GibraltarCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GreeceCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GreeceCountryRule.java new file mode 100644 index 00000000000..4d28cbc0872 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GreeceCountryRule.java @@ -0,0 +1,41 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class GreeceCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (roadClass == RoadClass.MOTORWAY) { + return Toll.ALL; + } + + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GuernseyCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GuernseyCountryRule.java new file mode 100644 index 00000000000..6c5e5e47dd9 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/GuernseyCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class GuernseyCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/HungaryCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/HungaryCountryRule.java new file mode 100644 index 00000000000..6e288bda384 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/HungaryCountryRule.java @@ -0,0 +1,49 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Hungarian roads + * + * @author Thomas Butz + */ +public class HungaryCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + switch (roadClass) { + case MOTORWAY: + return Toll.ALL; + case TRUNK: + case PRIMARY: + return Toll.HGV; + default: + return currentToll; + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IcelandCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IcelandCountryRule.java new file mode 100644 index 00000000000..1342e71da7c --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IcelandCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class IcelandCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IrelandCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IrelandCountryRule.java new file mode 100644 index 00000000000..105162ec406 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IrelandCountryRule.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class IrelandCountryRule implements CountryRule { + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IsleOfManCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IsleOfManCountryRule.java new file mode 100644 index 00000000000..cb391439a6c --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/IsleOfManCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class IsleOfManCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/ItalyCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/ItalyCountryRule.java new file mode 100644 index 00000000000..ba70969f299 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/ItalyCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Italian roads + * + * @author Thomas Butz + */ +public class ItalyCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/JerseyCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/JerseyCountryRule.java new file mode 100644 index 00000000000..c3220b4a9eb --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/JerseyCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class JerseyCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LatviaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LatviaCountryRule.java new file mode 100644 index 00000000000..e55b9e7d0a9 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LatviaCountryRule.java @@ -0,0 +1,41 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class LatviaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (roadClass == RoadClass.MOTORWAY || roadClass == RoadClass.TRUNK || roadClass == RoadClass.PRIMARY) { + return Toll.HGV; + } + + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LiechtensteinCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LiechtensteinCountryRule.java new file mode 100644 index 00000000000..d7e636b6578 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LiechtensteinCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class LiechtensteinCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LithuaniaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LithuaniaCountryRule.java new file mode 100644 index 00000000000..66d1f6499d8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LithuaniaCountryRule.java @@ -0,0 +1,41 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class LithuaniaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (roadClass == RoadClass.MOTORWAY || roadClass == RoadClass.TRUNK || roadClass == RoadClass.PRIMARY) { + return Toll.HGV; + } + + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LuxembourgCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LuxembourgCountryRule.java new file mode 100644 index 00000000000..19b6b9f7883 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/LuxembourgCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Luxembourgish roads + * + * @author Thomas Butz + */ +public class LuxembourgCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.HGV; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MaltaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MaltaCountryRule.java new file mode 100644 index 00000000000..88a3ae03cc5 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MaltaCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class MaltaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MoldovaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MoldovaCountryRule.java new file mode 100644 index 00000000000..c5e4beddfce --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MoldovaCountryRule.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class MoldovaCountryRule implements CountryRule { + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MonacoCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MonacoCountryRule.java new file mode 100644 index 00000000000..277137176f6 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MonacoCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class MonacoCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MontenegroCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MontenegroCountryRule.java new file mode 100644 index 00000000000..e505b0b5c1e --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/MontenegroCountryRule.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class MontenegroCountryRule implements CountryRule { + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NetherlandsCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NetherlandsCountryRule.java new file mode 100644 index 00000000000..2d8aa038b05 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NetherlandsCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Netherlands roads + * + * @author Thomas Butz + */ +public class NetherlandsCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.HGV; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NorthMacedoniaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NorthMacedoniaCountryRule.java new file mode 100644 index 00000000000..ffb94a76356 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NorthMacedoniaCountryRule.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class NorthMacedoniaCountryRule implements CountryRule { + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NorwayCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NorwayCountryRule.java new file mode 100644 index 00000000000..c670d9af40a --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/NorwayCountryRule.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class NorwayCountryRule implements CountryRule { + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/PolandCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/PolandCountryRule.java new file mode 100644 index 00000000000..fb26f885f38 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/PolandCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Polish roads + * + * @author Thomas Butz + */ +public class PolandCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.HGV; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/PortugalCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/PortugalCountryRule.java new file mode 100644 index 00000000000..8f3c6c85b76 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/PortugalCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Portuguese roads + * + * @author Thomas Butz + */ +public class PortugalCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RomaniaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RomaniaCountryRule.java new file mode 100644 index 00000000000..cb310251e1f --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RomaniaCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Croatian roads + * + * @author Thomas Butz + */ +public class RomaniaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (roadClass == RoadClass.MOTORWAY || roadClass == RoadClass.TRUNK) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RomaniaSpatialRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RomaniaSpatialRule.java new file mode 100644 index 00000000000..efa447f344c --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RomaniaSpatialRule.java @@ -0,0 +1,48 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Romanian roads + * + * @author Thomas Butz + */ +public class RomaniaSpatialRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + switch (roadClass) { + case MOTORWAY: + case TRUNK: + case PRIMARY: + return Toll.ALL; + default: + return currentToll; + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RussiaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RussiaCountryRule.java new file mode 100644 index 00000000000..11776a22080 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/RussiaCountryRule.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class RussiaCountryRule implements CountryRule { + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SanMarinoCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SanMarinoCountryRule.java new file mode 100644 index 00000000000..ec4ae43c31c --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SanMarinoCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class SanMarinoCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SerbiaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SerbiaCountryRule.java new file mode 100644 index 00000000000..5ced7eac829 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SerbiaCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Serbian roads + * + * @author Thomas Butz + */ +public class SerbiaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SlovakiaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SlovakiaCountryRule.java new file mode 100644 index 00000000000..be03fe0da76 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SlovakiaCountryRule.java @@ -0,0 +1,47 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Slovakian roads + * + * @author Thomas Butz + */ +public class SlovakiaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + switch (roadClass) { + case MOTORWAY: + case TRUNK: + return Toll.ALL; + default: + return currentToll; + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SloveniaCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SloveniaCountryRule.java new file mode 100644 index 00000000000..5bfcc699222 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SloveniaCountryRule.java @@ -0,0 +1,47 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Slovenian roads + * + * @author Thomas Butz + */ +public class SloveniaCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + switch (roadClass) { + case MOTORWAY: + case TRUNK: + return Toll.ALL; + default: + return currentToll; + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SpainCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SpainCountryRule.java new file mode 100644 index 00000000000..727b06b22f9 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SpainCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Spanish roads + * + * @author Thomas Butz + */ +public class SpainCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.ALL; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SwedenCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SwedenCountryRule.java new file mode 100644 index 00000000000..4d303b2f246 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SwedenCountryRule.java @@ -0,0 +1,43 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Swedish roads + * + * @author Thomas Butz + */ +public class SwedenCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (RoadClass.MOTORWAY == roadClass) + return Toll.HGV; + return currentToll; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SwitzerlandCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SwitzerlandCountryRule.java new file mode 100644 index 00000000000..04745249df7 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/SwitzerlandCountryRule.java @@ -0,0 +1,50 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +/** + * Defines the default rules for Swiss roads + * + * @author Thomas Butz + */ +public class SwitzerlandCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + RoadClass roadClass = RoadClass.find(readerWay.getTag("highway", "")); + if (currentToll != null) + return currentToll; + + switch (roadClass) { + case MOTORWAY: + case TRUNK: + return Toll.ALL; + default: + return currentToll; + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/UkraineCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/UkraineCountryRule.java new file mode 100644 index 00000000000..acbed2fb5b0 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/UkraineCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class UkraineCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/UnitedKingdomCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/UnitedKingdomCountryRule.java new file mode 100644 index 00000000000..5bc7e4e5856 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/UnitedKingdomCountryRule.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class UnitedKingdomCountryRule implements CountryRule { + +} diff --git a/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/VaticanCityCountryRule.java b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/VaticanCityCountryRule.java new file mode 100644 index 00000000000..1c1e7f50af0 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/countryrules/europe/VaticanCityCountryRule.java @@ -0,0 +1,35 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.countryrules.europe; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.util.countryrules.CountryRule; + +public class VaticanCityCountryRule implements CountryRule { + + @Override + public Toll getToll(ReaderWay readerWay, Toll currentToll) { + if (currentToll != Toll.MISSING) { + return currentToll; + } + + return Toll.NO; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAccessParser.java new file mode 100644 index 00000000000..bd876cd2fee --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAccessParser.java @@ -0,0 +1,129 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderNode; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.ConditionalOSMTagInspector; +import com.graphhopper.reader.osm.conditional.ConditionalTagInspector; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.storage.IntsRef; + +import java.util.*; + +public abstract class AbstractAccessParser implements TagParser { + static final Collection FERRIES = Arrays.asList("shuttle_train", "ferry"); + static final Collection ONEWAYS = Arrays.asList("yes", "true", "1", "-1"); + static final Collection INTENDED = Arrays.asList("yes", "designated", "official", "permissive"); + + // order is important + protected final List restrictions = new ArrayList<>(5); + protected final Set restrictedValues = new HashSet<>(5); + + protected final Set intendedValues = new HashSet<>(INTENDED); + protected final Set ferries = new HashSet<>(FERRIES); + protected final Set oneways = new HashSet<>(ONEWAYS); + // http://wiki.openstreetmap.org/wiki/Mapfeatures#Barrier + protected final Set barriers = new HashSet<>(5); + protected final BooleanEncodedValue accessEnc; + private boolean blockFords = true; + private ConditionalTagInspector conditionalTagInspector; + + protected AbstractAccessParser(BooleanEncodedValue accessEnc, TransportationMode transportationMode) { + this.accessEnc = accessEnc; + + restrictedValues.add("no"); + restrictedValues.add("restricted"); + restrictedValues.add("military"); + restrictedValues.add("emergency"); + restrictedValues.add("private"); + restrictedValues.add("permit"); + + restrictions.addAll(OSMRoadAccessParser.toOSMRestrictions(transportationMode)); + } + + public AbstractAccessParser init(DateRangeParser dateRangeParser) { + setConditionalTagInspector(new ConditionalOSMTagInspector(Collections.singletonList(dateRangeParser), + restrictions, restrictedValues, intendedValues, false)); + return this; + } + + protected void setConditionalTagInspector(ConditionalTagInspector inspector) { + conditionalTagInspector = inspector; + } + + public boolean isBlockFords() { + return blockFords; + } + + protected void blockFords(boolean blockFords) { + this.blockFords = blockFords; + } + + protected void blockPrivate(boolean blockPrivate) { + if (!blockPrivate) { + if (!restrictedValues.remove("private")) + throw new IllegalStateException("no 'private' found in restrictedValues"); + if (!restrictedValues.remove("permit")) + throw new IllegalStateException("no 'permit' found in restrictedValues"); + intendedValues.add("private"); + intendedValues.add("permit"); + } + } + + public ConditionalTagInspector getConditionalTagInspector() { + return conditionalTagInspector; + } + + protected void handleBarrierEdge(int edgeId, EdgeIntAccess edgeIntAccess, Map nodeTags) { + // for now we just create a dummy reader node, because our encoders do not make use of the coordinates anyway + ReaderNode readerNode = new ReaderNode(0, 0, 0, nodeTags); + // block access for barriers + if (isBarrier(readerNode)) { + BooleanEncodedValue accessEnc = getAccessEnc(); + accessEnc.setBool(false, edgeId, edgeIntAccess, false); + accessEnc.setBool(true, edgeId, edgeIntAccess, false); + } + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + handleWayTags(edgeId, edgeIntAccess, way); + } + + public abstract void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way); + + /** + * @return true if the given OSM node blocks access for this vehicle, false otherwise + */ + public boolean isBarrier(ReaderNode node) { + // note that this method will be only called for certain nodes as defined by OSMReader! + String firstValue = node.getFirstPriorityTag(restrictions); + if (restrictedValues.contains(firstValue) || node.hasTag("locked", "yes")) + return true; + else if (intendedValues.contains(firstValue)) + return false; + else if (node.hasTag("barrier", barriers)) + return true; + else + return blockFords && node.hasTag("ford", "yes"); + } + + public final BooleanEncodedValue getAccessEnc() { + return accessEnc; + } + + public final List getRestrictions() { + return restrictions; + } + + public final String getName() { + return accessEnc.getName(); + } + + @Override + public String toString() { + return getName(); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAverageSpeedParser.java new file mode 100644 index 00000000000..b9b9ce74c52 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/AbstractAverageSpeedParser.java @@ -0,0 +1,83 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.util.FerrySpeedCalculator; +import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; +import com.graphhopper.storage.IntsRef; + +import java.util.HashSet; +import java.util.Set; + +import static com.graphhopper.routing.util.parsers.AbstractAccessParser.FERRIES; + +public abstract class AbstractAverageSpeedParser implements TagParser { + // http://wiki.openstreetmap.org/wiki/Mapfeatures#Barrier + protected final DecimalEncodedValue avgSpeedEnc; + // This value determines the maximal possible speed of any road regardless of the maxspeed value + // lower values allow more compact representation of the routing graph + protected final double maxPossibleSpeed; + protected final Set ferries = new HashSet<>(FERRIES); + protected final FerrySpeedCalculator ferrySpeedCalc; + + protected AbstractAverageSpeedParser(DecimalEncodedValue speedEnc, double maxPossibleSpeed) { + this.maxPossibleSpeed = maxPossibleSpeed; + this.avgSpeedEnc = speedEnc; + + ferrySpeedCalc = new FerrySpeedCalculator(speedEnc.getSmallestNonZeroValue(), maxPossibleSpeed, 5); + } + + public double getMaxSpeed() { + return maxPossibleSpeed; + } + + /** + * @return {@link Double#NaN} if no maxspeed found + */ + public static double getMaxSpeed(ReaderWay way, boolean bwd) { + double maxSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed")); + double directedMaxSpeed = OSMValueExtractor.stringToKmh(way.getTag(bwd ? "maxspeed:backward" : "maxspeed:forward")); + if (isValidSpeed(directedMaxSpeed) && (!isValidSpeed(maxSpeed) || directedMaxSpeed < maxSpeed)) + maxSpeed = directedMaxSpeed; + return maxSpeed; + } + + /** + * @return true if the given speed is not {@link Double#NaN} + */ + protected static boolean isValidSpeed(double speed) { + return !Double.isNaN(speed); + } + + public final DecimalEncodedValue getAverageSpeedEnc() { + return avgSpeedEnc; + } + + protected void setSpeed(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, double speed) { + // special case when speed is non-zero but would be "rounded down" to 0 due to the low precision of the EncodedValue + if (speed > 0.1 && speed < avgSpeedEnc.getSmallestNonZeroValue()) + speed = avgSpeedEnc.getSmallestNonZeroValue(); + if (speed < avgSpeedEnc.getSmallestNonZeroValue()) { + avgSpeedEnc.setDecimal(reverse, edgeId, edgeIntAccess, 0); + } else { + avgSpeedEnc.setDecimal(reverse, edgeId, edgeIntAccess, Math.min(speed, getMaxSpeed())); + } + } + + public final String getName() { + return avgSpeedEnc.getName(); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + handleWayTags(edgeId, edgeIntAccess, way); + } + + public abstract void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way); + + @Override + public String toString() { + return getName(); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAccessParser.java new file mode 100644 index 00000000000..e67fbfe31e6 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAccessParser.java @@ -0,0 +1,24 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.Roundabout; +import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.util.PMap; + +public class BikeAccessParser extends BikeCommonAccessParser { + + public BikeAccessParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "bike"))), + lookup.getBooleanEncodedValue(Roundabout.KEY)); + blockPrivate(properties.getBool("block_private", true)); + blockFords(properties.getBool("block_fords", false)); + } + + public BikeAccessParser(BooleanEncodedValue accessEnc, BooleanEncodedValue roundaboutEnc) { + super(accessEnc, roundaboutEnc); + barriers.add("kissing_gate"); + barriers.add("stile"); + barriers.add("turnstile"); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAverageSpeedParser.java new file mode 100644 index 00000000000..b7e742ca061 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeAverageSpeedParser.java @@ -0,0 +1,17 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; + +public class BikeAverageSpeedParser extends BikeCommonAverageSpeedParser { + + public BikeAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "bike"))), + lookup.getEnumEncodedValue(Smoothness.KEY, Smoothness.class)); + } + + public BikeAverageSpeedParser(DecimalEncodedValue speedEnc, EnumEncodedValue smoothnessEnc) { + super(speedEnc, smoothnessEnc); + addPushingSection("path"); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAccessParser.java new file mode 100644 index 00000000000..59a9f5fc191 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAccessParser.java @@ -0,0 +1,166 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.routing.util.WayAccess; + +import java.util.*; + +public abstract class BikeCommonAccessParser extends AbstractAccessParser implements TagParser { + + private static final Set OPP_LANES = new HashSet<>(Arrays.asList("opposite", "opposite_lane", "opposite_track")); + private final Set allowedHighways = new HashSet<>(); + private final BooleanEncodedValue roundaboutEnc; + + protected BikeCommonAccessParser(BooleanEncodedValue accessEnc, BooleanEncodedValue roundaboutEnc) { + super(accessEnc, TransportationMode.BIKE); + + this.roundaboutEnc = roundaboutEnc; + + restrictedValues.add("agricultural"); + restrictedValues.add("forestry"); + restrictedValues.add("delivery"); + + intendedValues.add("yes"); + intendedValues.add("designated"); + intendedValues.add("official"); + intendedValues.add("permissive"); + + barriers.add("fence"); + + allowedHighways.addAll(Arrays.asList("living_street", "steps", "cycleway", "path", "footway", "platform", + "pedestrian", "track", "service", "residential", "unclassified", "road", "bridleway", + "motorway", "motorway_link", "trunk", "trunk_link", + "primary", "primary_link", "secondary", "secondary_link", "tertiary", "tertiary_link")); + } + + public WayAccess getAccess(ReaderWay way) { + String highwayValue = way.getTag("highway"); + if (highwayValue == null) { + WayAccess access = WayAccess.CAN_SKIP; + + if (way.hasTag("route", ferries)) { + // if bike is NOT explicitly tagged allow bike but only if foot is not specified either + String bikeTag = way.getTag("bicycle"); + if (bikeTag == null && !way.hasTag("foot") || intendedValues.contains(bikeTag)) + access = WayAccess.FERRY; + } + + // special case not for all acceptedRailways, only platform + if (way.hasTag("railway", "platform")) + access = WayAccess.WAY; + + if (way.hasTag("man_made", "pier")) + access = WayAccess.WAY; + + if (!access.canSkip()) { + if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) + return WayAccess.CAN_SKIP; + return access; + } + + return WayAccess.CAN_SKIP; + } + + if (!allowedHighways.contains(highwayValue)) + return WayAccess.CAN_SKIP; + + String sacScale = way.getTag("sac_scale"); + if (sacScale != null) { + if (!isSacScaleAllowed(sacScale)) + return WayAccess.CAN_SKIP; + } + + // use the way if it is tagged for bikes + if (way.hasTag("bicycle", "dismount") || way.hasTag("highway", "cycleway")) + return WayAccess.WAY; + + boolean permittedWayConditionallyRestricted = getConditionalTagInspector().isPermittedWayConditionallyRestricted(way); + boolean restrictedWayConditionallyPermitted = getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way); + String firstValue = way.getFirstPriorityTag(restrictions); + if (!firstValue.isEmpty()) { + String[] restrict = firstValue.split(";"); + for (String value : restrict) { + if (restrictedValues.contains(value) && !restrictedWayConditionallyPermitted) + return WayAccess.CAN_SKIP; + if (intendedValues.contains(value) && !permittedWayConditionallyRestricted) + return WayAccess.WAY; + } + } + + // accept only if explicitly tagged for bike usage + if ("motorway".equals(highwayValue) || "motorway_link".equals(highwayValue) || "bridleway".equals(highwayValue)) + return WayAccess.CAN_SKIP; + + if (way.hasTag("motorroad", "yes")) + return WayAccess.CAN_SKIP; + + if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) + return WayAccess.CAN_SKIP; + + if (permittedWayConditionallyRestricted) + return WayAccess.CAN_SKIP; + + return WayAccess.WAY; + } + + boolean isSacScaleAllowed(String sacScale) { + // other scales are nearly impossible by an ordinary bike, see http://wiki.openstreetmap.org/wiki/Key:sac_scale + return "hiking".equals(sacScale); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + WayAccess access = getAccess(way); + if (access.canSkip()) + return; + + if (access.isFerry()) { + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } else { + handleAccess(edgeId, edgeIntAccess, way); + } + + if (way.hasTag("gh:barrier_edge")) { + List> nodeTags = way.getTag("node_tags", Collections.emptyList()); + handleBarrierEdge(edgeId, edgeIntAccess, nodeTags.get(0)); + } + } + + protected void handleAccess(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + // handle oneways. The value -1 means it is a oneway but for reverse direction of stored geometry. + // The tagging oneway:bicycle=no or cycleway:right:oneway=no or cycleway:left:oneway=no lifts the generic oneway restriction of the way for bike + boolean isOneway = way.hasTag("oneway", ONEWAYS) && !way.hasTag("oneway", "-1") && !way.hasTag("bicycle:backward", INTENDED) + || way.hasTag("oneway", "-1") && !way.hasTag("bicycle:forward", INTENDED) + || way.hasTag("oneway:bicycle", ONEWAYS) + || way.hasTag("cycleway:left:oneway", ONEWAYS) + || way.hasTag("cycleway:right:oneway", ONEWAYS) + || way.hasTag("vehicle:backward", restrictedValues) && !way.hasTag("bicycle:forward", INTENDED) + || way.hasTag("vehicle:forward", restrictedValues) && !way.hasTag("bicycle:backward", INTENDED) + || way.hasTag("bicycle:forward", restrictedValues) + || way.hasTag("bicycle:backward", restrictedValues); + + if ((isOneway || roundaboutEnc.getBool(false, edgeId, edgeIntAccess)) + && !way.hasTag("oneway:bicycle", "no") + && !way.hasTag("cycleway", OPP_LANES) + && !way.hasTag("cycleway:left", OPP_LANES) + && !way.hasTag("cycleway:right", OPP_LANES) + && !way.hasTag("cycleway:left:oneway", "no") + && !way.hasTag("cycleway:right:oneway", "no")) { + boolean isBackward = way.hasTag("oneway", "-1") + || way.hasTag("oneway:bicycle", "-1") + || way.hasTag("cycleway:left:oneway", "-1") + || way.hasTag("cycleway:right:oneway", "-1") + || way.hasTag("vehicle:forward", restrictedValues) + || way.hasTag("bicycle:forward", restrictedValues); + accessEnc.setBool(isBackward, edgeId, edgeIntAccess, true); + + } else { + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAverageSpeedParser.java new file mode 100644 index 00000000000..cb93ddb3018 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonAverageSpeedParser.java @@ -0,0 +1,247 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.Smoothness; +import com.graphhopper.util.Helper; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public abstract class BikeCommonAverageSpeedParser extends AbstractAverageSpeedParser implements TagParser { + + public static double MAX_SPEED = 30; + protected static final int PUSHING_SECTION_SPEED = 4; + protected static final int MIN_SPEED = 2; + // Pushing section highways are parts where you need to get off your bike and push it (German: Schiebestrecke) + protected final HashSet pushingSectionsHighways = new HashSet<>(); + private final Map trackTypeSpeeds = new HashMap<>(); + private final Map surfaceSpeeds = new HashMap<>(); + private final Map smoothnessFactor = new HashMap<>(); + private final Map highwaySpeeds = new HashMap<>(); + private final EnumEncodedValue smoothnessEnc; + protected final Set intendedValues = new HashSet<>(5); + + protected BikeCommonAverageSpeedParser(DecimalEncodedValue speedEnc, EnumEncodedValue smoothnessEnc) { + super(speedEnc, speedEnc.getNextStorableValue(MAX_SPEED)); + this.smoothnessEnc = smoothnessEnc; + + // duplicate code as also in BikeCommonPriorityParser + addPushingSection("footway"); + addPushingSection("pedestrian"); + addPushingSection("steps"); + addPushingSection("platform"); + + setTrackTypeSpeed("grade1", 18); // paved + setTrackTypeSpeed("grade2", 12); // now unpaved ... + setTrackTypeSpeed("grade3", 8); + setTrackTypeSpeed("grade4", 6); + setTrackTypeSpeed("grade5", 4); // like sand/grass + + setSurfaceSpeed("paved", 18); + setSurfaceSpeed("asphalt", 18); + setSurfaceSpeed("cobblestone", 8); + setSurfaceSpeed("cobblestone:flattened", 10); + setSurfaceSpeed("sett", 10); + setSurfaceSpeed("concrete", 18); + setSurfaceSpeed("concrete:lanes", 16); + setSurfaceSpeed("concrete:plates", 16); + setSurfaceSpeed("paving_stones", 14); + setSurfaceSpeed("paving_stones:30", 14); + setSurfaceSpeed("unpaved", 12); + setSurfaceSpeed("compacted", 14); + setSurfaceSpeed("dirt", 10); + setSurfaceSpeed("earth", 12); + setSurfaceSpeed("fine_gravel", 18); + setSurfaceSpeed("grass", 8); + setSurfaceSpeed("grass_paver", 8); + setSurfaceSpeed("gravel", 12); + setSurfaceSpeed("ground", 12); + setSurfaceSpeed("ice", MIN_SPEED); + setSurfaceSpeed("metal", 10); + setSurfaceSpeed("mud", 10); + setSurfaceSpeed("pebblestone", 14); + setSurfaceSpeed("salt", PUSHING_SECTION_SPEED); + setSurfaceSpeed("sand", PUSHING_SECTION_SPEED); + setSurfaceSpeed("wood", PUSHING_SECTION_SPEED); + + setHighwaySpeed("living_street", PUSHING_SECTION_SPEED); + setHighwaySpeed("steps", MIN_SPEED); + + final int CYCLEWAY_SPEED = 18; // Make sure cycleway and path use same speed value, see #634 + setHighwaySpeed("cycleway", CYCLEWAY_SPEED); + setHighwaySpeed("path", 10); + setHighwaySpeed("footway", 6); + setHighwaySpeed("platform", PUSHING_SECTION_SPEED); + setHighwaySpeed("pedestrian", PUSHING_SECTION_SPEED); + setHighwaySpeed("track", 12); + setHighwaySpeed("service", 12); + setHighwaySpeed("residential", 18); + // no other highway applies: + setHighwaySpeed("unclassified", 16); + // unknown road: + setHighwaySpeed("road", 12); + + setHighwaySpeed("trunk", 18); + setHighwaySpeed("trunk_link", 18); + setHighwaySpeed("primary", 18); + setHighwaySpeed("primary_link", 18); + setHighwaySpeed("secondary", 18); + setHighwaySpeed("secondary_link", 18); + setHighwaySpeed("tertiary", 18); + setHighwaySpeed("tertiary_link", 18); + + // special case see tests and #191 + setHighwaySpeed("motorway", 18); + setHighwaySpeed("motorway_link", 18); + + setHighwaySpeed("bridleway", PUSHING_SECTION_SPEED); + + // note that this factor reduces the speed but only until MIN_SPEED + setSmoothnessSpeedFactor(Smoothness.MISSING, 1.0d); + setSmoothnessSpeedFactor(Smoothness.OTHER, 0.7d); + setSmoothnessSpeedFactor(Smoothness.EXCELLENT, 1.1d); + setSmoothnessSpeedFactor(Smoothness.GOOD, 1.0d); + setSmoothnessSpeedFactor(Smoothness.INTERMEDIATE, 0.9d); + setSmoothnessSpeedFactor(Smoothness.BAD, 0.7d); + setSmoothnessSpeedFactor(Smoothness.VERY_BAD, 0.4d); + setSmoothnessSpeedFactor(Smoothness.HORRIBLE, 0.3d); + setSmoothnessSpeedFactor(Smoothness.VERY_HORRIBLE, 0.1d); + setSmoothnessSpeedFactor(Smoothness.IMPASSABLE, 0); + + intendedValues.add("yes"); + intendedValues.add("designated"); + intendedValues.add("official"); + intendedValues.add("permissive"); + } + + /** + * @param way needed to retrieve tags + * @param speed speed guessed e.g. from the road type or other tags + * @return The assumed average speed. + */ + public double applyMaxSpeed(ReaderWay way, double speed, boolean bwd) { + double maxSpeed = getMaxSpeed(way, bwd); + // We strictly obey speed limits, see #600 + if (isValidSpeed(maxSpeed) && speed > maxSpeed) { + return maxSpeed; + } + if (isValidSpeed(speed) && speed > maxPossibleSpeed) + return maxPossibleSpeed; + return speed; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + String highwayValue = way.getTag("highway"); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) { + double ferrySpeed = ferrySpeedCalc.getSpeed(way); + avgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, ferrySpeed); + if (avgSpeedEnc.isStoreTwoDirections()) + avgSpeedEnc.setDecimal(true, edgeId, edgeIntAccess, ferrySpeed); + } + if (!way.hasTag("railway", "platform") && !way.hasTag("man_made", "pier")) + return; + } + + double speed = getSpeed(way); + Smoothness smoothness = smoothnessEnc.getEnum(false, edgeId, edgeIntAccess); + speed = Math.max(MIN_SPEED, smoothnessFactor.get(smoothness) * speed); + double speedFwd = applyMaxSpeed(way, speed, false); + avgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, speedFwd); + if (avgSpeedEnc.isStoreTwoDirections()) { + double bwdSpeed = applyMaxSpeed(way, speed, true); + avgSpeedEnc.setDecimal(true, edgeId, edgeIntAccess, bwdSpeed); + } + } + + int getSpeed(ReaderWay way) { + int speed = PUSHING_SECTION_SPEED; + String highwayTag = way.getTag("highway"); + Integer highwaySpeed = highwaySpeeds.get(highwayTag); + + if (way.hasTag("railway", "platform")) + highwaySpeed = PUSHING_SECTION_SPEED; + // Under certain conditions we need to increase the speed of pushing sections to the speed of a "highway=cycleway" + else if (way.hasTag("highway", pushingSectionsHighways) + && ((way.hasTag("foot", "yes") && way.hasTag("segregated", "yes")) + || (way.hasTag("bicycle", intendedValues)))) + highwaySpeed = getHighwaySpeed("cycleway"); + + String s = way.getTag("surface"); + Integer surfaceSpeed = 0; + if (!Helper.isEmpty(s)) { + surfaceSpeed = surfaceSpeeds.get(s); + if (surfaceSpeed != null) { + speed = surfaceSpeed; + // boost handling for good surfaces but avoid boosting if pushing section + if (highwaySpeed != null && surfaceSpeed > highwaySpeed && pushingSectionsHighways.contains(highwayTag)) + speed = highwaySpeed; + } + } else { + String tt = way.getTag("tracktype"); + if (!Helper.isEmpty(tt)) { + Integer tInt = trackTypeSpeeds.get(tt); + if (tInt != null) + speed = tInt; + } else if (highwaySpeed != null) { + if (!way.hasTag("service")) + speed = highwaySpeed; + else + speed = highwaySpeeds.get("living_street"); + } + } + + // Until now we assumed that the way is no pushing section + // Now we check that, but only in case that our speed computed so far is bigger compared to the PUSHING_SECTION_SPEED + if (speed > PUSHING_SECTION_SPEED + && (way.hasTag("highway", pushingSectionsHighways) || way.hasTag("bicycle", "dismount"))) { + if (!way.hasTag("bicycle", intendedValues)) { + // Here we set the speed for pushing sections and set speed for steps as even lower: + speed = way.hasTag("highway", "steps") ? MIN_SPEED : PUSHING_SECTION_SPEED; + } else if (way.hasTag("bicycle", "designated") || way.hasTag("bicycle", "official") || + way.hasTag("segregated", "yes") || way.hasTag("bicycle", "yes")) { + // Here we handle the cases where the OSM tagging results in something similar to "highway=cycleway" + if (way.hasTag("segregated", "yes")) + speed = highwaySpeeds.get("cycleway"); + else + speed = way.hasTag("bicycle", "yes") ? 10 : highwaySpeeds.get("cycleway"); + + // valid surface speed? + if (surfaceSpeed > 0) + speed = Math.min(speed, surfaceSpeed); + } + } + return speed; + } + + void setHighwaySpeed(String highway, int speed) { + highwaySpeeds.put(highway, speed); + } + + int getHighwaySpeed(String key) { + return highwaySpeeds.get(key); + } + + void addPushingSection(String highway) { + pushingSectionsHighways.add(highway); + } + + void setTrackTypeSpeed(String tracktype, int speed) { + trackTypeSpeeds.put(tracktype, speed); + } + + void setSurfaceSpeed(String surface, int speed) { + surfaceSpeeds.put(surface, speed); + } + + void setSmoothnessSpeedFactor(Smoothness smoothness, double speedfactor) { + smoothnessFactor.put(smoothness, speedfactor); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonPriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonPriorityParser.java new file mode 100644 index 00000000000..0ba95fdfac7 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikeCommonPriorityParser.java @@ -0,0 +1,242 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.RouteNetwork; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.storage.IntsRef; + +import java.util.*; + +import static com.graphhopper.routing.ev.RouteNetwork.*; +import static com.graphhopper.routing.util.PriorityCode.*; +import static com.graphhopper.routing.util.parsers.AbstractAccessParser.FERRIES; +import static com.graphhopper.routing.util.parsers.AbstractAccessParser.INTENDED; +import static com.graphhopper.routing.util.parsers.AbstractAverageSpeedParser.getMaxSpeed; +import static com.graphhopper.routing.util.parsers.AbstractAverageSpeedParser.isValidSpeed; + +public abstract class BikeCommonPriorityParser implements TagParser { + + // Pushing section highways are parts where you need to get off your bike and push it (German: Schiebestrecke) + protected final HashSet pushingSectionsHighways = new HashSet<>(); + protected final Set preferHighwayTags = new HashSet<>(); + protected final Set avoidHighwayTags = new HashSet<>(); + protected final Set unpavedSurfaceTags = new HashSet<>(); + protected final Set ferries = new HashSet<>(FERRIES); + protected final Set intendedValues = new HashSet<>(INTENDED); + + protected final DecimalEncodedValue avgSpeedEnc; + protected final DecimalEncodedValue priorityEnc; + // Car speed limit which switches the preference from UNCHANGED to AVOID_IF_POSSIBLE + int avoidSpeedLimit; + EnumEncodedValue bikeRouteEnc; + Map routeMap = new HashMap<>(); + + // This is the specific bicycle class + private String classBicycleKey; + + protected BikeCommonPriorityParser(DecimalEncodedValue priorityEnc, DecimalEncodedValue avgSpeedEnc, + EnumEncodedValue bikeRouteEnc) { + this.bikeRouteEnc = bikeRouteEnc; + this.priorityEnc = priorityEnc; + this.avgSpeedEnc = avgSpeedEnc; + + // duplicate code as also in BikeCommonAverageSpeedParser + addPushingSection("footway"); + addPushingSection("pedestrian"); + addPushingSection("steps"); + addPushingSection("platform"); + + unpavedSurfaceTags.add("unpaved"); + unpavedSurfaceTags.add("gravel"); + unpavedSurfaceTags.add("ground"); + unpavedSurfaceTags.add("dirt"); + unpavedSurfaceTags.add("grass"); + unpavedSurfaceTags.add("compacted"); + unpavedSurfaceTags.add("earth"); + unpavedSurfaceTags.add("fine_gravel"); + unpavedSurfaceTags.add("grass_paver"); + unpavedSurfaceTags.add("ice"); + unpavedSurfaceTags.add("mud"); + unpavedSurfaceTags.add("salt"); + unpavedSurfaceTags.add("sand"); + unpavedSurfaceTags.add("wood"); + + avoidHighwayTags.add("steps"); + avoidHighwayTags.add("motorway"); + avoidHighwayTags.add("motorway_link"); + avoidHighwayTags.add("bridleway"); + + routeMap.put(INTERNATIONAL, BEST.getValue()); + routeMap.put(NATIONAL, BEST.getValue()); + routeMap.put(REGIONAL, VERY_NICE.getValue()); + routeMap.put(LOCAL, PREFER.getValue()); + + avoidSpeedLimit = 71; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + String highwayValue = way.getTag("highway"); + Integer priorityFromRelation = routeMap.get(bikeRouteEnc.getEnum(false, edgeId, edgeIntAccess)); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) { + priorityFromRelation = SLIGHT_AVOID.getValue(); + } else { + return; + } + } + + double maxSpeed = Math.max(avgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), avgSpeedEnc.getDecimal(true, edgeId, edgeIntAccess)); + priorityEnc.setDecimal(false, edgeId, edgeIntAccess, PriorityCode.getValue(handlePriority(way, maxSpeed, priorityFromRelation))); + } + + /** + * In this method we prefer cycleways or roads with designated bike access and avoid big roads + * or roads with trams or pedestrian. + * + * @return new priority based on priorityFromRelation and on the tags in ReaderWay. + */ + int handlePriority(ReaderWay way, double wayTypeSpeed, Integer priorityFromRelation) { + TreeMap weightToPrioMap = new TreeMap<>(); + if (priorityFromRelation == null) + weightToPrioMap.put(0d, UNCHANGED.getValue()); + else + weightToPrioMap.put(110d, priorityFromRelation); + + collect(way, wayTypeSpeed, weightToPrioMap); + + // pick priority with biggest order value + return weightToPrioMap.lastEntry().getValue(); + } + + // Conversion of class value to priority. See http://wiki.openstreetmap.org/wiki/Class:bicycle + private PriorityCode convertClassValueToPriority(String tagvalue) { + int classvalue; + try { + classvalue = Integer.parseInt(tagvalue); + } catch (NumberFormatException e) { + return UNCHANGED; + } + + switch (classvalue) { + case 3: + return BEST; + case 2: + return VERY_NICE; + case 1: + return PREFER; + case 0: + return UNCHANGED; + case -1: + return SLIGHT_AVOID; + case -2: + return AVOID; + case -3: + return AVOID_MORE; + default: + return UNCHANGED; + } + } + + /** + * @param weightToPrioMap associate a weight with every priority. This sorted map allows + * subclasses to 'insert' more important priorities as well as overwrite determined priorities. + */ + void collect(ReaderWay way, double wayTypeSpeed, TreeMap weightToPrioMap) { + String highway = way.getTag("highway"); + if (way.hasTag("bicycle", "designated") || way.hasTag("bicycle", "official")) { + if ("path".equals(highway)) + weightToPrioMap.put(100d, VERY_NICE.getValue()); + else + weightToPrioMap.put(100d, PREFER.getValue()); + } + + if ("cycleway".equals(highway)) { + if (way.hasTag("foot", intendedValues) && !way.hasTag("segregated", "yes")) + weightToPrioMap.put(100d, PREFER.getValue()); + else + weightToPrioMap.put(100d, VERY_NICE.getValue()); + } + + double maxSpeed = Math.max(getMaxSpeed(way, false), getMaxSpeed(way, true)); + if (preferHighwayTags.contains(highway) || (isValidSpeed(maxSpeed) && maxSpeed <= 30)) { + if (!isValidSpeed(maxSpeed) || maxSpeed < avoidSpeedLimit) { + weightToPrioMap.put(40d, PREFER.getValue()); + if (way.hasTag("tunnel", intendedValues)) + weightToPrioMap.put(40d, UNCHANGED.getValue()); + } + } else if (avoidHighwayTags.contains(highway) + || isValidSpeed(maxSpeed) && maxSpeed >= avoidSpeedLimit && !"track".equals(highway)) { + weightToPrioMap.put(50d, AVOID.getValue()); + if (way.hasTag("tunnel", intendedValues) || way.hasTag("hazmat", intendedValues)) + weightToPrioMap.put(50d, BAD.getValue()); + } + + String cycleway = way.getFirstPriorityTag(Arrays.asList("cycleway", "cycleway:left", "cycleway:right")); + if (Arrays.asList("lane", "shared_lane", "share_busway", "shoulder").contains(cycleway)) { + weightToPrioMap.put(100d, UNCHANGED.getValue()); + } else if ("track".equals(cycleway)) { + weightToPrioMap.put(100d, PREFER.getValue()); + } + + if (way.hasTag("bicycle", "use_sidepath")) { + weightToPrioMap.put(100d, REACH_DESTINATION.getValue()); + } + + if (pushingSectionsHighways.contains(highway) || "parking_aisle".equals(way.getTag("service"))) { + PriorityCode pushingSectionPrio = SLIGHT_AVOID; + if (way.hasTag("bicycle", "yes") || way.hasTag("bicycle", "permissive")) + pushingSectionPrio = PREFER; + if (way.hasTag("bicycle", "designated") || way.hasTag("bicycle", "official")) + pushingSectionPrio = VERY_NICE; + if (way.hasTag("foot", "yes")) { + pushingSectionPrio = PriorityCode.values()[pushingSectionPrio.ordinal() - 1]; + if (way.hasTag("segregated", "yes")) + pushingSectionPrio = PriorityCode.values()[pushingSectionPrio.ordinal() + 1]; + } + weightToPrioMap.put(100d, pushingSectionPrio.getValue()); + } + + if (way.hasTag("railway", "tram")) + weightToPrioMap.put(50d, AVOID_MORE.getValue()); + + if (way.hasTag("lcn", "yes")) + weightToPrioMap.put(100d, PREFER.getValue()); + + String classBicycleValue = way.getTag(classBicycleKey); + if (classBicycleValue != null) { + // We assume that humans are better in classifying preferences compared to our algorithm above -> weight = 100 + weightToPrioMap.put(100d, convertClassValueToPriority(classBicycleValue).getValue()); + } else { + String classBicycle = way.getTag("class:bicycle"); + if (classBicycle != null) + weightToPrioMap.put(100d, convertClassValueToPriority(classBicycle).getValue()); + } + + // Increase the priority for scenic routes or in case that maxspeed limits our average speed as compensation. See #630 + if (way.hasTag("scenic", "yes") || maxSpeed > 0 && maxSpeed <= wayTypeSpeed) { + int lastEntryValue = weightToPrioMap.lastEntry().getValue(); + if (lastEntryValue < BEST.getValue()) { + int lastEntryIndex = Arrays.stream(PriorityCode.values()).filter(pc -> pc.getValue() == lastEntryValue).findFirst().orElse(UNCHANGED).ordinal(); + // Increase the PriorityCode by one step + weightToPrioMap.put(110d, PriorityCode.values()[lastEntryIndex + 1].getValue()); + } + } + } + + // TODO duplicated in average speed + void addPushingSection(String highway) { + pushingSectionsHighways.add(highway); + } + + void setSpecificClassBicycle(String subkey) { + classBicycleKey = "class:bicycle:" + subkey; + } + + public final DecimalEncodedValue getPriorityEnc() { + return priorityEnc; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/BikePriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/BikePriorityParser.java new file mode 100644 index 00000000000..1d5d147f9b2 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/BikePriorityParser.java @@ -0,0 +1,36 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; + +public class BikePriorityParser extends BikeCommonPriorityParser { + + public BikePriorityParser(EncodedValueLookup lookup, PMap properties) { + this( + lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "bike"))), + lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "bike"))), + lookup.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class) + ); + } + + public BikePriorityParser(DecimalEncodedValue priorityEnc, DecimalEncodedValue speedEnc, EnumEncodedValue bikeRouteEnc) { + super(priorityEnc, speedEnc, bikeRouteEnc); + + addPushingSection("path"); + + avoidHighwayTags.add("trunk"); + avoidHighwayTags.add("trunk_link"); + avoidHighwayTags.add("primary"); + avoidHighwayTags.add("primary_link"); + avoidHighwayTags.add("secondary"); + avoidHighwayTags.add("secondary_link"); + + preferHighwayTags.add("service"); + preferHighwayTags.add("tertiary"); + preferHighwayTags.add("tertiary_link"); + preferHighwayTags.add("residential"); + preferHighwayTags.add("unclassified"); + + setSpecificClassBicycle("touring"); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/CarAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/CarAccessParser.java new file mode 100644 index 00000000000..6acf137d0e9 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/CarAccessParser.java @@ -0,0 +1,183 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.routing.util.WayAccess; +import com.graphhopper.util.PMap; + +import java.util.*; + +public class CarAccessParser extends AbstractAccessParser implements TagParser { + + protected final Set trackTypeValues = new HashSet<>(); + protected final Set highwayValues = new HashSet<>(); + protected final BooleanEncodedValue roundaboutEnc; + + public CarAccessParser(EncodedValueLookup lookup, PMap properties) { + this( + lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "car"))), + lookup.getBooleanEncodedValue(Roundabout.KEY), + properties, + TransportationMode.CAR + ); + } + + public CarAccessParser(BooleanEncodedValue accessEnc, + BooleanEncodedValue roundaboutEnc, PMap properties, + TransportationMode transportationMode) { + super(accessEnc, transportationMode); + this.roundaboutEnc = roundaboutEnc; + restrictedValues.add("agricultural"); + restrictedValues.add("forestry"); + restrictedValues.add("delivery"); + + blockPrivate(properties.getBool("block_private", true)); + blockFords(properties.getBool("block_fords", false)); + + intendedValues.add("yes"); + intendedValues.add("designated"); + intendedValues.add("permissive"); + + barriers.add("kissing_gate"); + barriers.add("fence"); + barriers.add("bollard"); + barriers.add("stile"); + barriers.add("turnstile"); + barriers.add("cycle_barrier"); + barriers.add("motorcycle_barrier"); + barriers.add("block"); + barriers.add("bus_trap"); + barriers.add("sump_buster"); + barriers.add("jersey_barrier"); + + highwayValues.addAll(Arrays.asList("motorway", "motorway_link", "trunk", "trunk_link", + "primary", "primary_link", "secondary", "secondary_link", "tertiary", "tertiary_link", + "unclassified", "residential", "living_street", "service", "road", "track")); + + trackTypeValues.addAll(Arrays.asList("grade1", "grade2", "grade3", null)); + } + + public WayAccess getAccess(ReaderWay way) { + // TODO: Ferries have conditionals, like opening hours or are closed during some time in the year + String highwayValue = way.getTag("highway"); + String firstValue = way.getFirstPriorityTag(restrictions); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) { + if (restrictedValues.contains(firstValue)) + return WayAccess.CAN_SKIP; + if (intendedValues.contains(firstValue) || + // implied default is allowed only if foot and bicycle is not specified: + firstValue.isEmpty() && !way.hasTag("foot") && !way.hasTag("bicycle")) + return WayAccess.FERRY; + } + return WayAccess.CAN_SKIP; + } + + if ("service".equals(highwayValue) && "emergency_access".equals(way.getTag("service"))) { + return WayAccess.CAN_SKIP; + } + + if ("track".equals(highwayValue) && !trackTypeValues.contains(way.getTag("tracktype"))) + return WayAccess.CAN_SKIP; + + if (!highwayValues.contains(highwayValue)) + return WayAccess.CAN_SKIP; + + if (way.hasTag("impassable", "yes") || way.hasTag("status", "impassable")) + return WayAccess.CAN_SKIP; + + // multiple restrictions needs special handling, see also motorcycle + boolean permittedWayConditionallyRestricted = getConditionalTagInspector().isPermittedWayConditionallyRestricted(way); + boolean restrictedWayConditionallyPermitted = getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way); + if (!firstValue.isEmpty()) { + String[] restrict = firstValue.split(";"); + for (String value : restrict) { + if (restrictedValues.contains(value) && !restrictedWayConditionallyPermitted) + return WayAccess.CAN_SKIP; + if (intendedValues.contains(value) && !permittedWayConditionallyRestricted) + return WayAccess.WAY; + } + } + + if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) + return WayAccess.CAN_SKIP; + + if (permittedWayConditionallyRestricted) + return WayAccess.CAN_SKIP; + + return WayAccess.WAY; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + WayAccess access = getAccess(way); + if (access.canSkip()) + return; + + if (!access.isFerry()) { + boolean isRoundabout = roundaboutEnc.getBool(false, edgeId, edgeIntAccess); + if (isOneway(way) || isRoundabout) { + if (isForwardOneway(way)) + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + if (isBackwardOneway(way)) + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } else { + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } + + } else { + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } + + if (way.hasTag("gh:barrier_edge")) { + List> nodeTags = way.getTag("node_tags", Collections.emptyList()); + handleBarrierEdge(edgeId, edgeIntAccess, nodeTags.get(0)); + } + } + + /** + * make sure that isOneway is called before + */ + protected boolean isBackwardOneway(ReaderWay way) { + return way.hasTag("oneway", "-1") + || way.hasTag("vehicle:forward", restrictedValues) + || way.hasTag("motor_vehicle:forward", restrictedValues); + } + + /** + * make sure that isOneway is called before + */ + protected boolean isForwardOneway(ReaderWay way) { + return !way.hasTag("oneway", "-1") + && !way.hasTag("vehicle:forward", restrictedValues) + && !way.hasTag("motor_vehicle:forward", restrictedValues); + } + + protected boolean isOneway(ReaderWay way) { + return way.hasTag("oneway", oneways) + || way.hasTag("vehicle:backward", restrictedValues) + || way.hasTag("vehicle:forward", restrictedValues) + || way.hasTag("motor_vehicle:backward", restrictedValues) + || way.hasTag("motor_vehicle:forward", restrictedValues); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/CarAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/CarAverageSpeedParser.java new file mode 100644 index 00000000000..1527cb4ad93 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/CarAverageSpeedParser.java @@ -0,0 +1,164 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.util.Helper; +import com.graphhopper.util.PMap; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class CarAverageSpeedParser extends AbstractAverageSpeedParser implements TagParser { + + public static final double CAR_MAX_SPEED = 140; + protected final Map trackTypeSpeedMap = new HashMap<>(); + protected final Set badSurfaceSpeedMap = new HashSet<>(); + // This value determines the maximal possible on roads with bad surfaces + private final int badSurfaceSpeed; + + /** + * A map which associates string to speed. Get some impression: + * http://www.itoworld.com/map/124#fullscreen + * http://wiki.openstreetmap.org/wiki/OSM_tags_for_routing/Maxspeed + */ + protected final Map defaultSpeedMap = new HashMap<>(); + + public CarAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { + this( + lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "car"))), + lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "car"))).getNextStorableValue(CAR_MAX_SPEED) + ); + } + + public CarAverageSpeedParser(DecimalEncodedValue speedEnc, double maxPossibleSpeed) { + super(speedEnc, maxPossibleSpeed); + + badSurfaceSpeedMap.add("cobblestone"); + badSurfaceSpeedMap.add("grass_paver"); + badSurfaceSpeedMap.add("gravel"); + badSurfaceSpeedMap.add("sand"); + badSurfaceSpeedMap.add("paving_stones"); + badSurfaceSpeedMap.add("dirt"); + badSurfaceSpeedMap.add("ground"); + badSurfaceSpeedMap.add("grass"); + badSurfaceSpeedMap.add("unpaved"); + badSurfaceSpeedMap.add("compacted"); + + // autobahn + defaultSpeedMap.put("motorway", 100); + defaultSpeedMap.put("motorway_link", 70); + // bundesstraße + defaultSpeedMap.put("trunk", 70); + defaultSpeedMap.put("trunk_link", 65); + // linking bigger town + defaultSpeedMap.put("primary", 65); + defaultSpeedMap.put("primary_link", 60); + // linking towns + villages + defaultSpeedMap.put("secondary", 60); + defaultSpeedMap.put("secondary_link", 50); + // streets without middle line separation + defaultSpeedMap.put("tertiary", 50); + defaultSpeedMap.put("tertiary_link", 40); + defaultSpeedMap.put("unclassified", 30); + defaultSpeedMap.put("residential", 30); + // spielstraße + defaultSpeedMap.put("living_street", 5); + defaultSpeedMap.put("service", 20); + // unknown road + defaultSpeedMap.put("road", 20); + // forestry stuff + defaultSpeedMap.put("track", 15); + + trackTypeSpeedMap.put("grade1", 20); // paved + trackTypeSpeedMap.put("grade2", 15); // now unpaved - gravel mixed with ... + trackTypeSpeedMap.put("grade3", 10); // ... hard and soft materials + trackTypeSpeedMap.put(null, defaultSpeedMap.get("track")); + + // limit speed on bad surfaces to 30 km/h + badSurfaceSpeed = 30; + } + + protected double getSpeed(ReaderWay way) { + String highwayValue = way.getTag("highway"); + Integer speed = defaultSpeedMap.get(highwayValue); + + // even inaccessible edges get a speed assigned + if (speed == null) speed = 10; + + if (highwayValue.equals("track")) { + String tt = way.getTag("tracktype"); + if (!Helper.isEmpty(tt)) { + Integer tInt = trackTypeSpeedMap.get(tt); + if (tInt != null) + speed = tInt; + } + } + + return speed; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + String highwayValue = way.getTag("highway"); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) { + double ferrySpeed = ferrySpeedCalc.getSpeed(way); + setSpeed(false, edgeId, edgeIntAccess, ferrySpeed); + if (avgSpeedEnc.isStoreTwoDirections()) + setSpeed(true, edgeId, edgeIntAccess, ferrySpeed); + } + return; + } + + // get assumed speed from highway type + double speed = getSpeed(way); + speed = applyBadSurfaceSpeed(way, speed); + + setSpeed(false, edgeId, edgeIntAccess, applyMaxSpeed(way, speed, false)); + setSpeed(true, edgeId, edgeIntAccess, applyMaxSpeed(way, speed, true)); + } + + /** + * @param way needed to retrieve tags + * @param speed speed guessed e.g. from the road type or other tags + * @return The assumed speed. + */ + protected double applyMaxSpeed(ReaderWay way, double speed, boolean bwd) { + double maxSpeed = getMaxSpeed(way, bwd); + return isValidSpeed(maxSpeed) ? maxSpeed * 0.9 : speed; + } + + /** + * @param way needed to retrieve tags + * @param speed speed guessed e.g. from the road type or other tags + * @return The assumed speed + */ + protected double applyBadSurfaceSpeed(ReaderWay way, double speed) { + // limit speed if bad surface + if (badSurfaceSpeed > 0 && isValidSpeed(speed) && speed > badSurfaceSpeed && way.hasTag("surface", badSurfaceSpeedMap)) + speed = badSurfaceSpeed; + return speed; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/CountryParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/CountryParser.java index a3446b63a12..e9aa6b955cd 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/CountryParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/CountryParser.java @@ -20,6 +20,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.Country; import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.storage.IntsRef; public class CountryParser implements TagParser { @@ -30,9 +31,8 @@ public CountryParser(EnumEncodedValue countryEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { Country country = way.getTag("country", Country.MISSING); - countryEnc.setEnum(false, edgeFlags, country); - return edgeFlags; + countryEnc.setEnum(false, edgeId, edgeIntAccess, country); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java b/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java index eb5b1be0fec..f7c19fa1cd0 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/DefaultTagParserFactory.java @@ -19,16 +19,12 @@ import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.TransportationMode; - -import static com.graphhopper.util.Helper.toLowerCase; +import com.graphhopper.util.PMap; public class DefaultTagParserFactory implements TagParserFactory { - @Override - public TagParser create(EncodedValueLookup lookup, String name) { - name = name.trim(); - if (!name.equals(toLowerCase(name))) - throw new IllegalArgumentException("Use lower case for TagParsers: " + name); + @Override + public TagParser create(EncodedValueLookup lookup, String name, PMap properties) { if (Roundabout.KEY.equals(name)) return new OSMRoundaboutParser(lookup.getBooleanEncodedValue(Roundabout.KEY)); else if (name.equals(RoadClass.KEY)) @@ -39,10 +35,6 @@ else if (name.equals(RoadEnvironment.KEY)) return new OSMRoadEnvironmentParser(lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class)); else if (name.equals(RoadAccess.KEY)) return new OSMRoadAccessParser(lookup.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR)); - else if (name.equals("car_access")) - return new OSMAccessParser(lookup.getBooleanEncodedValue("car_access"), lookup.getBooleanEncodedValue(Roundabout.KEY), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR), TransportationMode.CAR); - else if (name.equals("bike_access")) - return new OSMAccessParser(lookup.getBooleanEncodedValue("bike_access"), lookup.getBooleanEncodedValue(Roundabout.KEY), OSMRoadAccessParser.toOSMRestrictions(TransportationMode.BIKE), TransportationMode.BIKE); else if (name.equals(MaxSpeed.KEY)) return new OSMMaxSpeedParser(lookup.getDecimalEncodedValue(MaxSpeed.KEY)); else if (name.equals(MaxWeight.KEY)) @@ -63,6 +55,8 @@ else if (name.equals(Toll.KEY)) return new OSMTollParser(lookup.getEnumEncodedValue(Toll.KEY, Toll.class)); else if (name.equals(TrackType.KEY)) return new OSMTrackTypeParser(lookup.getEnumEncodedValue(TrackType.KEY, TrackType.class)); + else if (name.equals(Hgv.KEY)) + return new OSMHgvParser(lookup.getEnumEncodedValue(Hgv.KEY, Hgv.class)); else if (name.equals(Hazmat.KEY)) return new OSMHazmatParser(lookup.getEnumEncodedValue(Hazmat.KEY, Hazmat.class)); else if (name.equals(HazmatTunnel.KEY)) @@ -71,15 +65,20 @@ else if (name.equals(HazmatWater.KEY)) return new OSMHazmatWaterParser(lookup.getEnumEncodedValue(HazmatWater.KEY, HazmatWater.class)); else if (name.equals(Lanes.KEY)) return new OSMLanesParser(lookup.getIntEncodedValue(Lanes.KEY)); + else if (name.equals(OSMWayID.KEY)) + return new OSMWayIDParser(lookup.getIntEncodedValue(OSMWayID.KEY)); else if (name.equals(MtbRating.KEY)) return new OSMMtbRatingParser(lookup.getIntEncodedValue(MtbRating.KEY)); else if (name.equals(HikeRating.KEY)) return new OSMHikeRatingParser(lookup.getIntEncodedValue(HikeRating.KEY)); else if (name.equals(HorseRating.KEY)) return new OSMHorseRatingParser(lookup.getIntEncodedValue(HorseRating.KEY)); + else if (name.equals(Footway.KEY)) + return new OSMFootwayParser(lookup.getEnumEncodedValue(Footway.KEY, Footway.class)); else if (name.equals(Country.KEY)) return new CountryParser(lookup.getEnumEncodedValue(Country.KEY, Country.class)); - - throw new IllegalArgumentException("DefaultTagParserFactory cannot find: " + name); + else if (name.equals(Crossing.KEY)) + return new OSMCrossingParser(lookup.getEnumEncodedValue(Crossing.KEY, Crossing.class)); + return null; } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/FootAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/FootAccessParser.java new file mode 100644 index 00000000000..e2a4d4553d5 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/FootAccessParser.java @@ -0,0 +1,184 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.routing.util.WayAccess; +import com.graphhopper.util.PMap; + +import java.util.*; + +import static com.graphhopper.routing.ev.RouteNetwork.*; +import static com.graphhopper.routing.util.PriorityCode.UNCHANGED; + +public class FootAccessParser extends AbstractAccessParser implements TagParser { + + final Set safeHighwayTags = new HashSet<>(); + final Set allowedHighwayTags = new HashSet<>(); + final Set avoidHighwayTags = new HashSet<>(); + final Set allowedSacScale = new HashSet<>(); + protected HashSet sidewalkValues = new HashSet<>(5); + protected Map routeMap = new HashMap<>(); + + public FootAccessParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "foot")))); + blockPrivate(properties.getBool("block_private", true)); + blockFords(properties.getBool("block_fords", false)); + } + + protected FootAccessParser(BooleanEncodedValue accessEnc) { + super(accessEnc, TransportationMode.FOOT); + + intendedValues.add("yes"); + intendedValues.add("designated"); + intendedValues.add("official"); + intendedValues.add("permissive"); + + sidewalkValues.add("yes"); + sidewalkValues.add("both"); + sidewalkValues.add("left"); + sidewalkValues.add("right"); + + barriers.add("fence"); + + safeHighwayTags.add("footway"); + safeHighwayTags.add("path"); + safeHighwayTags.add("steps"); + safeHighwayTags.add("pedestrian"); + safeHighwayTags.add("living_street"); + safeHighwayTags.add("track"); + safeHighwayTags.add("residential"); + safeHighwayTags.add("service"); + safeHighwayTags.add("platform"); + + avoidHighwayTags.add("trunk"); + avoidHighwayTags.add("trunk_link"); + avoidHighwayTags.add("primary"); + avoidHighwayTags.add("primary_link"); + avoidHighwayTags.add("secondary"); + avoidHighwayTags.add("secondary_link"); + avoidHighwayTags.add("tertiary"); + avoidHighwayTags.add("tertiary_link"); + + allowedHighwayTags.addAll(safeHighwayTags); + allowedHighwayTags.addAll(avoidHighwayTags); + allowedHighwayTags.add("cycleway"); + allowedHighwayTags.add("unclassified"); + allowedHighwayTags.add("road"); + // disallowed in some countries + //allowedHighwayTags.add("bridleway"); + + routeMap.put(INTERNATIONAL, UNCHANGED.getValue()); + routeMap.put(NATIONAL, UNCHANGED.getValue()); + routeMap.put(REGIONAL, UNCHANGED.getValue()); + routeMap.put(LOCAL, UNCHANGED.getValue()); + + allowedSacScale.add("hiking"); + allowedSacScale.add("mountain_hiking"); + allowedSacScale.add("demanding_mountain_hiking"); + } + + /** + * Some ways are okay but not separate for pedestrians. + */ + public WayAccess getAccess(ReaderWay way) { + String highwayValue = way.getTag("highway"); + if (highwayValue == null) { + WayAccess acceptPotentially = WayAccess.CAN_SKIP; + + if (way.hasTag("route", ferries)) { + String footTag = way.getTag("foot"); + if (footTag == null || intendedValues.contains(footTag)) + acceptPotentially = WayAccess.FERRY; + } + + // special case not for all acceptedRailways, only platform + if (way.hasTag("railway", "platform")) + acceptPotentially = WayAccess.WAY; + + if (way.hasTag("man_made", "pier")) + acceptPotentially = WayAccess.WAY; + + if (!acceptPotentially.canSkip()) { + if (way.hasTag(restrictions, restrictedValues) && !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way)) + return WayAccess.CAN_SKIP; + return acceptPotentially; + } + + return WayAccess.CAN_SKIP; + } + + // other scales are too dangerous, see http://wiki.openstreetmap.org/wiki/Key:sac_scale + if (way.getTag("sac_scale") != null && !way.hasTag("sac_scale", allowedSacScale)) + return WayAccess.CAN_SKIP; + + boolean permittedWayConditionallyRestricted = getConditionalTagInspector().isPermittedWayConditionallyRestricted(way); + boolean restrictedWayConditionallyPermitted = getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way); + String firstValue = way.getFirstPriorityTag(restrictions); + if (!firstValue.isEmpty()) { + String[] restrict = firstValue.split(";"); + for (String value : restrict) { + if (restrictedValues.contains(value) && !restrictedWayConditionallyPermitted) + return WayAccess.CAN_SKIP; + if (intendedValues.contains(value) && !permittedWayConditionallyRestricted) + return WayAccess.WAY; + } + } + + if (way.hasTag("sidewalk", sidewalkValues)) + return WayAccess.WAY; + + if (!allowedHighwayTags.contains(highwayValue)) + return WayAccess.CAN_SKIP; + + if (way.hasTag("motorroad", "yes")) + return WayAccess.CAN_SKIP; + + if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) + return WayAccess.CAN_SKIP; + + if (permittedWayConditionallyRestricted) + return WayAccess.CAN_SKIP; + + return WayAccess.WAY; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + WayAccess access = getAccess(way); + if (access.canSkip()) + return; + + if (way.hasTag("oneway:foot", oneways) || way.hasTag("foot:backward") || way.hasTag("foot:forward") + || way.hasTag("oneway", oneways) && way.hasTag("highway", "steps") // outdated mapping style + ) { + boolean reverse = way.hasTag("oneway:foot", "-1") || way.hasTag("foot:backward", "yes") || way.hasTag("foot:forward", "no"); + accessEnc.setBool(reverse, edgeId, edgeIntAccess, true); + } else { + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } + + if (way.hasTag("gh:barrier_edge")) { + List> nodeTags = way.getTag("node_tags", Collections.emptyList()); + handleBarrierEdge(edgeId, edgeIntAccess, nodeTags.get(0)); + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/FootAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/FootAverageSpeedParser.java new file mode 100644 index 00000000000..e235dbadcc0 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/FootAverageSpeedParser.java @@ -0,0 +1,102 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; + +import java.util.*; + +import static com.graphhopper.routing.ev.RouteNetwork.*; +import static com.graphhopper.routing.util.PriorityCode.*; + +public class FootAverageSpeedParser extends AbstractAverageSpeedParser implements TagParser { + static final int SLOW_SPEED = 2; + static final int MEAN_SPEED = 5; + // larger value required - ferries are faster than pedestrians + static final int FERRY_SPEED = 15; + final Set safeHighwayTags = new HashSet<>(); + final Set allowedHighwayTags = new HashSet<>(); + final Set avoidHighwayTags = new HashSet<>(); + protected HashSet sidewalkValues = new HashSet<>(5); + protected HashSet sidewalksNoValues = new HashSet<>(5); + protected Map routeMap = new HashMap<>(); + + public FootAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "foot")))); + } + + protected FootAverageSpeedParser(DecimalEncodedValue speedEnc) { + super(speedEnc, speedEnc.getNextStorableValue(FERRY_SPEED)); + + sidewalksNoValues.add("no"); + sidewalksNoValues.add("none"); + // see #712 + sidewalksNoValues.add("separate"); + + sidewalkValues.add("yes"); + sidewalkValues.add("both"); + sidewalkValues.add("left"); + sidewalkValues.add("right"); + + safeHighwayTags.add("footway"); + safeHighwayTags.add("path"); + safeHighwayTags.add("steps"); + safeHighwayTags.add("pedestrian"); + safeHighwayTags.add("living_street"); + safeHighwayTags.add("track"); + safeHighwayTags.add("residential"); + safeHighwayTags.add("service"); + safeHighwayTags.add("platform"); + + avoidHighwayTags.add("trunk"); + avoidHighwayTags.add("trunk_link"); + avoidHighwayTags.add("primary"); + avoidHighwayTags.add("primary_link"); + avoidHighwayTags.add("secondary"); + avoidHighwayTags.add("secondary_link"); + avoidHighwayTags.add("tertiary"); + avoidHighwayTags.add("tertiary_link"); + + allowedHighwayTags.addAll(safeHighwayTags); + allowedHighwayTags.addAll(avoidHighwayTags); + allowedHighwayTags.add("cycleway"); + allowedHighwayTags.add("unclassified"); + allowedHighwayTags.add("road"); + // disallowed in some countries + //allowedHighwayTags.add("bridleway"); + + routeMap.put(INTERNATIONAL, UNCHANGED.getValue()); + routeMap.put(NATIONAL, UNCHANGED.getValue()); + routeMap.put(REGIONAL, UNCHANGED.getValue()); + routeMap.put(LOCAL, UNCHANGED.getValue()); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + String highwayValue = way.getTag("highway"); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) { + double ferrySpeed = ferrySpeedCalc.getSpeed(way); + setSpeed(edgeId, edgeIntAccess, true, true, ferrySpeed); + } + if (!way.hasTag("railway", "platform") && !way.hasTag("man_made", "pier")) + return; + } + + String sacScale = way.getTag("sac_scale"); + if (sacScale != null) { + setSpeed(edgeId, edgeIntAccess, true, true, "hiking".equals(sacScale) ? MEAN_SPEED : SLOW_SPEED); + } else { + setSpeed(edgeId, edgeIntAccess, true, true, way.hasTag("highway", "steps") ? MEAN_SPEED - 2 : MEAN_SPEED); + } + } + + void setSpeed(int edgeId, EdgeIntAccess edgeIntAccess, boolean fwd, boolean bwd, double speed) { + if (speed > getMaxSpeed()) + speed = getMaxSpeed(); + if (fwd) + avgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, speed); + if (bwd && avgSpeedEnc.isStoreTwoDirections()) + avgSpeedEnc.setDecimal(true, edgeId, edgeIntAccess, speed); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/FootPriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/FootPriorityParser.java new file mode 100644 index 00000000000..e6f8764d6d8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/FootPriorityParser.java @@ -0,0 +1,130 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.PMap; + +import java.util.*; + +import static com.graphhopper.routing.ev.RouteNetwork.*; +import static com.graphhopper.routing.util.PriorityCode.*; +import static com.graphhopper.routing.util.parsers.AbstractAccessParser.FERRIES; +import static com.graphhopper.routing.util.parsers.AbstractAccessParser.INTENDED; +import static com.graphhopper.routing.util.parsers.AbstractAverageSpeedParser.getMaxSpeed; +import static com.graphhopper.routing.util.parsers.AbstractAverageSpeedParser.isValidSpeed; + +public class FootPriorityParser implements TagParser { + final Set ferries = new HashSet<>(FERRIES); + final Set intendedValues = new HashSet<>(INTENDED); + final Set safeHighwayTags = new HashSet<>(); + final Set avoidHighwayTags = new HashSet<>(); + protected HashSet sidewalkValues = new HashSet<>(5); + protected HashSet sidewalksNoValues = new HashSet<>(5); + protected final DecimalEncodedValue priorityWayEncoder; + protected EnumEncodedValue footRouteEnc; + protected Map routeMap = new HashMap<>(); + + public FootPriorityParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "foot"))), + lookup.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class) + ); + } + + protected FootPriorityParser(DecimalEncodedValue priorityEnc, EnumEncodedValue footRouteEnc) { + this.footRouteEnc = footRouteEnc; + priorityWayEncoder = priorityEnc; + + sidewalksNoValues.add("no"); + sidewalksNoValues.add("none"); + // see #712 + sidewalksNoValues.add("separate"); + + sidewalkValues.add("yes"); + sidewalkValues.add("both"); + sidewalkValues.add("left"); + sidewalkValues.add("right"); + + safeHighwayTags.add("footway"); + safeHighwayTags.add("path"); + safeHighwayTags.add("steps"); + safeHighwayTags.add("pedestrian"); + safeHighwayTags.add("living_street"); + safeHighwayTags.add("track"); + safeHighwayTags.add("residential"); + safeHighwayTags.add("service"); + safeHighwayTags.add("platform"); + + avoidHighwayTags.add("trunk"); + avoidHighwayTags.add("trunk_link"); + avoidHighwayTags.add("primary"); + avoidHighwayTags.add("primary_link"); + avoidHighwayTags.add("secondary"); + avoidHighwayTags.add("secondary_link"); + avoidHighwayTags.add("tertiary"); + avoidHighwayTags.add("tertiary_link"); + + routeMap.put(INTERNATIONAL, UNCHANGED.getValue()); + routeMap.put(NATIONAL, UNCHANGED.getValue()); + routeMap.put(REGIONAL, UNCHANGED.getValue()); + routeMap.put(LOCAL, UNCHANGED.getValue()); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + String highwayValue = way.getTag("highway"); + Integer priorityFromRelation = routeMap.get(footRouteEnc.getEnum(false, edgeId, edgeIntAccess)); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) + priorityWayEncoder.setDecimal(false, edgeId, edgeIntAccess, PriorityCode.getValue(handlePriority(way, priorityFromRelation))); + } else { + priorityWayEncoder.setDecimal(false, edgeId, edgeIntAccess, PriorityCode.getValue(handlePriority(way, priorityFromRelation))); + } + } + + public int handlePriority(ReaderWay way, Integer priorityFromRelation) { + TreeMap weightToPrioMap = new TreeMap<>(); + if (priorityFromRelation == null) + weightToPrioMap.put(0d, UNCHANGED.getValue()); + else + weightToPrioMap.put(110d, priorityFromRelation); + + collect(way, weightToPrioMap); + + // pick priority with biggest order value + return weightToPrioMap.lastEntry().getValue(); + } + + /** + * @param weightToPrioMap associate a weight with every priority. This sorted map allows + * subclasses to 'insert' more important priorities as well as overwrite determined priorities. + */ + void collect(ReaderWay way, TreeMap weightToPrioMap) { + String highway = way.getTag("highway"); + if (way.hasTag("foot", "designated")) + weightToPrioMap.put(100d, PREFER.getValue()); + + double maxSpeed = Math.max(getMaxSpeed(way, false), getMaxSpeed(way, true)); + if (safeHighwayTags.contains(highway) || (isValidSpeed(maxSpeed) && maxSpeed <= 20)) { + weightToPrioMap.put(40d, PREFER.getValue()); + if (way.hasTag("tunnel", intendedValues)) { + if (way.hasTag("sidewalk", sidewalksNoValues)) + weightToPrioMap.put(40d, AVOID.getValue()); + else + weightToPrioMap.put(40d, UNCHANGED.getValue()); + } + } else if ((isValidSpeed(maxSpeed) && maxSpeed > 50) || avoidHighwayTags.contains(highway)) { + if (way.hasTag("sidewalk", sidewalksNoValues)) + weightToPrioMap.put(40d, VERY_BAD.getValue()); + else if (!way.hasTag("sidewalk", sidewalkValues)) + weightToPrioMap.put(40d, AVOID.getValue()); + else + weightToPrioMap.put(40d, SLIGHT_AVOID.getValue()); + } else if (way.hasTag("sidewalk", sidewalksNoValues)) + weightToPrioMap.put(40d, AVOID.getValue()); + + if (way.hasTag("bicycle", "official") || way.hasTag("bicycle", "designated")) + weightToPrioMap.put(44d, SLIGHT_AVOID.getValue()); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcycleAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcycleAccessParser.java new file mode 100644 index 00000000000..10862b879d7 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcycleAccessParser.java @@ -0,0 +1,109 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.routing.util.WayAccess; +import com.graphhopper.util.PMap; + +import java.util.Arrays; + +public class MotorcycleAccessParser extends CarAccessParser { + + public MotorcycleAccessParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "motorcycle"))), + lookup.getBooleanEncodedValue(Roundabout.KEY), + properties, + TransportationMode.MOTORCYCLE); + } + + public MotorcycleAccessParser(BooleanEncodedValue accessEnc, BooleanEncodedValue roundaboutEnc, + PMap properties, TransportationMode transportationMode) { + super(accessEnc, roundaboutEnc, properties, transportationMode); + + barriers.remove("bus_trap"); + barriers.remove("sump_buster"); + + trackTypeValues.clear(); + trackTypeValues.addAll(Arrays.asList("grade1")); + } + + @Override + public WayAccess getAccess(ReaderWay way) { + String highwayValue = way.getTag("highway"); + String firstValue = way.getFirstPriorityTag(restrictions); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) { + if (restrictedValues.contains(firstValue)) + return WayAccess.CAN_SKIP; + if (intendedValues.contains(firstValue) || + // implied default is allowed only if foot and bicycle is not specified: + firstValue.isEmpty() && !way.hasTag("foot") && !way.hasTag("bicycle")) + return WayAccess.FERRY; + } + return WayAccess.CAN_SKIP; + } + + if ("service".equals(highwayValue) && "emergency_access".equals(way.getTag("service"))) { + return WayAccess.CAN_SKIP; + } + + if ("track".equals(highwayValue)) { + String tt = way.getTag("tracktype"); + if (tt != null && !tt.equals("grade1")) + return WayAccess.CAN_SKIP; + } + + if (!highwayValues.contains(highwayValue)) + return WayAccess.CAN_SKIP; + + if (way.hasTag("impassable", "yes") || way.hasTag("status", "impassable")) + return WayAccess.CAN_SKIP; + + if (!firstValue.isEmpty()) { + String[] restrict = firstValue.split(";"); + boolean notConditionalyPermitted = !getConditionalTagInspector().isRestrictedWayConditionallyPermitted(way); + for (String value : restrict) { + if (restrictedValues.contains(value) && notConditionalyPermitted) + return WayAccess.CAN_SKIP; + if (intendedValues.contains(value)) + return WayAccess.WAY; + } + } + + // do not drive street cars into fords + if (isBlockFords() && ("ford".equals(highwayValue) || way.hasTag("ford"))) + return WayAccess.CAN_SKIP; + + if (getConditionalTagInspector().isPermittedWayConditionallyRestricted(way)) + return WayAccess.CAN_SKIP; + else + return WayAccess.WAY; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + WayAccess access = getAccess(way); + if (access.canSkip()) + return; + + if (!access.isFerry()) { + + boolean isRoundabout = roundaboutEnc.getBool(false, edgeId, edgeIntAccess); + if (way.hasTag("oneway", oneways) || isRoundabout) { + if (way.hasTag("oneway", "-1")) { + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } else { + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + } + } else { + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + } + + } else { + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcycleAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcycleAverageSpeedParser.java new file mode 100644 index 00000000000..d8106bcd174 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcycleAverageSpeedParser.java @@ -0,0 +1,83 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; +import com.graphhopper.util.PMap; + +public class MotorcycleAverageSpeedParser extends CarAverageSpeedParser { + public static final double MOTORCYCLE_MAX_SPEED = 120; + + public MotorcycleAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { + this( + lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "motorcycle"))), + lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "motorcycle"))).getNextStorableValue(MOTORCYCLE_MAX_SPEED) + ); + } + + public MotorcycleAverageSpeedParser(DecimalEncodedValue speedEnc, double maxPossibleSpeed) { + super(speedEnc, maxPossibleSpeed); + + defaultSpeedMap.clear(); + + // autobahn + defaultSpeedMap.put("motorway", 100); + defaultSpeedMap.put("motorway_link", 70); + // bundesstraße + defaultSpeedMap.put("trunk", 80); + defaultSpeedMap.put("trunk_link", 75); + // linking bigger town + defaultSpeedMap.put("primary", 65); + defaultSpeedMap.put("primary_link", 60); + // linking towns + villages + defaultSpeedMap.put("secondary", 60); + defaultSpeedMap.put("secondary_link", 50); + // streets without middle line separation + defaultSpeedMap.put("tertiary", 50); + defaultSpeedMap.put("tertiary_link", 40); + defaultSpeedMap.put("unclassified", 30); + defaultSpeedMap.put("residential", 30); + // spielstraße + defaultSpeedMap.put("living_street", 5); + defaultSpeedMap.put("service", 20); + // unknown road + defaultSpeedMap.put("road", 20); + // forestry stuff + defaultSpeedMap.put("track", 15); + + trackTypeSpeedMap.clear(); + trackTypeSpeedMap.put("grade1", 20); + trackTypeSpeedMap.put(null, defaultSpeedMap.get("track")); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + String highwayValue = way.getTag("highway"); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) { + double ferrySpeed = ferrySpeedCalc.getSpeed(way); + setSpeed(false, edgeId, edgeIntAccess, ferrySpeed); + setSpeed(true, edgeId, edgeIntAccess, ferrySpeed); + } + } else { + double speed = getSpeed(way); + setSpeed(true, edgeId, edgeIntAccess, applyMaxSpeed(way, speed, true)); + setSpeed(false, edgeId, edgeIntAccess, applyMaxSpeed(way, speed, true)); + } + } + + protected double applyMaxSpeed(ReaderWay way, double speed, boolean bwd) { + speed = super.applyMaxSpeed(way, speed, bwd); + double maxMCSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed:motorcycle")); + if (isValidSpeed(maxMCSpeed)) + speed = Math.min(maxMCSpeed * 0.9, speed); + + // limit speed to max 30 km/h if bad surface + if (isValidSpeed(speed) && speed > 30 && way.hasTag("surface", badSurfaceSpeedMap)) + speed = 30; + return speed; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcyclePriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcyclePriorityParser.java new file mode 100644 index 00000000000..65c9a56fdb2 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MotorcyclePriorityParser.java @@ -0,0 +1,51 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.VehiclePriority; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.PMap; + +import java.util.HashSet; + +public class MotorcyclePriorityParser implements TagParser { + + private final HashSet avoidSet = new HashSet<>(); + private final HashSet preferSet = new HashSet<>(); + private final DecimalEncodedValue priorityWayEncoder; + + public MotorcyclePriorityParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "motorcycle")))); + } + + public MotorcyclePriorityParser(DecimalEncodedValue priorityWayEncoder) { + this.priorityWayEncoder = priorityWayEncoder; + + avoidSet.add("motorway"); + avoidSet.add("trunk"); + avoidSet.add("residential"); + + preferSet.add("primary"); + preferSet.add("secondary"); + preferSet.add("tertiary"); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + priorityWayEncoder.setDecimal(false, edgeId, edgeIntAccess, PriorityCode.getValue(handlePriority(way))); + } + + private int handlePriority(ReaderWay way) { + String highway = way.getTag("highway", ""); + if (avoidSet.contains(highway) || way.hasTag("motorroad", "yes")) { + return PriorityCode.BAD.getValue(); + } else if (preferSet.contains(highway)) { + return PriorityCode.BEST.getValue(); + } + + return PriorityCode.UNCHANGED.getValue(); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAccessParser.java new file mode 100644 index 00000000000..ffa0672c005 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAccessParser.java @@ -0,0 +1,28 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.Roundabout; +import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.util.PMap; + +public class MountainBikeAccessParser extends BikeCommonAccessParser { + + public MountainBikeAccessParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "mtb"))), + lookup.getBooleanEncodedValue(Roundabout.KEY)); + blockPrivate(properties.getBool("block_private", true)); + blockFords(properties.getBool("block_fords", false)); + } + + protected MountainBikeAccessParser(BooleanEncodedValue accessEnc, BooleanEncodedValue roundaboutEnc) { + super(accessEnc, roundaboutEnc); + } + + @Override + boolean isSacScaleAllowed(String sacScale) { + // other scales are too dangerous even for MTB, see http://wiki.openstreetmap.org/wiki/Key:sac_scale + return "hiking".equals(sacScale) || "mountain_hiking".equals(sacScale) + || "demanding_mountain_hiking".equals(sacScale) || "alpine_hiking".equals(sacScale); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAverageSpeedParser.java new file mode 100644 index 00000000000..650f72da2e8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikeAverageSpeedParser.java @@ -0,0 +1,47 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; + +public class MountainBikeAverageSpeedParser extends BikeCommonAverageSpeedParser { + + public MountainBikeAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "mtb"))), + lookup.getEnumEncodedValue(Smoothness.KEY, Smoothness.class)); + } + + protected MountainBikeAverageSpeedParser(DecimalEncodedValue speedEnc, EnumEncodedValue smoothnessEnc) { + super(speedEnc, smoothnessEnc); + setTrackTypeSpeed("grade1", 18); // paved + setTrackTypeSpeed("grade2", 16); // now unpaved ... + setTrackTypeSpeed("grade3", 12); + setTrackTypeSpeed("grade4", 8); + setTrackTypeSpeed("grade5", 6); // like sand/grass + + setSurfaceSpeed("concrete", 14); + setSurfaceSpeed("concrete:lanes", 16); + setSurfaceSpeed("concrete:plates", 16); + setSurfaceSpeed("dirt", 14); + setSurfaceSpeed("earth", 14); + setSurfaceSpeed("fine_gravel", 18); + setSurfaceSpeed("grass", 14); + setSurfaceSpeed("grass_paver", 14); + setSurfaceSpeed("gravel", 16); + setSurfaceSpeed("ground", 16); + setSurfaceSpeed("ice", MIN_SPEED); + setSurfaceSpeed("metal", 10); + setSurfaceSpeed("mud", 12); + setSurfaceSpeed("salt", 12); + setSurfaceSpeed("sand", 10); + setSurfaceSpeed("wood", 10); + + setHighwaySpeed("living_street", PUSHING_SECTION_SPEED); + setHighwaySpeed("steps", PUSHING_SECTION_SPEED); + + setHighwaySpeed("path", 18); + setHighwaySpeed("footway", PUSHING_SECTION_SPEED); + setHighwaySpeed("pedestrian", PUSHING_SECTION_SPEED); + setHighwaySpeed("track", 18); + setHighwaySpeed("residential", 16); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikePriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikePriorityParser.java new file mode 100644 index 00000000000..d81e7246205 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/MountainBikePriorityParser.java @@ -0,0 +1,61 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; + +import java.util.TreeMap; + +import static com.graphhopper.routing.ev.RouteNetwork.*; +import static com.graphhopper.routing.util.PriorityCode.*; + +public class MountainBikePriorityParser extends BikeCommonPriorityParser { + + public MountainBikePriorityParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "mtb"))), + lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "mtb"))), + lookup.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class)); + } + + protected MountainBikePriorityParser(DecimalEncodedValue speedEnc, DecimalEncodedValue priorityEnc, + EnumEncodedValue bikeRouteEnc) { + super(priorityEnc, speedEnc, bikeRouteEnc); + + routeMap.put(INTERNATIONAL, PREFER.getValue()); + routeMap.put(NATIONAL, PREFER.getValue()); + routeMap.put(REGIONAL, PREFER.getValue()); + routeMap.put(LOCAL, BEST.getValue()); + + avoidHighwayTags.add("primary"); + avoidHighwayTags.add("primary_link"); + avoidHighwayTags.add("secondary"); + avoidHighwayTags.add("secondary_link"); + + preferHighwayTags.add("road"); + preferHighwayTags.add("track"); + preferHighwayTags.add("path"); + preferHighwayTags.add("service"); + preferHighwayTags.add("tertiary"); + preferHighwayTags.add("tertiary_link"); + preferHighwayTags.add("residential"); + preferHighwayTags.add("unclassified"); + + setSpecificClassBicycle("mtb"); + } + + @Override + void collect(ReaderWay way, double wayTypeSpeed, TreeMap weightToPrioMap) { + super.collect(way, wayTypeSpeed, weightToPrioMap); + + String highway = way.getTag("highway"); + if ("track".equals(highway)) { + String trackType = way.getTag("tracktype"); + if ("grade1".equals(trackType)) + weightToPrioMap.put(50d, UNCHANGED.getValue()); + else if (trackType == null) + weightToPrioMap.put(90d, PREFER.getValue()); + else if (trackType.startsWith("grade")) + weightToPrioMap.put(100d, VERY_NICE.getValue()); + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMAccessParser.java deleted file mode 100644 index a61728d73d0..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMAccessParser.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.RoadAccess; -import com.graphhopper.routing.util.TransportationMode; -import com.graphhopper.routing.util.countryrules.CountryRule; -import com.graphhopper.storage.IntsRef; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static com.graphhopper.routing.ev.RoadAccess.YES; - -// TODO rename to car_restricted or car_allowed? -// if car_allowed == false -// if car_access == false -public class OSMAccessParser implements TagParser { - - private final BooleanEncodedValue accessEnc; - private final List restrictions; - private final TransportationMode transportationMode; - private final Set restrictedValues = new HashSet<>(10); - private final Set oneways = new HashSet<>(5); - private final HashSet oppositeLanes = new HashSet<>(); - private final BooleanEncodedValue roundaboutEnc; - - private final Set intendedValues = new HashSet<>(5); - - public OSMAccessParser(BooleanEncodedValue accessEnc, BooleanEncodedValue roundaboutEnc, List restrictions, TransportationMode transportationMode) { - this.roundaboutEnc = roundaboutEnc; - this.accessEnc = accessEnc; - this.restrictions = restrictions; - this.transportationMode = transportationMode; - - restrictedValues.add("agricultural"); - restrictedValues.add("forestry"); - restrictedValues.add("no"); - restrictedValues.add("restricted"); - restrictedValues.add("delivery"); - restrictedValues.add("military"); - restrictedValues.add("emergency"); - restrictedValues.add("private"); - - oneways.add("yes"); - oneways.add("true"); - oneways.add("1"); - oneways.add("-1"); - - intendedValues.add("yes"); - intendedValues.add("designated"); - intendedValues.add("official"); - intendedValues.add("permissive"); - - oppositeLanes.add("opposite"); - oppositeLanes.add("opposite_lane"); - oppositeLanes.add("opposite_track"); - } - - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { - RoadAccess accessValue = YES; - RoadAccess tmpAccessValue; - for (String restriction : restrictions) { - tmpAccessValue = RoadAccess.find(way.getTag(restriction, "yes")); - if (tmpAccessValue != null && tmpAccessValue.ordinal() > accessValue.ordinal()) { - accessValue = tmpAccessValue; - } - } - - CountryRule countryRule = way.getTag("country_rule", null); - if (countryRule != null) - accessValue = countryRule.getAccess(way, transportationMode, accessValue); - - boolean access = accessValue != RoadAccess.NO; - accessEnc.setBool(false, edgeFlags, access); - - if (access) { - boolean isRoundabout = roundaboutEnc.getBool(false, edgeFlags); - if (transportationMode.isMotorVehicle() && (isOneway(way) || isRoundabout)) { - if (isForwardOneway(way)) - accessEnc.setBool(false, edgeFlags, true); - if (isBackwardOneway(way)) - accessEnc.setBool(true, edgeFlags, true); - } else if (transportationMode == TransportationMode.BIKE - && (isBikeOneway(way) || isRoundabout && !way.hasTag("oneway:bicycle", "no") - && !way.hasTag("cycleway", oppositeLanes) - && !way.hasTag("cycleway:left", oppositeLanes) - && !way.hasTag("cycleway:right", oppositeLanes) - && !way.hasTag("cycleway:left:oneway", "-1") - && !way.hasTag("cycleway:right:oneway", "-1"))) { - boolean isBackward = way.hasTag("oneway", "-1") - || way.hasTag("oneway:bicycle", "-1") - || way.hasTag("vehicle:forward", restrictedValues) - || way.hasTag("bicycle:forward", restrictedValues); - accessEnc.setBool(isBackward, edgeFlags, true); - } else { - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - } - } - - return edgeFlags; - } - - /** - * make sure that isOneway is called before - */ - protected boolean isBackwardOneway(ReaderWay way) { - return way.hasTag("oneway", "-1") - || way.hasTag("vehicle:forward", restrictedValues) - || way.hasTag("motor_vehicle:forward", restrictedValues); - } - - /** - * make sure that isOneway is called before - */ - protected boolean isForwardOneway(ReaderWay way) { - return !way.hasTag("oneway", "-1") - && !way.hasTag("vehicle:forward", restrictedValues) - && !way.hasTag("motor_vehicle:forward", restrictedValues); - } - - protected boolean isOneway(ReaderWay way) { - return way.hasTag("oneway", oneways) - || way.hasTag("vehicle:backward", restrictedValues) - || way.hasTag("vehicle:forward", restrictedValues) - || way.hasTag("motor_vehicle:backward", restrictedValues) - || way.hasTag("motor_vehicle:forward", restrictedValues); - } - - - boolean isBikeOneway(ReaderWay way) { - return way.hasTag("oneway", oneways) && !way.hasTag("oneway", "-1") && !way.hasTag("bicycle:backward", intendedValues) - || way.hasTag("oneway", "-1") && !way.hasTag("bicycle:forward", intendedValues) - || way.hasTag("oneway:bicycle", oneways) - || way.hasTag("vehicle:backward", restrictedValues) && !way.hasTag("bicycle:forward", intendedValues) - || way.hasTag("vehicle:forward", restrictedValues) && !way.hasTag("bicycle:backward", intendedValues) - || way.hasTag("bicycle:forward", restrictedValues) - || way.hasTag("bicycle:backward", restrictedValues); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMBikeNetworkTagParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMBikeNetworkTagParser.java index 64fe7c087c5..0788d464004 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMBikeNetworkTagParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMBikeNetworkTagParser.java @@ -19,9 +19,7 @@ import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RouteNetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.Helper; @@ -38,8 +36,9 @@ public OSMBikeNetworkTagParser(EnumEncodedValue bikeRouteEnc, Enco } @Override - public IntsRef handleRelationTags(IntsRef relFlags, ReaderRelation relation) { - RouteNetwork oldBikeNetwork = transformerRouteRelEnc.getEnum(false, relFlags); + public void handleRelationTags(IntsRef relFlags, ReaderRelation relation) { + IntsRefEdgeIntAccess relIntAccess = new IntsRefEdgeIntAccess(relFlags); + RouteNetwork oldBikeNetwork = transformerRouteRelEnc.getEnum(false, -1, relIntAccess); if (relation.hasTag("route", "bicycle")) { String tag = Helper.toLowerCase(relation.getTag("network", "")); RouteNetwork newBikeNetwork = RouteNetwork.LOCAL; @@ -53,17 +52,19 @@ public IntsRef handleRelationTags(IntsRef relFlags, ReaderRelation relation) { newBikeNetwork = RouteNetwork.INTERNATIONAL; } if (oldBikeNetwork == RouteNetwork.MISSING || oldBikeNetwork.ordinal() > newBikeNetwork.ordinal()) - transformerRouteRelEnc.setEnum(false, relFlags, newBikeNetwork); + transformerRouteRelEnc.setEnum(false, -1, relIntAccess, newBikeNetwork); } - - return relFlags; } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { // just copy value into different bit range - RouteNetwork routeNetwork = transformerRouteRelEnc.getEnum(false, relationFlags); - bikeRouteEnc.setEnum(false, edgeFlags, routeNetwork); - return edgeFlags; + IntsRefEdgeIntAccess relIntAccess = new IntsRefEdgeIntAccess(relationFlags); + RouteNetwork routeNetwork = transformerRouteRelEnc.getEnum(false, -1, relIntAccess); + bikeRouteEnc.setEnum(false, edgeId, edgeIntAccess, routeNetwork); + } + + public EnumEncodedValue getTransformerRouteRelEnc() { + return transformerRouteRelEnc; } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMCrossingParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMCrossingParser.java new file mode 100644 index 00000000000..5f463714b17 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMCrossingParser.java @@ -0,0 +1,62 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.Crossing; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.Helper; + +import java.util.List; +import java.util.Map; + +/** + * Parses the node information regarding crossing=* and railway=* + */ +public class OSMCrossingParser implements TagParser { + protected final EnumEncodedValue crossingEnc; + + public OSMCrossingParser(EnumEncodedValue crossingEnc) { + this.crossingEnc = crossingEnc; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { + List> nodeTags = readerWay.getTag("node_tags", null); + if (nodeTags == null) + return; + + for (int i = 0; i < nodeTags.size(); i++) { + Map tags = nodeTags.get(i); + if ("crossing".equals(tags.get("railway")) || "level_crossing".equals(tags.get("railway"))) { + String barrierVal = (String) tags.get("crossing:barrier"); + crossingEnc.setEnum(false, edgeId, edgeIntAccess, (Helper.isEmpty(barrierVal) || "no".equals(barrierVal)) ? Crossing.RAILWAY : Crossing.RAILWAY_BARRIER); + return; + } + + String crossingSignals = (String) tags.get("crossing:signals"); + if ("yes".equals(crossingSignals)) { + crossingEnc.setEnum(false, edgeId, edgeIntAccess, Crossing.TRAFFIC_SIGNALS); + return; + } + + String crossingMarkings = (String) tags.get("crossing:markings"); + if ("yes".equals(crossingMarkings)) { + crossingEnc.setEnum(false, edgeId, edgeIntAccess, Crossing.MARKED); + return; + } + + String crossingValue = (String) tags.get("crossing"); + // some crossing values like "no" do not require highway=crossing and sometimes no crossing value exists although highway=crossing + if (Helper.isEmpty(crossingValue) && ("no".equals(crossingSignals) || "no".equals(crossingMarkings) + || "crossing".equals(tags.get("highway")) || "crossing".equals(tags.get("footway")) || "crossing".equals(tags.get("cycleway")))) { + crossingEnc.setEnum(false, edgeId, edgeIntAccess, Crossing.UNMARKED); + // next node could have more specific Crossing value + continue; + } + Crossing crossing = Crossing.find(crossingValue); + if (crossing != Crossing.MISSING) + crossingEnc.setEnum(false, edgeId, edgeIntAccess, crossing); + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMFootNetworkTagParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMFootNetworkTagParser.java index af93f8e2ed9..878d7db0769 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMFootNetworkTagParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMFootNetworkTagParser.java @@ -19,9 +19,7 @@ import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RouteNetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.Helper; @@ -38,8 +36,9 @@ public OSMFootNetworkTagParser(EnumEncodedValue footRouteEnc, Enco } @Override - public IntsRef handleRelationTags(IntsRef relFlags, ReaderRelation relation) { - RouteNetwork oldFootNetwork = transformerRouteRelEnc.getEnum(false, relFlags); + public void handleRelationTags(IntsRef relFlags, ReaderRelation relation) { + IntsRefEdgeIntAccess relIntAccess = new IntsRefEdgeIntAccess(relFlags); + RouteNetwork oldFootNetwork = transformerRouteRelEnc.getEnum(false, -1, relIntAccess); if (relation.hasTag("route", "hiking") || relation.hasTag("route", "foot")) { String tag = Helper.toLowerCase(relation.getTag("network", "")); RouteNetwork newFootNetwork = RouteNetwork.LOCAL; @@ -53,17 +52,15 @@ public IntsRef handleRelationTags(IntsRef relFlags, ReaderRelation relation) { newFootNetwork = RouteNetwork.INTERNATIONAL; } if (oldFootNetwork == RouteNetwork.MISSING || oldFootNetwork.ordinal() > newFootNetwork.ordinal()) - transformerRouteRelEnc.setEnum(false, relFlags, newFootNetwork); + transformerRouteRelEnc.setEnum(false, -1, relIntAccess, newFootNetwork); } - - return relFlags; } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { // just copy value into different bit range - RouteNetwork footNetwork = transformerRouteRelEnc.getEnum(false, relationFlags); - footRouteEnc.setEnum(false, edgeFlags, footNetwork); - return edgeFlags; + IntsRefEdgeIntAccess relIntAccess = new IntsRefEdgeIntAccess(relationFlags); + RouteNetwork footNetwork = transformerRouteRelEnc.getEnum(false, -1, relIntAccess); + footRouteEnc.setEnum(false, edgeId, edgeIntAccess, footNetwork); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMFootwayParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMFootwayParser.java new file mode 100644 index 00000000000..a4a51f99ca9 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMFootwayParser.java @@ -0,0 +1,42 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.Footway; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.storage.IntsRef; + +/** + * see: https://wiki.openstreetmap.org/wiki/Key%3Afootway + */ +public class OSMFootwayParser implements TagParser { + private final EnumEncodedValue footwayEnc; + + public OSMFootwayParser(EnumEncodedValue footwayEnc) { + this.footwayEnc = footwayEnc; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + String footway = way.getTag("footway"); + footwayEnc.setEnum(false, edgeId, edgeIntAccess, Footway.find(footway)); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParser.java index b10156af506..c00a7feada6 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParser.java @@ -2,6 +2,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.storage.IntsRef; import java.util.Arrays; @@ -9,30 +10,39 @@ import java.util.List; /** - * This parser scans different OSM tags to identify ways where a cyclist has to get off her bike. + * This parser scans different OSM tags to identify ways where a cyclist has to get off her bike. Like on footway but + * also in reverse oneway direction. */ public class OSMGetOffBikeParser implements TagParser { - private final HashSet pushBikeHighwayTags = new HashSet<>(); - private final List accepted = Arrays.asList("designated", "yes", "official", "permissive"); - private final BooleanEncodedValue offBikeEnc; - public OSMGetOffBikeParser(BooleanEncodedValue getOffBikeEnc) { - // steps -> special handling - this(getOffBikeEnc, Arrays.asList("path", "footway", "pedestrian", "platform")); - } + private final List INTENDED = Arrays.asList("designated", "yes", "official", "permissive"); + // steps -> special handling + private final HashSet GET_OFF_BIKE = new HashSet<>(Arrays.asList("path", "footway", "pedestrian", "platform")); + private final BooleanEncodedValue getOffBikeEnc; + private final BooleanEncodedValue bikeAccessEnc; - public OSMGetOffBikeParser(BooleanEncodedValue getOffBikeEnc, List pushBikeTags) { - offBikeEnc = getOffBikeEnc; - pushBikeHighwayTags.addAll(pushBikeTags); + /** + * @param bikeAccessEnc used to find out if way is oneway and so it does not matter which bike type is used. + */ + public OSMGetOffBikeParser(BooleanEncodedValue getOffBikeEnc, BooleanEncodedValue bikeAccessEnc) { + this.getOffBikeEnc = getOffBikeEnc; + this.bikeAccessEnc = bikeAccessEnc; } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { String highway = way.getTag("highway"); - if (!way.hasTag("bicycle", accepted) && (pushBikeHighwayTags.contains(highway) || way.hasTag("railway", "platform")) + if (!way.hasTag("bicycle", INTENDED) && (GET_OFF_BIKE.contains(highway) || way.hasTag("railway", "platform")) || "steps".equals(highway) || way.hasTag("bicycle", "dismount")) { - offBikeEnc.setBool(false, edgeFlags, true); + getOffBikeEnc.setBool(false, edgeId, edgeIntAccess, true); + getOffBikeEnc.setBool(true, edgeId, edgeIntAccess, true); + } + boolean fwd = bikeAccessEnc.getBool(false, edgeId, edgeIntAccess); + boolean bwd = bikeAccessEnc.getBool(true, edgeId, edgeIntAccess); + // get off bike for reverse oneways + if (fwd != bwd) { + if (!fwd) getOffBikeEnc.setBool(false, edgeId, edgeIntAccess, true); + if (!bwd) getOffBikeEnc.setBool(true, edgeId, edgeIntAccess, true); } - return edgeFlags; } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatParser.java index 4f2b40de1fc..d7c6000480f 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatParser.java @@ -3,6 +3,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; import com.graphhopper.routing.ev.Hazmat; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.storage.IntsRef; public class OSMHazmatParser implements TagParser { @@ -14,10 +15,8 @@ public OSMHazmatParser(EnumEncodedValue hazEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { - if (readerWay.hasTag("hazmat", "no")) { - hazEnc.setEnum(false, edgeFlags, Hazmat.NO); - } - return edgeFlags; + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { + if (readerWay.hasTag("hazmat", "no")) + hazEnc.setEnum(false, edgeId, edgeIntAccess, Hazmat.NO); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatTunnelParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatTunnelParser.java index 3fb228fb119..bc20e6d331f 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatTunnelParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatTunnelParser.java @@ -3,6 +3,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; import com.graphhopper.routing.ev.HazmatTunnel; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.storage.IntsRef; public class OSMHazmatTunnelParser implements TagParser { @@ -24,23 +25,21 @@ public OSMHazmatTunnelParser(EnumEncodedValue hazTunnelEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { if (readerWay.hasTag("hazmat:adr_tunnel_cat", TUNNEL_CATEGORY_NAMES)) { HazmatTunnel code = HazmatTunnel.valueOf(readerWay.getTag("hazmat:adr_tunnel_cat")); - hazTunnelEnc.setEnum(false, edgeFlags, code); + hazTunnelEnc.setEnum(false, edgeId, edgeIntAccess, code); } else if (readerWay.hasTag("hazmat:tunnel_cat", TUNNEL_CATEGORY_NAMES)) { HazmatTunnel code = HazmatTunnel.valueOf(readerWay.getTag("hazmat:tunnel_cat")); - hazTunnelEnc.setEnum(false, edgeFlags, code); + hazTunnelEnc.setEnum(false, edgeId, edgeIntAccess, code); } else if (readerWay.hasTag("tunnel", "yes")) { HazmatTunnel[] codes = HazmatTunnel.values(); for (int i = codes.length - 1; i >= 0; i--) { if (readerWay.hasTag("hazmat:" + codes[i].name(), "no")) { - hazTunnelEnc.setEnum(false, edgeFlags, codes[i]); + hazTunnelEnc.setEnum(false, edgeId, edgeIntAccess, codes[i]); break; } } } - - return edgeFlags; } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatWaterParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatWaterParser.java index c6376b3e596..7c1cc986a1c 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatWaterParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHazmatWaterParser.java @@ -3,6 +3,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; import com.graphhopper.routing.ev.HazmatWater; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.storage.IntsRef; @@ -15,13 +16,12 @@ public OSMHazmatWaterParser(EnumEncodedValue hazWaterEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { if (readerWay.hasTag("hazmat:water", "no")) { - hazWaterEnc.setEnum(false, edgeFlags, HazmatWater.NO); + hazWaterEnc.setEnum(false, edgeId, edgeIntAccess, HazmatWater.NO); } else if (readerWay.hasTag("hazmat:water", "permissive")) { - hazWaterEnc.setEnum(false, edgeFlags, HazmatWater.PERMISSIVE); + hazWaterEnc.setEnum(false, edgeId, edgeIntAccess, HazmatWater.PERMISSIVE); } - return edgeFlags; } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHgvParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHgvParser.java new file mode 100644 index 00000000000..03918c5cb31 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHgvParser.java @@ -0,0 +1,20 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.Hgv; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.storage.IntsRef; + +public class OSMHgvParser implements TagParser { + EnumEncodedValue hgvEnc; + + public OSMHgvParser(EnumEncodedValue hgvEnc) { + this.hgvEnc = hgvEnc; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + hgvEnc.setEnum(false, edgeId, edgeIntAccess, Hgv.find(way.getTag("hgv"))); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHikeRatingParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHikeRatingParser.java index 1efccd09114..8e99957d6c9 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHikeRatingParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHikeRatingParser.java @@ -18,6 +18,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.IntEncodedValue; import com.graphhopper.storage.IntsRef; @@ -35,7 +36,7 @@ public OSMHikeRatingParser(IntEncodedValue sacScaleEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { String scale = readerWay.getTag("sac_scale"); int rating = 0; if (scale != null) { @@ -47,8 +48,6 @@ public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef rel else if (scale.equals("difficult_alpine_hiking")) rating = 6; } if (rating != 0) - sacScaleEnc.setInt(false, edgeFlags, rating); - - return edgeFlags; + sacScaleEnc.setInt(false, edgeId, edgeIntAccess, rating); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHorseRatingParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHorseRatingParser.java index 4c860da9276..ab701c3fba9 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHorseRatingParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMHorseRatingParser.java @@ -18,6 +18,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.IntEncodedValue; import com.graphhopper.storage.IntsRef; @@ -35,7 +36,7 @@ public OSMHorseRatingParser(IntEncodedValue horseScale) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { String scale = readerWay.getTag("horse_scale"); int rating = 0; if (scale != null) { @@ -47,7 +48,6 @@ public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef rel else if (scale.equals("impossible")) rating = 6; } if (rating != 0) - horseScale.setInt(false, edgeFlags, rating); - return edgeFlags; + horseScale.setInt(false, edgeId, edgeIntAccess, rating); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMLanesParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMLanesParser.java index d65116e9d7c..946cf6b0811 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMLanesParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMLanesParser.java @@ -19,6 +19,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.IntEncodedValue; import com.graphhopper.storage.IntsRef; @@ -33,7 +34,7 @@ public OSMLanesParser(IntEncodedValue lanesEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { int laneCount = 1; if (way.hasTag("lanes")) { String noLanes = way.getTag("lanes"); @@ -53,7 +54,6 @@ else if (noLanesInt > 6) } } } - lanesEnc.setInt(false, edgeFlags, laneCount); - return edgeFlags; + lanesEnc.setInt(false, edgeId, edgeIntAccess, laneCount); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxAxleLoadParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxAxleLoadParser.java index 827b92b83e9..d849d5f09ac 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxAxleLoadParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxAxleLoadParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; import com.graphhopper.storage.IntsRef; @@ -33,8 +34,7 @@ public OSMMaxAxleLoadParser(DecimalEncodedValue maxAxleLoadEncoder) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { - OSMValueExtractor.extractTons(edgeFlags, way, maxAxleLoadEncoder, Collections.singletonList("maxaxleload")); - return edgeFlags; + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + OSMValueExtractor.extractTons(edgeId, edgeIntAccess, way, maxAxleLoadEncoder, Collections.singletonList("maxaxleload")); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxHeightParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxHeightParser.java index b8b3821536e..27615d6366f 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxHeightParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxHeightParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; import com.graphhopper.storage.IntsRef; @@ -34,9 +35,8 @@ public OSMMaxHeightParser(DecimalEncodedValue heightEncoder) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { List heightTags = Arrays.asList("maxheight", "maxheight:physical"/*, the OSM tag "height" is not used for the height of a road, so omit it here! */); - OSMValueExtractor.extractMeter(edgeFlags, way, heightEncoder, heightTags); - return edgeFlags; + OSMValueExtractor.extractMeter(edgeId, edgeIntAccess, way, heightEncoder, heightTags); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxLengthParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxLengthParser.java index 7b3b2072cd9..ce3e8c2c9c8 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxLengthParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxLengthParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; import com.graphhopper.storage.IntsRef; @@ -33,8 +34,7 @@ public OSMMaxLengthParser(DecimalEncodedValue lengthEncoder) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { - OSMValueExtractor.extractMeter(edgeFlags, way, lengthEncoder, Collections.singletonList("maxlength")); - return edgeFlags; + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + OSMValueExtractor.extractMeter(edgeId, edgeIntAccess, way, lengthEncoder, Collections.singletonList("maxlength")); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxSpeedParser.java index 49f70a90107..29151f33f8e 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxSpeedParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxSpeedParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.MaxSpeed; import com.graphhopper.routing.util.TransportationMode; import com.graphhopper.routing.util.countryrules.CountryRule; @@ -39,7 +40,7 @@ public OSMMaxSpeedParser(DecimalEncodedValue carMaxSpeedEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { double maxSpeed = OSMValueExtractor.stringToKmh(way.getTag("maxspeed")); CountryRule countryRule = way.getTag("country_rule", null); @@ -61,12 +62,11 @@ public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationF if (!isValidSpeed(fwdSpeed)) fwdSpeed = UNSET_SPEED; - carMaxSpeedEnc.setDecimal(false, edgeFlags, fwdSpeed); + carMaxSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, fwdSpeed); if (!isValidSpeed(bwdSpeed)) bwdSpeed = UNSET_SPEED; - carMaxSpeedEnc.setDecimal(true, edgeFlags, bwdSpeed); - return edgeFlags; + carMaxSpeedEnc.setDecimal(true, edgeId, edgeIntAccess, bwdSpeed); } /** diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxWeightParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxWeightParser.java index 8aa5890c7b1..7b397b3263a 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxWeightParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxWeightParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; import com.graphhopper.storage.IntsRef; @@ -34,10 +35,9 @@ public OSMMaxWeightParser(DecimalEncodedValue weightEncoder) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { // do not include OSM tag "height" here as it has completely different meaning (height of peak) List weightTags = Arrays.asList("maxweight", "maxgcweight"); - OSMValueExtractor.extractTons(edgeFlags, way, weightEncoder, weightTags); - return edgeFlags; + OSMValueExtractor.extractTons(edgeId, edgeIntAccess, way, weightEncoder, weightTags); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxWidthParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxWidthParser.java index 8f81391f77e..b3f65145308 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxWidthParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMaxWidthParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.util.parsers.helpers.OSMValueExtractor; import com.graphhopper.storage.IntsRef; @@ -34,9 +35,8 @@ public OSMMaxWidthParser(DecimalEncodedValue widthEncoder) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { List widthTags = Arrays.asList("maxwidth", "maxwidth:physical", "width"); - OSMValueExtractor.extractMeter(edgeFlags, way, widthEncoder, widthTags); - return edgeFlags; + OSMValueExtractor.extractMeter(edgeId, edgeIntAccess, way, widthEncoder, widthTags); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMtbRatingParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMtbRatingParser.java index 3590e997d91..ec425dbe1b5 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMtbRatingParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMMtbRatingParser.java @@ -18,6 +18,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.IntEncodedValue; import com.graphhopper.storage.IntsRef; @@ -26,7 +27,7 @@ * A mapping mtb:scale=0 corresponds to 1 and mtb:scale=1 to 2 until 7. * * @see Key:mtb:scale for details on OSM mountain biking difficulties. - * @see Single Trail Scale + * @see Single Trail Scale */ public class OSMMtbRatingParser implements TagParser { private final IntEncodedValue mtbRatingEnc; @@ -36,7 +37,7 @@ public OSMMtbRatingParser(IntEncodedValue mtbRatingEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { String scale = readerWay.getTag("mtb:scale"); int rating = 0; if (scale != null) { @@ -49,7 +50,6 @@ public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef rel } } if (rating > 0 && rating < 8) - mtbRatingEnc.setInt(false, edgeFlags, rating); - return edgeFlags; + mtbRatingEnc.setInt(false, edgeId, edgeIntAccess, rating); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParser.java index c27d58b0fc8..2534c747c10 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParser.java @@ -19,13 +19,16 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.RoadAccess; import com.graphhopper.routing.util.TransportationMode; import com.graphhopper.routing.util.countryrules.CountryRule; import com.graphhopper.storage.IntsRef; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import static com.graphhopper.routing.ev.RoadAccess.YES; @@ -39,22 +42,40 @@ public OSMRoadAccessParser(EnumEncodedValue roadAccessEnc, List accessValue.ordinal()) { - accessValue = tmpAccessValue; + + List> nodeTags = readerWay.getTag("node_tags", Collections.emptyList()); + // a barrier edge has the restriction in both nodes and the tags are the same + if (readerWay.hasTag("gh:barrier_edge")) + for (String restriction : restrictions) { + Object value = nodeTags.get(0).get(restriction); + if (value != null) accessValue = getRoadAccess((String) value, accessValue); } + + for (String restriction : restrictions) { + accessValue = getRoadAccess(readerWay.getTag(restriction), accessValue); } CountryRule countryRule = readerWay.getTag("country_rule", null); if (countryRule != null) accessValue = countryRule.getAccess(readerWay, TransportationMode.CAR, accessValue); - roadAccessEnc.setEnum(false, edgeFlags, accessValue); - return edgeFlags; + roadAccessEnc.setEnum(false, edgeId, edgeIntAccess, accessValue); + } + + private RoadAccess getRoadAccess(String tagValue, RoadAccess accessValue) { + RoadAccess tmpAccessValue; + if (tagValue != null) { + String[] complex = tagValue.split(";"); + for (String simple : complex) { + tmpAccessValue = RoadAccess.find(simple); + if (tmpAccessValue != null && tmpAccessValue.ordinal() > accessValue.ordinal()) { + accessValue = tmpAccessValue; + } + } + } + return accessValue; } public static List toOSMRestrictions(TransportationMode mode) { @@ -73,6 +94,8 @@ public static List toOSMRestrictions(TransportationMode mode) { return Arrays.asList("hgv", "motor_vehicle", "vehicle", "access"); case PSV: return Arrays.asList("psv", "motor_vehicle", "vehicle", "access"); + case BUS: + return Arrays.asList("bus", "psv", "motor_vehicle", "vehicle", "access"); default: throw new IllegalArgumentException("Cannot convert TransportationMode " + mode + " to list of restrictions"); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadClassLinkParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadClassLinkParser.java index a5337bdede2..a38727f3e8e 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadClassLinkParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadClassLinkParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.Helper; @@ -30,10 +31,9 @@ public OSMRoadClassLinkParser(BooleanEncodedValue linkEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { String highwayTag = readerWay.getTag("highway"); if (!Helper.isEmpty(highwayTag) && highwayTag.endsWith("_link")) - linkEnc.setBool(false, edgeFlags, true); - return edgeFlags; + linkEnc.setBool(false, edgeId, edgeIntAccess, true); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadClassParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadClassParser.java index c93e85dd5af..a46c3a96372 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadClassParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadClassParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.RoadClass; import com.graphhopper.storage.IntsRef; @@ -33,16 +34,15 @@ public OSMRoadClassParser(EnumEncodedValue roadClassEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { String roadClassTag = readerWay.getTag("highway"); if (roadClassTag == null) - return edgeFlags; + return; RoadClass roadClass = RoadClass.find(roadClassTag); if (roadClass == OTHER && roadClassTag.endsWith("_link")) roadClass = RoadClass.find(roadClassTag.substring(0, roadClassTag.length() - 5)); if (roadClass != OTHER) - roadClassEnc.setEnum(false, edgeFlags, roadClass); - return edgeFlags; + roadClassEnc.setEnum(false, edgeId, edgeIntAccess, roadClass); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadEnvironmentParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadEnvironmentParser.java index 1d6feb470fa..9ba52ce172d 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadEnvironmentParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoadEnvironmentParser.java @@ -19,9 +19,14 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.RoadEnvironment; import com.graphhopper.storage.IntsRef; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import static com.graphhopper.routing.ev.RoadEnvironment.*; public class OSMRoadEnvironmentParser implements TagParser { @@ -33,7 +38,14 @@ public OSMRoadEnvironmentParser(EnumEncodedValue roadEnvEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { + List> nodeTags = readerWay.getTag("node_tags", Collections.emptyList()); + // a barrier edge has the restriction in both nodes and the tags are the same + if (readerWay.hasTag("gh:barrier_edge") && nodeTags.get(0).containsKey("ford")) { + roadEnvEnc.setEnum(false, edgeId, edgeIntAccess, FORD); + return; + } + RoadEnvironment roadEnvironment = OTHER; if ((readerWay.hasTag("route", "ferry") && !readerWay.hasTag("ferry", "no")) || // TODO shuttle_train is sometimes also used in relations, e.g. https://www.openstreetmap.org/relation/1932780 @@ -49,7 +61,6 @@ else if (readerWay.hasTag("highway")) roadEnvironment = ROAD; if (roadEnvironment != OTHER) - roadEnvEnc.setEnum(false, edgeFlags, roadEnvironment); - return edgeFlags; + roadEnvEnc.setEnum(false, edgeId, edgeIntAccess, roadEnvironment); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoundaboutParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoundaboutParser.java index eba64918655..cad15014986 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoundaboutParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMRoundaboutParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.storage.IntsRef; public class OSMRoundaboutParser implements TagParser { @@ -30,10 +31,9 @@ public OSMRoundaboutParser(BooleanEncodedValue roundaboutEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { boolean isRoundabout = way.hasTag("junction", "roundabout") || way.hasTag("junction", "circular"); if (isRoundabout) - roundaboutEnc.setBool(false, edgeFlags, true); - return edgeFlags; + roundaboutEnc.setBool(false, edgeId, edgeIntAccess, true); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMSmoothnessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMSmoothnessParser.java index 8fed148fa81..f9435793ed2 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMSmoothnessParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMSmoothnessParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.Smoothness; import com.graphhopper.storage.IntsRef; @@ -33,13 +34,12 @@ public OSMSmoothnessParser(EnumEncodedValue smoothnessEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { String smoothnessTag = readerWay.getTag("smoothness"); Smoothness smoothness = Smoothness.find(smoothnessTag); if (smoothness == MISSING) - return edgeFlags; + return; - smoothnessEnc.setEnum(false, edgeFlags, smoothness); - return edgeFlags; + smoothnessEnc.setEnum(false, edgeId, edgeIntAccess, smoothness); } } \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMSurfaceParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMSurfaceParser.java index 60bb400ffc6..808070af301 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMSurfaceParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMSurfaceParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.Surface; import com.graphhopper.storage.IntsRef; @@ -33,11 +34,11 @@ public OSMSurfaceParser(EnumEncodedValue surfaceEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { String surfaceTag = readerWay.getTag("surface"); Surface surface = Surface.find(surfaceTag); if (surface == MISSING) - return edgeFlags; + return; if (surfaceTag.equals("metal")) surface = PAVED; @@ -48,7 +49,6 @@ else if (surfaceTag.equals("wood")) else if (surfaceTag.equals("earth")) surface = DIRT; - surfaceEnc.setEnum(false, edgeFlags, surface); - return edgeFlags; + surfaceEnc.setEnum(false, edgeId, edgeIntAccess, surface); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTollParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTollParser.java index d784af6e107..20782dcef7c 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTollParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTollParser.java @@ -19,8 +19,8 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.Toll; -import com.graphhopper.routing.util.TransportationMode; import com.graphhopper.routing.util.countryrules.CountryRule; import com.graphhopper.storage.IntsRef; @@ -38,7 +38,7 @@ public OSMTollParser(EnumEncodedValue tollEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { Toll toll; if (readerWay.hasTag("toll", "yes")) { toll = Toll.ALL; @@ -52,10 +52,8 @@ public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef rel CountryRule countryRule = readerWay.getTag("country_rule", null); if (countryRule != null) - toll = countryRule.getToll(readerWay, TransportationMode.CAR, toll); - - tollEnc.setEnum(false, edgeFlags, toll); - - return edgeFlags; + toll = countryRule.getToll(readerWay, toll); + + tollEnc.setEnum(false, edgeId, edgeIntAccess, toll); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTrackTypeParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTrackTypeParser.java index 3929118e9a3..b0581898448 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTrackTypeParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTrackTypeParser.java @@ -19,6 +19,7 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.TrackType; import com.graphhopper.storage.IntsRef; @@ -33,12 +34,11 @@ public OSMTrackTypeParser(EnumEncodedValue trackTypeEnc) { } @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { String trackTypeTag = readerWay.getTag("tracktype"); TrackType trackType = TrackType.find(trackTypeTag); if (trackType != MISSING) - trackTypeEnc.setEnum(false, edgeFlags, trackType); - return edgeFlags; + trackTypeEnc.setEnum(false, edgeId, edgeIntAccess, trackType); } } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTurnRelationParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTurnRelationParser.java deleted file mode 100644 index 9ad3418d6a9..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMTurnRelationParser.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.OSMTurnRelation; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.util.AccessFilter; -import com.graphhopper.storage.Graph; -import com.graphhopper.storage.TurnCostStorage; -import com.graphhopper.util.EdgeExplorer; -import com.graphhopper.util.EdgeIterator; - -import java.util.List; - -/** - * This parser takes the turn restrictions from OSM (OSM does not provide turn costs, but only restrictions) and creates the appropriate turn costs (with value infinity) - */ -public class OSMTurnRelationParser implements TurnCostParser { - private final BooleanEncodedValue accessEnc; - private final DecimalEncodedValue turnCostEnc; - private final List restrictions; - private EdgeExplorer cachedOutExplorer, cachedInExplorer; - - public OSMTurnRelationParser(BooleanEncodedValue accessEnc, DecimalEncodedValue turnCostEnc, List restrictions) { - if (restrictions.isEmpty()) - throw new IllegalArgumentException("restrictions cannot be empty"); - this.accessEnc = accessEnc; - this.turnCostEnc = turnCostEnc; - this.restrictions = restrictions; - } - - @Override - public void createTurnCostEncodedValues(EncodedValueLookup lookup, List registerNewEncodedValue) { - registerNewEncodedValue.add(turnCostEnc); - } - - @Override - public void handleTurnRelationTags(OSMTurnRelation turnRelation, ExternalInternalMap map, Graph graph) { - if (!turnRelation.isVehicleTypeConcernedByTurnRestriction(restrictions)) - return; - - addRelationToTCStorage(turnRelation, map, graph); - } - - private EdgeExplorer getInExplorer(Graph graph) { - return cachedInExplorer == null ? cachedInExplorer = graph.createEdgeExplorer(AccessFilter.inEdges(accessEnc)) : cachedInExplorer; - } - - private EdgeExplorer getOutExplorer(Graph graph) { - return cachedOutExplorer == null ? cachedOutExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(accessEnc)) : cachedOutExplorer; - } - - /** - * Add the specified relation to the TurnCostStorage - */ - void addRelationToTCStorage(OSMTurnRelation osmTurnRelation, - ExternalInternalMap map, Graph graph) { - TurnCostStorage tcs = graph.getTurnCostStorage(); - int viaNode = map.getInternalNodeIdOfOsmNode(osmTurnRelation.getViaOsmNodeId()); - EdgeExplorer edgeOutExplorer = getOutExplorer(graph), edgeInExplorer = getInExplorer(graph); - - try { - int edgeIdFrom = EdgeIterator.NO_EDGE; - - // get all incoming edges and receive the edge which is defined by fromOsm - EdgeIterator iter = edgeInExplorer.setBaseNode(viaNode); - - while (iter.next()) { - if (map.getOsmIdOfInternalEdge(iter.getEdge()) == osmTurnRelation.getOsmIdFrom()) { - edgeIdFrom = iter.getEdge(); - break; - } - } - - if (!EdgeIterator.Edge.isValid(edgeIdFrom)) - return; - - // get all outgoing edges of the via node - iter = edgeOutExplorer.setBaseNode(viaNode); - // for TYPE_ONLY_* we add ALL restrictions (from, via, * ) EXCEPT the given turn - // for TYPE_NOT_* we add ONE restriction (from, via, to) - while (iter.next()) { - int edgeId = iter.getEdge(); - long wayId = map.getOsmIdOfInternalEdge(edgeId); - if (edgeId != edgeIdFrom && osmTurnRelation.getRestriction() == OSMTurnRelation.Type.ONLY && wayId != osmTurnRelation.getOsmIdTo() - || osmTurnRelation.getRestriction() == OSMTurnRelation.Type.NOT && wayId == osmTurnRelation.getOsmIdTo() && wayId >= 0) { - tcs.set(turnCostEnc, edgeIdFrom, viaNode, iter.getEdge(), Double.POSITIVE_INFINITY); - if (osmTurnRelation.getRestriction() == OSMTurnRelation.Type.NOT) - break; - } - } - } catch (Exception e) { - throw new IllegalStateException("Could not built turn table entry for relation of node with osmId:" + osmTurnRelation.getViaOsmNodeId(), e); - } - } - - @Override - public String getName() { - return turnCostEnc.getName(); - } - - @Override - public String toString() { - return getName(); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/OSMWayIDParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMWayIDParser.java new file mode 100644 index 00000000000..2dd7ea1b5f4 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/OSMWayIDParser.java @@ -0,0 +1,42 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.IntEncodedValue; +import com.graphhopper.storage.IntsRef; + +public class OSMWayIDParser implements TagParser { + private final IntEncodedValue osmWayIdEnc; + + public OSMWayIDParser(IntEncodedValue osmWayIdEnc) { + this.osmWayIdEnc = osmWayIdEnc; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + if (way.getId() > osmWayIdEnc.getMaxStorableInt()) + throw new IllegalArgumentException("Cannot store OSM way ID: " + way.getId() + " as it is too large (> " + + osmWayIdEnc.getMaxStorableInt() + "). You can disable " + osmWayIdEnc.getName() + " if you do not " + + "need to store the OSM way IDs"); + int wayId = Math.toIntExact(way.getId()); + osmWayIdEnc.setInt(false, edgeId, edgeIntAccess, wayId); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAccessParser.java new file mode 100644 index 00000000000..5843c167e9d --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAccessParser.java @@ -0,0 +1,25 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.Roundabout; +import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.util.PMap; + +public class RacingBikeAccessParser extends BikeCommonAccessParser { + + public RacingBikeAccessParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "racingbike"))), + lookup.getBooleanEncodedValue(Roundabout.KEY)); + blockPrivate(properties.getBool("block_private", true)); + blockFords(properties.getBool("block_fords", false)); + } + + protected RacingBikeAccessParser(BooleanEncodedValue accessEnc, BooleanEncodedValue roundaboutEnc) { + super(accessEnc, roundaboutEnc); + + barriers.add("kissing_gate"); + barriers.add("stile"); + barriers.add("turnstile"); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAverageSpeedParser.java new file mode 100644 index 00000000000..379ea764db6 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikeAverageSpeedParser.java @@ -0,0 +1,65 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; + +public class RacingBikeAverageSpeedParser extends BikeCommonAverageSpeedParser { + + public RacingBikeAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "racingbike"))), + lookup.getEnumEncodedValue(Smoothness.KEY, Smoothness.class)); + } + + protected RacingBikeAverageSpeedParser(DecimalEncodedValue speedEnc, EnumEncodedValue smoothnessEnc) { + super(speedEnc, smoothnessEnc); + + setTrackTypeSpeed("grade1", 20); // paved + setTrackTypeSpeed("grade2", 10); // now unpaved ... + setTrackTypeSpeed("grade3", PUSHING_SECTION_SPEED); + setTrackTypeSpeed("grade4", PUSHING_SECTION_SPEED); + setTrackTypeSpeed("grade5", PUSHING_SECTION_SPEED); + + setSurfaceSpeed("paved", 20); + setSurfaceSpeed("asphalt", 20); + setSurfaceSpeed("concrete", 20); + setSurfaceSpeed("concrete:lanes", 16); + setSurfaceSpeed("concrete:plates", 16); + setSurfaceSpeed("unpaved", MIN_SPEED); + setSurfaceSpeed("compacted", MIN_SPEED); + setSurfaceSpeed("dirt", MIN_SPEED); + setSurfaceSpeed("earth", MIN_SPEED); + setSurfaceSpeed("fine_gravel", PUSHING_SECTION_SPEED); + setSurfaceSpeed("grass", MIN_SPEED); + setSurfaceSpeed("grass_paver", MIN_SPEED); + setSurfaceSpeed("gravel", MIN_SPEED); + setSurfaceSpeed("ground", MIN_SPEED); + setSurfaceSpeed("ice", MIN_SPEED); + setSurfaceSpeed("metal", MIN_SPEED); + setSurfaceSpeed("mud", MIN_SPEED); + setSurfaceSpeed("pebblestone", PUSHING_SECTION_SPEED); + setSurfaceSpeed("salt", MIN_SPEED); + setSurfaceSpeed("sand", MIN_SPEED); + setSurfaceSpeed("wood", MIN_SPEED); + + setHighwaySpeed("path", 8); + setHighwaySpeed("footway", PUSHING_SECTION_SPEED); + setHighwaySpeed("track", MIN_SPEED); // assume unpaved + + setHighwaySpeed("trunk", 20); + setHighwaySpeed("trunk_link", 20); + setHighwaySpeed("primary", 20); + setHighwaySpeed("primary_link", 20); + setHighwaySpeed("secondary", 20); + setHighwaySpeed("secondary_link", 20); + setHighwaySpeed("tertiary", 20); + setHighwaySpeed("tertiary_link", 20); + + addPushingSection("path"); + + // overwite map from BikeCommon + setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.EXCELLENT, 1.2d); + setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.VERY_BAD, 0.1); + setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.HORRIBLE, 0.1); + setSmoothnessSpeedFactor(com.graphhopper.routing.ev.Smoothness.VERY_HORRIBLE, 0.1); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikePriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikePriorityParser.java new file mode 100644 index 00000000000..fc630f3bed8 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RacingBikePriorityParser.java @@ -0,0 +1,58 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; + +import java.util.TreeMap; + +import static com.graphhopper.routing.ev.RouteNetwork.*; +import static com.graphhopper.routing.util.PriorityCode.*; + +public class RacingBikePriorityParser extends BikeCommonPriorityParser { + + public RacingBikePriorityParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehiclePriority.key(properties.getString("name", "racingbike"))), + lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "racingbike"))), + lookup.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class)); + } + + protected RacingBikePriorityParser(DecimalEncodedValue priorityEnc, DecimalEncodedValue speedEnc, + EnumEncodedValue bikeRouteEnc) { + super(priorityEnc, speedEnc, bikeRouteEnc); + + addPushingSection("path"); + + preferHighwayTags.add("road"); + preferHighwayTags.add("secondary"); + preferHighwayTags.add("secondary_link"); + preferHighwayTags.add("tertiary"); + preferHighwayTags.add("tertiary_link"); + preferHighwayTags.add("residential"); + + routeMap.put(INTERNATIONAL, BEST.getValue()); + routeMap.put(NATIONAL, BEST.getValue()); + routeMap.put(REGIONAL, VERY_NICE.getValue()); + routeMap.put(LOCAL, UNCHANGED.getValue()); + + setSpecificClassBicycle("roadcycling"); + + avoidSpeedLimit = 81; + } + + @Override + void collect(ReaderWay way, double wayTypeSpeed, TreeMap weightToPrioMap) { + super.collect(way, wayTypeSpeed, weightToPrioMap); + + String highway = way.getTag("highway"); + if ("service".equals(highway) || "residential".equals(highway)) { + weightToPrioMap.put(40d, SLIGHT_AVOID.getValue()); + } else if ("track".equals(highway)) { + String trackType = way.getTag("tracktype"); + if ("grade1".equals(trackType)) + weightToPrioMap.put(110d, PREFER.getValue()); + else if (trackType == null || trackType.startsWith("grade")) + weightToPrioMap.put(110d, AVOID_MORE.getValue()); + } + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RelationTagParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RelationTagParser.java index 592437ac653..dad261cd4a8 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/RelationTagParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RelationTagParser.java @@ -31,5 +31,5 @@ public interface RelationTagParser extends TagParser { * Analyze the tags of a relation and create the routing flags for the second read step. * In the pre-parsing step this method will be called to determine the useful relation tags. */ - IntsRef handleRelationTags(IntsRef relFlags, ReaderRelation relation); + void handleRelationTags(IntsRef relFlags, ReaderRelation relation); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java new file mode 100644 index 00000000000..16f22e26257 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RestrictionSetter.java @@ -0,0 +1,200 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.parsers; + +import com.carrotsearch.hppc.*; +import com.carrotsearch.hppc.cursors.IntCursor; +import com.graphhopper.reader.osm.GraphRestriction; +import com.graphhopper.reader.osm.Pair; +import com.graphhopper.reader.osm.RestrictionType; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.EdgeExplorer; +import com.graphhopper.util.EdgeIterator; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.FetchMode; + +import java.util.List; + +import static com.graphhopper.reader.osm.RestrictionType.NO; +import static com.graphhopper.reader.osm.RestrictionType.ONLY; + +public class RestrictionSetter { + private final BaseGraph baseGraph; + private final EdgeExplorer edgeExplorer; + private final IntIntMap artificialEdgesByEdges = new IntIntHashMap(); + + public RestrictionSetter(BaseGraph baseGraph) { + this.baseGraph = baseGraph; + this.edgeExplorer = baseGraph.createEdgeExplorer(); + } + + /** + * Adds all the turn restriction entries to the graph that are needed to enforce the given restrictions, for + * a single turn cost encoded value. + * Implementing via-way turn restrictions requires adding artificial edges to the graph, which is also handled here. + * Since we keep track of the added artificial edges here it is important to only use one RestrictionSetter instance + * for **all** turn restrictions and vehicle types. + */ + public void setRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + // we first need to add all the artificial edges, because we might need to restrict turns between artificial + // edges created for different restrictions (when restrictions are overlapping) + addArtificialEdges(restrictions); + // now we can add all the via-way restrictions + addViaWayRestrictions(restrictions, turnCostEnc); + // ... and finally all the via-node restrictions + addViaNodeRestrictions(restrictions, turnCostEnc); + } + + private void addArtificialEdges(List> restrictions) { + for (Pair p : restrictions) { + if (p.first.isViaWayRestriction()) { + if (ignoreViaWayRestriction(p)) continue; + int viaEdge = p.first.getViaEdges().get(0); + int artificialEdge = artificialEdgesByEdges.getOrDefault(viaEdge, -1); + if (artificialEdge < 0) { + EdgeIteratorState viaEdgeState = baseGraph.getEdgeIteratorState(p.first.getViaEdges().get(0), Integer.MIN_VALUE); + EdgeIteratorState artificialEdgeState = baseGraph.edge(viaEdgeState.getBaseNode(), viaEdgeState.getAdjNode()) + .setFlags(viaEdgeState.getFlags()) + .setWayGeometry(viaEdgeState.fetchWayGeometry(FetchMode.PILLAR_ONLY)) + .setDistance(viaEdgeState.getDistance()) + .setKeyValues(viaEdgeState.getKeyValues()); + artificialEdge = artificialEdgeState.getEdge(); + artificialEdgesByEdges.put(viaEdge, artificialEdge); + } + } + } + } + + private void addViaWayRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + IntSet viaEdgesUsedByOnlyRestrictions = new IntHashSet(); + for (Pair p : restrictions) { + if (!p.first.isViaWayRestriction()) continue; + if (ignoreViaWayRestriction(p)) continue; + final int fromEdge = p.first.getFromEdges().get(0); + final int viaEdge = p.first.getViaEdges().get(0); + final int toEdge = p.first.getToEdges().get(0); + final int artificialVia = artificialEdgesByEdges.getOrDefault(viaEdge, viaEdge); + if (artificialVia == viaEdge) + throw new IllegalArgumentException("There should be an artificial edge for every via edge of a way restriction"); + if (p.second == ONLY && !viaEdgesUsedByOnlyRestrictions.add(viaEdge)) + throw new IllegalStateException("We cannot deal with 'only' via-way restrictions that use the same via edges"); + final int artificialFrom = artificialEdgesByEdges.getOrDefault(fromEdge, fromEdge); + final int artificialTo = artificialEdgesByEdges.getOrDefault(toEdge, toEdge); + final int fromToViaNode = p.first.getViaNodes().get(0); + final int viaToToNode = p.first.getViaNodes().get(1); + + // never turn between an artificial edge and its corresponding real edge + restrictTurn(turnCostEnc, artificialVia, fromToViaNode, viaEdge); + restrictTurn(turnCostEnc, viaEdge, fromToViaNode, artificialVia); + restrictTurn(turnCostEnc, artificialVia, viaToToNode, viaEdge); + restrictTurn(turnCostEnc, viaEdge, viaToToNode, artificialVia); + + if (p.second == NO) { + // This is how we implement via-way NO restrictions: we deny turning from the from-edge onto the via-edge, + // but allow turning onto the artificial edge instead. Then we deny turning from the artificial edge onto + // the to edge. + restrictTurn(turnCostEnc, fromEdge, fromToViaNode, viaEdge); + restrictTurn(turnCostEnc, artificialVia, viaToToNode, toEdge); + } else if (p.second == ONLY) { + // For via-way ONLY restrictions we have to turn from the from-edge onto the via-edge and from the via-edge + // onto the to-edge, but only if we actually start at the from-edge. Therefore we enforce turning onto + // the artificial via-edge when we are coming from the from-edge and only allow turning onto the to-edge + // when coming from the artificial via-edge. + EdgeIterator iter = edgeExplorer.setBaseNode(fromToViaNode); + while (iter.next()) + if (iter.getEdge() != fromEdge && iter.getEdge() != artificialVia) + restrictTurn(turnCostEnc, fromEdge, fromToViaNode, iter.getEdge()); + iter = edgeExplorer.setBaseNode(viaToToNode); + while (iter.next()) + if (iter.getEdge() != artificialVia && iter.getEdge() != toEdge) + restrictTurn(turnCostEnc, artificialVia, viaToToNode, iter.getEdge()); + } else { + throw new IllegalArgumentException("Unexpected restriction type: " + p.second); + } + + // this is important for overlapping restrictions + if (artificialFrom != fromEdge) + restrictTurn(turnCostEnc, artificialFrom, fromToViaNode, artificialVia); + if (artificialTo != toEdge) + restrictTurn(turnCostEnc, artificialVia, viaToToNode, artificialTo); + } + } + + private void addViaNodeRestrictions(List> restrictions, DecimalEncodedValue turnCostEnc) { + for (Pair p : restrictions) { + if (p.first.isViaWayRestriction()) continue; + final int viaNode = p.first.getViaNodes().get(0); + for (IntCursor fromEdgeCursor : p.first.getFromEdges()) { + for (IntCursor toEdgeCursor : p.first.getToEdges()) { + final int fromEdge = fromEdgeCursor.value; + final int toEdge = toEdgeCursor.value; + final int artificialFrom = artificialEdgesByEdges.getOrDefault(fromEdge, fromEdge); + final int artificialTo = artificialEdgesByEdges.getOrDefault(toEdge, toEdge); + if (p.second == NO) { + restrictTurn(turnCostEnc, fromEdge, viaNode, toEdge); + // we also need to restrict this term in case there are artificial edges for the from- and/or to-edge + if (artificialFrom != fromEdge) + restrictTurn(turnCostEnc, artificialFrom, viaNode, toEdge); + if (artificialTo != toEdge) + restrictTurn(turnCostEnc, fromEdge, viaNode, artificialTo); + if (artificialFrom != fromEdge && artificialTo != toEdge) + restrictTurn(turnCostEnc, artificialFrom, viaNode, artificialTo); + } else if (p.second == ONLY) { + // we need to restrict all turns except the one, but that also means not restricting the + // artificial counterparts of these turns, if they exist. + // we do not explicitly restrict the U-turn from the from-edge back to the from-edge though. + EdgeIterator iter = edgeExplorer.setBaseNode(viaNode); + while (iter.next()) { + if (iter.getEdge() != fromEdge && iter.getEdge() != toEdge && iter.getEdge() != artificialTo) + restrictTurn(turnCostEnc, fromEdge, viaNode, iter.getEdge()); + // and the same for the artificial edge belonging to the from-edge if it exists + if (fromEdge != artificialFrom && iter.getEdge() != artificialFrom && iter.getEdge() != toEdge && iter.getEdge() != artificialTo) + restrictTurn(turnCostEnc, artificialFrom, viaNode, iter.getEdge()); + } + } else { + throw new IllegalArgumentException("Unexpected restriction type: " + p.second); + } + } + } + } + } + + public IntIntMap getArtificialEdgesByEdges() { + return artificialEdgesByEdges; + } + + private void restrictTurn(DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode, int toEdge) { + if (fromEdge < 0 || toEdge < 0 || viaNode < 0) + throw new IllegalArgumentException("from/toEdge and viaNode must be >= 0"); + baseGraph.getTurnCostStorage().set(turnCostEnc, fromEdge, viaNode, toEdge, Double.POSITIVE_INFINITY); + } + + private static boolean ignoreViaWayRestriction(Pair p) { + // todo: how frequent are these? + if (p.first.getViaEdges().size() > 1) + // no multi-restrictions yet + return true; + if (p.first.getFromEdges().size() > 1 || p.first.getToEdges().size() > 1) + // no multi-from or -to yet + return true; + return false; + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAccessParser.java new file mode 100644 index 00000000000..17be2eef811 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAccessParser.java @@ -0,0 +1,32 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.PMap; + +public class RoadsAccessParser implements TagParser { + private final BooleanEncodedValue accessEnc; + + public RoadsAccessParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getBooleanEncodedValue(VehicleAccess.key(properties.getString("name", "roads")))); + } + + public RoadsAccessParser(BooleanEncodedValue accessEnc) { + this.accessEnc = accessEnc; + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + } + + @Override + public String toString() { + return accessEnc.getName(); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAverageSpeedParser.java new file mode 100644 index 00000000000..2486b0e52be --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/RoadsAverageSpeedParser.java @@ -0,0 +1,32 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.PMap; + +public class RoadsAverageSpeedParser implements TagParser { + public static final double ROADS_MAX_SPEED = 254; + private final DecimalEncodedValue avgSpeedEnc; + private final double maxPossibleSpeed; + + public RoadsAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(VehicleSpeed.key(properties.getString("name", "roads")))); + } + + public RoadsAverageSpeedParser(DecimalEncodedValue avgSpeedEnc) { + this.avgSpeedEnc = avgSpeedEnc; + this.maxPossibleSpeed = this.avgSpeedEnc.getNextStorableValue(ROADS_MAX_SPEED); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + // let's make it high and let it be reduced in the custom model + avgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, maxPossibleSpeed); + if (avgSpeedEnc.isStoreTwoDirections()) + avgSpeedEnc.setDecimal(true, edgeId, edgeIntAccess, maxPossibleSpeed); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/TagParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/TagParser.java index 14319babf9f..56ae88d9811 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/TagParser.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/TagParser.java @@ -18,6 +18,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.storage.IntsRef; /** @@ -26,5 +27,5 @@ */ public interface TagParser { - IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, IntsRef relationFlags); + void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/TagParserFactory.java b/core/src/main/java/com/graphhopper/routing/util/parsers/TagParserFactory.java index 444177a83b6..2a0a676cd3c 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/TagParserFactory.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/TagParserFactory.java @@ -1,7 +1,8 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.util.PMap; public interface TagParserFactory { - TagParser create(EncodedValueLookup lookup, String name); + TagParser create(EncodedValueLookup lookup, String name, PMap properties); } diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/TurnCostParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/TurnCostParser.java deleted file mode 100644 index cd9e4035f1b..00000000000 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/TurnCostParser.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.OSMTurnRelation; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.storage.Graph; - -import java.util.List; - -/** - * This interface serves the purpose of converting relation flags into turn cost information. Unlike RelationTagParser - * it can be assumed that the graph topology is already intact when handleTurnRelationTags is called. - */ -public interface TurnCostParser { - String getName(); - - void createTurnCostEncodedValues(EncodedValueLookup lookup, List registerNewEncodedValue); - - void handleTurnRelationTags(OSMTurnRelation turnRelation, ExternalInternalMap map, Graph graph); - - /** - * This map associates the internal GraphHopper nodes IDs with external IDs (OSM) and similarly for the edge IDs - * required to write the turn costs. Returns -1 if there is no entry for the given OSM ID. - */ - interface ExternalInternalMap { - int getInternalNodeIdOfOsmNode(long nodeOsmId); - - long getOsmIdOfInternalEdge(int edgeId); - } -} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java new file mode 100644 index 00000000000..83fa8e039d3 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAccessParser.java @@ -0,0 +1,143 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.routing.util.WayAccess; +import com.graphhopper.util.PMap; + +import java.util.HashSet; +import java.util.Set; + +public class WheelchairAccessParser extends FootAccessParser { + private final Set excludeSurfaces = new HashSet<>(); + private final Set excludeSmoothness = new HashSet<>(); + private final int maxInclinePercent = 6; + + public WheelchairAccessParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getBooleanEncodedValue(properties.getString("name", VehicleAccess.key("wheelchair")))); + blockPrivate(properties.getBool("block_private", true)); + blockFords(properties.getBool("block_fords", false)); + } + + protected WheelchairAccessParser(BooleanEncodedValue accessEnc) { + super(accessEnc); + + restrictions.add("wheelchair"); + + barriers.add("handrail"); + barriers.add("wall"); + barriers.add("turnstile"); + barriers.add("kissing_gate"); + barriers.add("stile"); + + safeHighwayTags.add("footway"); + safeHighwayTags.add("pedestrian"); + safeHighwayTags.add("living_street"); + safeHighwayTags.add("residential"); + safeHighwayTags.add("service"); + safeHighwayTags.add("platform"); + + safeHighwayTags.remove("steps"); + safeHighwayTags.remove("track"); + + allowedHighwayTags.clear(); + allowedHighwayTags.addAll(safeHighwayTags); + allowedHighwayTags.addAll(avoidHighwayTags); + allowedHighwayTags.add("cycleway"); + allowedHighwayTags.add("unclassified"); + allowedHighwayTags.add("road"); + + excludeSurfaces.add("cobblestone"); + excludeSurfaces.add("gravel"); + excludeSurfaces.add("sand"); + + excludeSmoothness.add("bad"); + excludeSmoothness.add("very_bad"); + excludeSmoothness.add("horrible"); + excludeSmoothness.add("very_horrible"); + excludeSmoothness.add("impassable"); + + allowedSacScale.clear(); + } + + /** + * Avoid some more ways than for pedestrian like hiking trails. + */ + @Override + public WayAccess getAccess(ReaderWay way) { + if (way.hasTag("surface", excludeSurfaces)) { + if (!way.hasTag("sidewalk", sidewalkValues)) { + return WayAccess.CAN_SKIP; + } else { + String sidewalk = way.getTag("sidewalk"); + if (way.hasTag("sidewalk:" + sidewalk + ":surface", excludeSurfaces)) { + return WayAccess.CAN_SKIP; + } + } + } + + if (way.hasTag("smoothness", excludeSmoothness)) { + if (!way.hasTag("sidewalk", sidewalkValues)) { + return WayAccess.CAN_SKIP; + } else { + String sidewalk = way.getTag("sidewalk"); + if (way.hasTag("sidewalk:" + sidewalk + ":smoothness", excludeSmoothness)) { + return WayAccess.CAN_SKIP; + } + } + } + + if (way.hasTag("incline")) { + String tagValue = way.getTag("incline"); + if (tagValue.endsWith("%") || tagValue.endsWith("°")) { + try { + double incline = Double.parseDouble(tagValue.substring(0, tagValue.length() - 1)); + if (tagValue.endsWith("°")) { + incline = Math.tan(incline * Math.PI / 180) * 100; + } + + if (-maxInclinePercent > incline || incline > maxInclinePercent) { + return WayAccess.CAN_SKIP; + } + } catch (NumberFormatException ex) { + } + } + } + + if (way.hasTag("kerb", "raised")) + return WayAccess.CAN_SKIP; + + if (way.hasTag("kerb")) { + String tagValue = way.getTag("kerb"); + if (tagValue.endsWith("cm") || tagValue.endsWith("mm")) { + try { + float kerbHeight = Float.parseFloat(tagValue.substring(0, tagValue.length() - 2)); + if (tagValue.endsWith("mm")) { + kerbHeight /= 100; + } + + int maxKerbHeightCm = 3; + if (kerbHeight > maxKerbHeightCm) { + return WayAccess.CAN_SKIP; + } + } catch (NumberFormatException ex) { + } + } + } + + return super.getAccess(way); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + WayAccess access = getAccess(way); + if (access.canSkip()) + return; + + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAverageSpeedParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAverageSpeedParser.java new file mode 100644 index 00000000000..456ad593b15 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairAverageSpeedParser.java @@ -0,0 +1,101 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.util.PMap; +import com.graphhopper.util.PointList; + +public class WheelchairAverageSpeedParser extends FootAverageSpeedParser { + private final int maxInclinePercent = 6; + + public WheelchairAverageSpeedParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(properties.getString("name", VehicleSpeed.key("wheelchair")))); + } + + protected WheelchairAverageSpeedParser(DecimalEncodedValue speedEnc) { + super(speedEnc); + + safeHighwayTags.add("footway"); + safeHighwayTags.add("pedestrian"); + safeHighwayTags.add("living_street"); + safeHighwayTags.add("residential"); + safeHighwayTags.add("service"); + safeHighwayTags.add("platform"); + + safeHighwayTags.remove("steps"); + safeHighwayTags.remove("track"); + + allowedHighwayTags.clear(); + allowedHighwayTags.addAll(safeHighwayTags); + allowedHighwayTags.addAll(avoidHighwayTags); + allowedHighwayTags.add("cycleway"); + allowedHighwayTags.add("unclassified"); + allowedHighwayTags.add("road"); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way) { + String highwayValue = way.getTag("highway"); + if (highwayValue == null) { + if (way.hasTag("route", ferries)) { + double ferrySpeed = ferrySpeedCalc.getSpeed(way); + setSpeed(edgeId, edgeIntAccess, true, true, ferrySpeed); + } + if (!way.hasTag("railway", "platform") && !way.hasTag("man_made", "pier")) + return; + } + + setSpeed(edgeId, edgeIntAccess, true, true, MEAN_SPEED); + applyWayTags(way, edgeId, edgeIntAccess); + } + + /** + * Calculate slopes from elevation data and set speed according to that. In-/declines between smallInclinePercent + * and maxInclinePercent will reduce speed to SLOW_SPEED. In-/declines above maxInclinePercent will result in zero + * speed. + */ + public void applyWayTags(ReaderWay way, int edgeId, EdgeIntAccess edgeIntAccess) { + PointList pl = way.getTag("point_list", null); + if (pl == null) + throw new IllegalArgumentException("The artificial point_list tag is missing"); + if (!way.hasTag("edge_distance")) + throw new IllegalArgumentException("The artificial edge_distance tag is missing"); + double fullDist2D = way.getTag("edge_distance", 0d); + if (Double.isInfinite(fullDist2D)) + throw new IllegalStateException("Infinite distance should not happen due to #435. way ID=" + way.getId()); + + // skip elevation data adjustment for too short segments, TODO use custom model for elevation handling + if (fullDist2D < 20 || !pl.is3D()) + return; + + double prevEle = pl.getEle(0); + double eleDelta = pl.getEle(pl.size() - 1) - prevEle; + double elePercent = eleDelta / fullDist2D * 100; + int smallInclinePercent = 3; + double fwdSpeed = 0, bwdSpeed = 0; + if (elePercent > smallInclinePercent && elePercent < maxInclinePercent) { + fwdSpeed = SLOW_SPEED; + bwdSpeed = MEAN_SPEED; + } else if (elePercent < -smallInclinePercent && elePercent > -maxInclinePercent) { + fwdSpeed = MEAN_SPEED; + bwdSpeed = SLOW_SPEED; + } else if (elePercent > maxInclinePercent || elePercent < -maxInclinePercent) { + // it can be problematic to exclude roads due to potential bad elevation data (e.g.delta for narrow nodes could be too high) + // so exclude only when we are certain + if (fullDist2D > 50) { + setSpeed(edgeId, edgeIntAccess, true, false, 0); + setSpeed(edgeId, edgeIntAccess, true, true, 0); + return; + } + + fwdSpeed = SLOW_SPEED; + bwdSpeed = SLOW_SPEED; + } + + if (fwdSpeed > 0) setSpeed(edgeId, edgeIntAccess, true, false, fwdSpeed); + if (bwdSpeed > 0) setSpeed(edgeId, edgeIntAccess, false, true, bwdSpeed); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairPriorityParser.java b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairPriorityParser.java new file mode 100644 index 00000000000..a632b91fc2d --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/WheelchairPriorityParser.java @@ -0,0 +1,61 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.PMap; + +import java.util.TreeMap; + +import static com.graphhopper.routing.util.PriorityCode.AVOID; +import static com.graphhopper.routing.util.PriorityCode.VERY_NICE; + +public class WheelchairPriorityParser extends FootPriorityParser { + + public WheelchairPriorityParser(EncodedValueLookup lookup, PMap properties) { + this(lookup.getDecimalEncodedValue(properties.getString("name", VehiclePriority.key("wheelchair"))), + lookup.getEnumEncodedValue(FootNetwork.KEY, RouteNetwork.class)); + } + + protected WheelchairPriorityParser(DecimalEncodedValue priorityEnc, EnumEncodedValue footRouteEnc) { + super(priorityEnc, footRouteEnc); + + safeHighwayTags.add("footway"); + safeHighwayTags.add("pedestrian"); + safeHighwayTags.add("living_street"); + safeHighwayTags.add("residential"); + safeHighwayTags.add("service"); + safeHighwayTags.add("platform"); + + safeHighwayTags.remove("steps"); + safeHighwayTags.remove("track"); + } + + @Override + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) { + Integer priorityFromRelation = routeMap.get(footRouteEnc.getEnum(false, edgeId, edgeIntAccess)); + priorityWayEncoder.setDecimal(false, edgeId, edgeIntAccess, PriorityCode.getValue(handlePriority(way, priorityFromRelation))); + } + + /** + * First get priority from {@link FootPriorityParser#handlePriority(ReaderWay, Integer)} then evaluate wheelchair specific + * tags. + * + * @return a priority for the given way + */ + @Override + public int handlePriority(ReaderWay way, Integer priorityFromRelation) { + TreeMap weightToPrioMap = new TreeMap<>(); + + weightToPrioMap.put(100d, super.handlePriority(way, priorityFromRelation)); + + if (way.hasTag("wheelchair", "designated")) { + weightToPrioMap.put(102d, VERY_NICE.getValue()); + } else if (way.hasTag("wheelchair", "limited")) { + weightToPrioMap.put(102d, AVOID.getValue()); + } + + return weightToPrioMap.lastEntry().getValue(); + } +} diff --git a/core/src/main/java/com/graphhopper/routing/util/parsers/helpers/OSMValueExtractor.java b/core/src/main/java/com/graphhopper/routing/util/parsers/helpers/OSMValueExtractor.java index e6951676ca4..be0e2da0ad0 100644 --- a/core/src/main/java/com/graphhopper/routing/util/parsers/helpers/OSMValueExtractor.java +++ b/core/src/main/java/com/graphhopper/routing/util/parsers/helpers/OSMValueExtractor.java @@ -1,42 +1,44 @@ package com.graphhopper.routing.util.parsers.helpers; -import static com.graphhopper.util.Helper.toLowerCase; - -import java.util.List; -import java.util.regex.Pattern; - import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.MaxSpeed; -import com.graphhopper.storage.IntsRef; import com.graphhopper.util.DistanceCalcEarth; import com.graphhopper.util.Helper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.regex.Pattern; + +import static com.graphhopper.util.Helper.toLowerCase; public class OSMValueExtractor { - - private static final Pattern TON_PATTERN = Pattern.compile("tons?"); - private static final Pattern MGW_PATTERN = Pattern.compile("mgw"); + + private static final Pattern TON_PATTERN = Pattern.compile("tons?"); + private static final Pattern MGW_PATTERN = Pattern.compile("mgw"); private static final Pattern WSPACE_PATTERN = Pattern.compile("\\s"); - private static final Pattern METER_PATTERN = Pattern.compile("meters?|mtrs?|mt|m\\."); - private static final Pattern INCH_PATTERN = Pattern.compile("\"|\'\'"); - private static final Pattern FEET_PATTERN = Pattern.compile("\'|feet"); + private static final Pattern METER_PATTERN = Pattern.compile("meters?|mtrs?|mt|m\\."); + private static final Pattern INCH_PATTERN = Pattern.compile("\"|\'\'"); + private static final Pattern FEET_PATTERN = Pattern.compile("\'|feet"); private static final Pattern APPROX_PATTERN = Pattern.compile("~|approx"); + private static final Logger logger = LoggerFactory.getLogger(OSMValueExtractor.class); private OSMValueExtractor() { // utility class } - public static void extractTons(IntsRef edgeFlags, ReaderWay way, DecimalEncodedValue valueEncoder, List keys) { + public static void extractTons(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, DecimalEncodedValue valueEncoder, List keys) { final String rawValue = way.getFirstPriorityTag(keys); double value = stringToTons(rawValue); - - if (Double.isNaN(value)) { - return; - } - - if (value > valueEncoder.getMaxDecimal()) - value = valueEncoder.getMaxDecimal(); - valueEncoder.setDecimal(false, edgeFlags, value); + + if (Double.isNaN(value)) value = Double.POSITIVE_INFINITY; + + valueEncoder.setDecimal(false, edgeId, edgeIntAccess, value); + // too many +// if (value - valueEncoder.getDecimal(false, edgeFlags) > 2) +// logger.warn("Value " + value + " for " + valueEncoder.getName() + " was too large and truncated to " + valueEncoder.getDecimal(false, edgeFlags)); } public static double stringToTons(String value) { @@ -66,17 +68,16 @@ public static double stringToTons(String value) { } } - public static void extractMeter(IntsRef edgeFlags, ReaderWay way, DecimalEncodedValue valueEncoder, List keys) { + public static void extractMeter(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, DecimalEncodedValue valueEncoder, List keys) { final String rawValue = way.getFirstPriorityTag(keys); double value = stringToMeter(rawValue); - - if (Double.isNaN(value)) { - return; - } - if (value > valueEncoder.getMaxDecimal()) - value = valueEncoder.getMaxDecimal(); - valueEncoder.setDecimal(false, edgeFlags, value); + if (Double.isNaN(value)) value = Double.POSITIVE_INFINITY; + + valueEncoder.setDecimal(false, edgeId, edgeIntAccess, value); + // too many +// if (value - valueEncoder.getDecimal(false, edgeFlags) > 2) +// logger.warn("Value " + value + " for " + valueEncoder.getName() + " was too large and truncated to " + valueEncoder.getDecimal(false, edgeFlags)); } public static double stringToMeter(String value) { @@ -124,7 +125,7 @@ public static double stringToMeter(String value) { if (value.isEmpty()) { return offset; } - + try { return Double.parseDouble(value) * factor + offset; } catch (NumberFormatException e) { @@ -149,20 +150,20 @@ public static boolean isInvalidValue(String value) { public static double stringToKmh(String str) { if (Helper.isEmpty(str)) return Double.NaN; - + // on some German autobahns and a very few other places if ("none".equals(str)) return MaxSpeed.UNLIMITED_SIGN_SPEED; - + if (str.endsWith(":rural") || str.endsWith(":trunk")) return 80; - + if (str.endsWith(":urban")) return 50; - + if (str.equals("walk") || str.endsWith(":living_street")) return 6; - + int mpInteger = str.indexOf("mp"); int knotInteger = str.indexOf("knots"); int kmInteger = str.indexOf("km"); @@ -183,18 +184,18 @@ public static double stringToKmh(String str) { } factor = 1; } - + double value; try { value = Integer.parseInt(str) * factor; } catch (Exception ex) { return Double.NaN; } - + if (value <= 0) { return Double.NaN; } - + return value; } } \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java index 11ae479b2ef..3e2f8d4056a 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/AbstractWeighting.java @@ -19,7 +19,6 @@ import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.FetchMode; @@ -29,24 +28,15 @@ * @author Peter Karich */ public abstract class AbstractWeighting implements Weighting { - protected final DecimalEncodedValue avSpeedEnc; protected final BooleanEncodedValue accessEnc; + protected final DecimalEncodedValue speedEnc; private final TurnCostProvider turnCostProvider; - protected AbstractWeighting(FlagEncoder encoder) { - this(encoder, NO_TURN_COST_PROVIDER); - } - - protected AbstractWeighting(FlagEncoder encoder, TurnCostProvider turnCostProvider) { - this(encoder.getAverageSpeedEnc(), encoder.getAccessEnc(), turnCostProvider); - } - - protected AbstractWeighting(DecimalEncodedValue avSpeedEnc, BooleanEncodedValue accessEnc, TurnCostProvider turnCostProvider) { + protected AbstractWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, TurnCostProvider turnCostProvider) { if (!isValidName(getName())) throw new IllegalStateException("Not a valid name for a Weighting: " + getName()); - - this.avSpeedEnc = avSpeedEnc; this.accessEnc = accessEnc; + this.speedEnc = speedEnc; this.turnCostProvider = turnCostProvider; } @@ -73,15 +63,15 @@ public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { throw new IllegalStateException("Calculating time should not require to read speed from edge in wrong direction. " + "(" + edgeState.getBaseNode() + " - " + edgeState.getAdjNode() + ") " + edgeState.fetchWayGeometry(FetchMode.ALL) + ", dist: " + edgeState.getDistance() + " " - + "Reverse:" + reverse + ", fwd:" + edgeState.get(accessEnc) + ", bwd:" + edgeState.getReverse(accessEnc) + ", fwd-speed: " + edgeState.get(avSpeedEnc) + ", bwd-speed: " + edgeState.getReverse(avSpeedEnc)); + + "Reverse:" + reverse + ", fwd:" + edgeState.get(accessEnc) + ", bwd:" + edgeState.getReverse(accessEnc) + ", fwd-speed: " + edgeState.get(speedEnc) + ", bwd-speed: " + edgeState.getReverse(speedEnc)); - double speed = reverse ? edgeState.getReverse(avSpeedEnc) : edgeState.get(avSpeedEnc); + double speed = reverse ? edgeState.getReverse(speedEnc) : edgeState.get(speedEnc); if (Double.isInfinite(speed) || Double.isNaN(speed) || speed < 0) throw new IllegalStateException("Invalid speed stored in edge! " + speed); if (speed == 0) throw new IllegalStateException("Speed cannot be 0 for unblocked edge, use access properties to mark edge blocked! Should only occur for shortest path calculation. See #242."); - return (long) (edgeState.getDistance() * 3600 / speed); + return Math.round(edgeState.getDistance() / speed * 3.6 * 1000); } @Override @@ -91,7 +81,7 @@ public double getSpeed(EdgeIteratorState edgeState, boolean reverse) { reverse = false; } - return reverse ? edgeState.getReverse(avSpeedEnc) : edgeState.get(avSpeedEnc); + return reverse ? edgeState.getReverse(speedEnc) : edgeState.get(speedEnc); } @Override @@ -122,7 +112,7 @@ static boolean isValidName(String name) { @Override public String toString() { - return getName() + "|" + avSpeedEnc.getName().split("$")[0]; + return getName() + "|" + speedEnc.getName().split("$")[0]; } } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/BalancedWeightApproximator.java b/core/src/main/java/com/graphhopper/routing/weighting/BalancedWeightApproximator.java index 33a240a8e57..62cbc5d65c2 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/BalancedWeightApproximator.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/BalancedWeightApproximator.java @@ -33,7 +33,7 @@ * Mitoh, K. (1994). A fast algorithm for finding better routes by ai search techniques. In VNIS, * pages 291–296. * - * [2] Pijls, W.H.L.M, & Post, H. (2008). A new bidirectional algorithm for shortest paths (No. EI 2008-25). + * [2] Pijls, W.H.L.M, & Post, H. (2008). A new bidirectional algorithm for shortest paths (No. EI 2008-25). * Report / Econometric Institute, Erasmus University Rotterdam * * @author jansoe diff --git a/core/src/main/java/com/graphhopper/routing/weighting/BlockAreaWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/BlockAreaWeighting.java deleted file mode 100644 index b1ceb891690..00000000000 --- a/core/src/main/java/com/graphhopper/routing/weighting/BlockAreaWeighting.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.graphhopper.routing.weighting; - -import com.graphhopper.routing.querygraph.QueryGraph; -import com.graphhopper.storage.GraphEdgeIdFinder; -import com.graphhopper.util.EdgeIteratorState; - -/** - * This weighting is a wrapper for every weighting to support block_area - */ -public class BlockAreaWeighting extends AbstractAdjustedWeighting { - - private GraphEdgeIdFinder.BlockArea blockArea; - - public BlockAreaWeighting(Weighting superWeighting, GraphEdgeIdFinder.BlockArea blockArea) { - super(superWeighting); - this.blockArea = blockArea; - } - - @Override - public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { - if (blockArea.intersects(edgeState)) - return Double.POSITIVE_INFINITY; - - return superWeighting.calcEdgeWeight(edgeState, reverse); - } - - @Override - public String getName() { - return "block_area"; - } -} diff --git a/core/src/main/java/com/graphhopper/routing/weighting/CurvatureWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/CurvatureWeighting.java deleted file mode 100644 index 8528277264a..00000000000 --- a/core/src/main/java/com/graphhopper/routing/weighting/CurvatureWeighting.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.weighting; - -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.PriorityCode; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.PMap; - -import static com.graphhopper.routing.util.PriorityCode.BEST; -import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER; - -/** - * This Class uses bendiness parameter to prefer curvy routes. - */ -public class CurvatureWeighting extends PriorityWeighting { - private final double minFactor; - private final DecimalEncodedValue priorityEnc; - private final DecimalEncodedValue curvatureEnc; - private final DecimalEncodedValue avSpeedEnc; - - public CurvatureWeighting(FlagEncoder flagEncoder, PMap pMap) { - this(flagEncoder, pMap, NO_TURN_COST_PROVIDER); - } - - public CurvatureWeighting(FlagEncoder flagEncoder, PMap pMap, TurnCostProvider turnCostProvider) { - super(flagEncoder, pMap, turnCostProvider); - - priorityEnc = flagEncoder.getDecimalEncodedValue(EncodingManager.getKey(flagEncoder, "priority")); - curvatureEnc = flagEncoder.getDecimalEncodedValue(EncodingManager.getKey(flagEncoder, "curvature")); - avSpeedEnc = flagEncoder.getDecimalEncodedValue(EncodingManager.getKey(flagEncoder, "average_speed")); - double minBendiness = 1; // see correctErrors - minFactor = minBendiness / Math.log(flagEncoder.getMaxSpeed()) / PriorityCode.getValue(BEST.getValue()); - } - - @Override - public double getMinWeight(double distance) { - return minFactor * distance; - } - - @Override - public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { - double priority = edgeState.get(priorityEnc); - double bendiness = edgeState.get(curvatureEnc); - double speed = getRoadSpeed(edgeState, reverse); - double roadDistance = edgeState.getDistance(); - - // We use the log of the speed to decrease the impact of the speed, therefore we don't use the highway - double regularWeight = roadDistance / Math.log(speed); - return (bendiness * regularWeight) / priority; - } - - protected double getRoadSpeed(EdgeIteratorState edge, boolean reverse) { - return reverse ? edge.getReverse(avSpeedEnc) : edge.get(avSpeedEnc); - } - - @Override - public String getName() { - return "curvature"; - } -} diff --git a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java index 1377c531553..7f996014c1a 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/DefaultTurnCostProvider.java @@ -19,8 +19,6 @@ package com.graphhopper.routing.weighting; import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.storage.TurnCostStorage; import com.graphhopper.util.EdgeIterator; @@ -32,19 +30,15 @@ public class DefaultTurnCostProvider implements TurnCostProvider { private final int uTurnCostsInt; private final double uTurnCosts; - public DefaultTurnCostProvider(FlagEncoder encoder, TurnCostStorage turnCostStorage) { - this(encoder, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS); - } - - public DecimalEncodedValue getTurnCostEnc() { - return turnCostEnc; + public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage) { + this(turnCostEnc, turnCostStorage, Weighting.INFINITE_U_TURN_COSTS); } /** * @param uTurnCosts the costs of a u-turn in seconds, for {@link Weighting#INFINITE_U_TURN_COSTS} the u-turn costs * will be infinite */ - public DefaultTurnCostProvider(FlagEncoder encoder, TurnCostStorage turnCostStorage, int uTurnCosts) { + public DefaultTurnCostProvider(DecimalEncodedValue turnCostEnc, TurnCostStorage turnCostStorage, int uTurnCosts) { if (uTurnCosts < 0 && uTurnCosts != INFINITE_U_TURN_COSTS) { throw new IllegalArgumentException("u-turn costs must be positive, or equal to " + INFINITE_U_TURN_COSTS + " (=infinite costs)"); } @@ -53,12 +47,15 @@ public DefaultTurnCostProvider(FlagEncoder encoder, TurnCostStorage turnCostStor if (turnCostStorage == null) { throw new IllegalArgumentException("No storage set to calculate turn weight"); } - String key = TurnCost.key(encoder.toString()); // if null the TurnCostProvider can be still useful for edge-based routing - this.turnCostEnc = encoder.hasEncodedValue(key) ? encoder.getDecimalEncodedValue(key) : null; + this.turnCostEnc = turnCostEnc; this.turnCostStorage = turnCostStorage; } + public DecimalEncodedValue getTurnCostEnc() { + return turnCostEnc; + } + @Override public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) { if (!EdgeIterator.Edge.isValid(edgeFrom) || !EdgeIterator.Edge.isValid(edgeTo)) { diff --git a/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java index 9b9069ec603..970e17a48d1 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/FastestWeighting.java @@ -17,9 +17,10 @@ */ package com.graphhopper.routing.weighting; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.ev.EnumEncodedValue; import com.graphhopper.routing.ev.RoadAccess; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.PMap; import com.graphhopper.util.Parameters.Routing; @@ -34,6 +35,8 @@ * @author Peter Karich */ public class FastestWeighting extends AbstractWeighting { + public static String DESTINATION_FACTOR = "road_access_destination_factor"; + public static String PRIVATE_FACTOR = "road_access_private_factor"; /** * Converting to seconds is not necessary but makes adding other penalties easier (e.g. turn * costs or traffic light costs etc) @@ -46,33 +49,31 @@ public class FastestWeighting extends AbstractWeighting { // this factor puts a penalty on roads with a "destination"-only or private access, see #733 and #1936 private final double destinationPenalty, privatePenalty; - public FastestWeighting(FlagEncoder encoder) { - this(encoder, new PMap(0)); + public FastestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + this(accessEnc, speedEnc, NO_TURN_COST_PROVIDER); } - public FastestWeighting(FlagEncoder encoder, TurnCostProvider turnCostProvider) { - this(encoder, new PMap(0), turnCostProvider); + public FastestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, TurnCostProvider turnCostProvider) { + this(accessEnc, speedEnc, null, new PMap(0), turnCostProvider); } - public FastestWeighting(FlagEncoder encoder, PMap map) { - this(encoder, map, NO_TURN_COST_PROVIDER); - } - - public FastestWeighting(FlagEncoder encoder, PMap map, TurnCostProvider turnCostProvider) { - super(encoder, turnCostProvider); + public FastestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, EnumEncodedValue roadAccessEnc, PMap map, TurnCostProvider turnCostProvider) { + super(accessEnc, speedEnc, turnCostProvider); headingPenalty = map.getDouble(Routing.HEADING_PENALTY, Routing.DEFAULT_HEADING_PENALTY); headingPenaltyMillis = Math.round(headingPenalty * 1000); - maxSpeed = encoder.getMaxSpeed() / SPEED_CONV; - - if (!encoder.hasEncodedValue(RoadAccess.KEY)) - throw new IllegalArgumentException("road_access is not available but expected for FastestWeighting"); - - // ensure that we do not need to change getMinWeight, i.e. road_access_factor >= 1 - double defaultDestinationFactor = encoder.isMotorVehicle() ? 10 : 1; - destinationPenalty = checkBounds("road_access_destination_factor", map.getDouble("road_access_destination_factor", defaultDestinationFactor), 1, 10); - double defaultPrivateFactor = encoder.isMotorVehicle() ? 10 : 1.2; - privatePenalty = checkBounds("road_access_private_factor", map.getDouble("road_access_private_factor", defaultPrivateFactor), 1, 10); - roadAccessEnc = destinationPenalty > 1 || privatePenalty > 1 ? encoder.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class) : null; + maxSpeed = speedEnc.getMaxOrMaxStorableDecimal() / SPEED_CONV; + + destinationPenalty = map.getDouble(DESTINATION_FACTOR, 1); + privatePenalty = map.getDouble(PRIVATE_FACTOR, 1); + // ensure that we do not need to change getMinWeight, i.e. both factors need to be >= 1 + checkBounds(DESTINATION_FACTOR, destinationPenalty, 1, 10); + checkBounds(PRIVATE_FACTOR, privatePenalty, 1, 10); + if (destinationPenalty > 1 || privatePenalty > 1) { + if (roadAccessEnc == null) + throw new IllegalArgumentException("road_access must not be null when destination or private penalties are > 1"); + this.roadAccessEnc = roadAccessEnc; + } else + this.roadAccessEnc = null; } @Override @@ -82,7 +83,7 @@ public double getMinWeight(double distance) { @Override public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { - double speed = reverse ? edgeState.getReverse(avSpeedEnc) : edgeState.get(avSpeedEnc); + double speed = reverse ? edgeState.getReverse(speedEnc) : edgeState.get(speedEnc); if (speed == 0) return Double.POSITIVE_INFINITY; diff --git a/core/src/main/java/com/graphhopper/routing/weighting/PriorityWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/PriorityWeighting.java index 6e3c5094505..43f33a43d16 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/PriorityWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/PriorityWeighting.java @@ -17,9 +17,10 @@ */ package com.graphhopper.routing.weighting; +import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.RoadAccess; import com.graphhopper.routing.util.PriorityCode; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.PMap; @@ -35,11 +36,12 @@ public class PriorityWeighting extends FastestWeighting { private final double minFactor; private final double maxPrio; - private final DecimalEncodedValue priorityEnc; + protected final DecimalEncodedValue priorityEnc; - public PriorityWeighting(FlagEncoder encoder, PMap pMap, TurnCostProvider turnCostProvider) { - super(encoder, pMap, turnCostProvider); - priorityEnc = encoder.getDecimalEncodedValue(EncodingManager.getKey(encoder, "priority")); + public PriorityWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, DecimalEncodedValue priorityEnc, + EnumEncodedValue roadAccessEnc, PMap pMap, TurnCostProvider turnCostProvider) { + super(accessEnc, speedEnc, roadAccessEnc, pMap, turnCostProvider); + this.priorityEnc = priorityEnc; minFactor = 1 / PriorityCode.getValue(BEST.getValue()); maxPrio = PriorityCode.getFactor(BEST.getValue()); } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java index cc6ec82b949..90bdf80be12 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/ShortFastestWeighting.java @@ -17,12 +17,13 @@ */ package com.graphhopper.routing.weighting; -import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.RoadAccess; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.PMap; -import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER; - /** * Calculates the fastest route with distance influence controlled by a new parameter. *

@@ -36,8 +37,8 @@ public class ShortFastestWeighting extends FastestWeighting { private final double distanceFactor; private final double timeFactor; - public ShortFastestWeighting(FlagEncoder encoder, PMap map, TurnCostProvider turnCostProvider) { - super(encoder, map, turnCostProvider); + public ShortFastestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, EnumEncodedValue roadAccessEnc, PMap map, TurnCostProvider turnCostProvider) { + super(accessEnc, speedEnc, roadAccessEnc, map, turnCostProvider); timeFactor = checkBounds(TIME_FACTOR, map.getDouble(TIME_FACTOR, 1), 0, 10); // default value derived from the cost for time e.g. 25€/hour and for distance 0.5€/km @@ -47,16 +48,6 @@ public ShortFastestWeighting(FlagEncoder encoder, PMap map, TurnCostProvider tur throw new IllegalArgumentException("[" + NAME + "] one of distance_factor or time_factor has to be non-zero"); } - public ShortFastestWeighting(FlagEncoder encoder, double distanceFactor) { - this(encoder, distanceFactor, NO_TURN_COST_PROVIDER); - } - - public ShortFastestWeighting(FlagEncoder encoder, double distanceFactor, TurnCostProvider turnCostProvider) { - super(encoder, new PMap(), turnCostProvider); - this.distanceFactor = checkBounds(DISTANCE_FACTOR, distanceFactor, 0, 10); - this.timeFactor = 1; - } - @Override public double getMinWeight(double distance) { return super.getMinWeight(distance) * timeFactor + distance * distanceFactor; diff --git a/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java index ba7a8bbdd52..5950173e9a0 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/ShortestWeighting.java @@ -19,7 +19,6 @@ import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.util.EdgeIteratorState; import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER; @@ -32,16 +31,12 @@ * @author Peter Karich */ public class ShortestWeighting extends AbstractWeighting { - public ShortestWeighting(FlagEncoder flagEncoder) { - this(flagEncoder, NO_TURN_COST_PROVIDER); + public ShortestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + this(accessEnc, speedEnc, NO_TURN_COST_PROVIDER); } - public ShortestWeighting(FlagEncoder flagEncoder, TurnCostProvider turnCostProvider) { - this(flagEncoder.getAverageSpeedEnc(), flagEncoder.getAccessEnc(), turnCostProvider); - } - - public ShortestWeighting(DecimalEncodedValue avgSpeedEnc, BooleanEncodedValue accessEnc, TurnCostProvider turnCostProvider) { - super(avgSpeedEnc, accessEnc, turnCostProvider); + public ShortestWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, TurnCostProvider turnCostProvider) { + super(accessEnc, speedEnc, turnCostProvider); } @Override diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/ExpressionVisitor.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitor.java similarity index 63% rename from core/src/main/java/com/graphhopper/routing/weighting/custom/ExpressionVisitor.java rename to core/src/main/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitor.java index 333295023ba..997c6c3ece0 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/ExpressionVisitor.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitor.java @@ -17,7 +17,6 @@ */ package com.graphhopper.routing.weighting.custom; -import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.EncodedValueLookup; import com.graphhopper.routing.ev.RouteNetwork; import com.graphhopper.routing.ev.StringEncodedValue; @@ -30,25 +29,27 @@ import static com.graphhopper.routing.weighting.custom.CustomModelParser.IN_AREA_PREFIX; -class ExpressionVisitor implements Visitor.AtomVisitor { +/** + * Expression visitor for the if or else_if condition. + */ +class ConditionalExpressionVisitor implements Visitor.AtomVisitor { + private static final Set allowedMethodParents = new HashSet<>(Arrays.asList("edge", "Math")); + private static final Set allowedMethods = new HashSet<>(Arrays.asList("ordinal", "getDistance", "getName", + "contains", "sqrt", "abs")); private final ParseResult result; - private final EncodedValueLookup lookup; private final TreeMap replacements = new TreeMap<>(); - private final NameValidator nameValidator; - private final Set allowedMethods = new HashSet<>(Arrays.asList("ordinal", "getDistance", "getName", - "contains", "sqrt", "abs")); + private final NameValidator variableValidator; private String invalidMessage; - public ExpressionVisitor(ParseResult result, NameValidator nameValidator, EncodedValueLookup lookup) { + public ConditionalExpressionVisitor(ParseResult result, NameValidator variableValidator) { this.result = result; - this.nameValidator = nameValidator; - this.lookup = lookup; + this.variableValidator = variableValidator; } // allow only methods and other identifiers (constants and encoded values) boolean isValidIdentifier(String identifier) { - if (nameValidator.isValid(identifier)) { + if (variableValidator.isValid(identifier)) { if (!Character.isUpperCase(identifier.charAt(0))) result.guessedVariables.add(identifier); return true; @@ -83,18 +84,35 @@ public Boolean visitRvalue(Java.Rvalue rv) throws Exception { } else if (rv instanceof Java.UnaryOperation) { Java.UnaryOperation uo = (Java.UnaryOperation) rv; if (uo.operator.equals("!")) return uo.operand.accept(this); + if (uo.operator.equals("-")) return uo.operand.accept(this); return false; } else if (rv instanceof Java.MethodInvocation) { Java.MethodInvocation mi = (Java.MethodInvocation) rv; if (allowedMethods.contains(mi.methodName)) { - // skip methods like this.in() for now + // skip methods like this.in() if (mi.target != null) { - // edge.getDistance, Math.sqrt => check target name (edge or Math) Java.AmbiguousName n = (Java.AmbiguousName) mi.target.toRvalue(); - if (n.identifiers.length == 2 && isValidIdentifier(n.identifiers[0])) return true; + if (n.identifiers.length == 2) { + if (allowedMethodParents.contains(n.identifiers[0])) { + // edge.getDistance(), Math.sqrt(x) => check target name i.e. edge or Math + if (mi.arguments.length == 0) { + result.guessedVariables.add(n.identifiers[0]); // return "edge" + return true; + } else if (mi.arguments.length == 1) { + // return "x" but verify before + return mi.arguments[0].accept(this); + } + } else if (variableValidator.isValid(n.identifiers[0])) { + // road_class.ordinal() + if (mi.arguments.length == 0) { + result.guessedVariables.add(n.identifiers[0]); // return road_class + return true; + } + } + } } } - invalidMessage = mi.methodName + " is illegal method"; + invalidMessage = mi.methodName + " is an illegal method in a conditional expression"; return false; } else if (rv instanceof Java.ParenthesizedExpression) { return ((Java.ParenthesizedExpression) rv).value.accept(this); @@ -104,21 +122,10 @@ public Boolean visitRvalue(Java.Rvalue rv) throws Exception { if (binOp.lhs instanceof Java.AmbiguousName && ((Java.AmbiguousName) binOp.lhs).identifiers.length == 1) { String lhVarAsString = ((Java.AmbiguousName) binOp.lhs).identifiers[0]; boolean eqOps = binOp.operator.equals("==") || binOp.operator.equals("!="); - if (binOp.rhs instanceof Java.StringLiteral) { - // replace String with its index for faster comparison (?) and skipping the Map lookup at runtime - if (lookup.hasEncodedValue(lhVarAsString)) { - if (!eqOps) - throw new IllegalArgumentException("Operator " + binOp.operator + " not allowed for String"); - StringEncodedValue ev = lookup.getStringEncodedValue(lhVarAsString); - String str = ((Java.StringLiteral) binOp.rhs).value; - int integ = ev.indexOf(str.substring(1, str.length() - 1)); - if (integ == 0) integ = -1; // 0 means not found and this should always trigger inequality - replacements.put(startRH, new Replacement(startRH, str.length(), "" + integ)); - } - } else if (binOp.rhs instanceof Java.AmbiguousName && ((Java.AmbiguousName) binOp.rhs).identifiers.length == 1) { + if (binOp.rhs instanceof Java.AmbiguousName && ((Java.AmbiguousName) binOp.rhs).identifiers.length == 1) { // Make enum explicit as NO or OTHER can occur in other enums so convert "toll == NO" to "toll == Toll.NO" String rhValueAsString = ((Java.AmbiguousName) binOp.rhs).identifiers[0]; - if (nameValidator.isValid(lhVarAsString) && Helper.toUpperCase(rhValueAsString).equals(rhValueAsString)) { + if (variableValidator.isValid(lhVarAsString) && Helper.toUpperCase(rhValueAsString).equals(rhValueAsString)) { if (!eqOps) throw new IllegalArgumentException("Operator " + binOp.operator + " not allowed for enum"); String value = toEncodedValueClassName(binOp.lhs.toString()); @@ -146,31 +153,6 @@ public Boolean visitConstructorInvocation(Java.ConstructorInvocation ci) { return false; } - static void parseExpressions(StringBuilder expressions, NameValidator nameInConditionValidator, String exceptionInfo, - Set createObjects, List list, EncodedValueLookup lookup, String lastStmt) { - - for (Statement statement : list) { - if (statement.getKeyword() == Statement.Keyword.ELSE) { - if (!Helper.isEmpty(statement.getCondition())) - throw new IllegalArgumentException("expression must be empty but was " + statement.getCondition()); - - expressions.append("else {" + statement.getOperation().build(statement.getValue()) + "; }\n"); - } else if (statement.getKeyword() == Statement.Keyword.ELSEIF || statement.getKeyword() == Statement.Keyword.IF) { - ExpressionVisitor.ParseResult parseResult = parseExpression(statement.getCondition(), nameInConditionValidator, lookup); - if (!parseResult.ok) - throw new IllegalArgumentException(exceptionInfo + " invalid expression \"" + statement.getCondition() + "\"" + - (parseResult.invalidMessage == null ? "" : ": " + parseResult.invalidMessage)); - createObjects.addAll(parseResult.guessedVariables); - if (statement.getKeyword() == Statement.Keyword.ELSEIF) - expressions.append("else "); - expressions.append("if (" + parseResult.converted + ") {" + statement.getOperation().build(statement.getValue()) + "; }\n"); - } else { - throw new IllegalArgumentException("The statement must be either 'if', 'else_if' or 'else'"); - } - } - expressions.append(lastStmt); - } - /** * Enforce simple expressions of user input to increase security. * @@ -178,7 +160,7 @@ static void parseExpressions(StringBuilder expressions, NameValidator nameInCond * converted expression that includes class names for constants to avoid conflicts e.g. when doing "toll == Toll.NO" * instead of "toll == NO". */ - static ParseResult parseExpression(String expression, NameValidator validator, EncodedValueLookup lookup) { + static ParseResult parse(String expression, NameValidator validator) { ParseResult result = new ParseResult(); try { Parser parser = new Parser(new Scanner("ignore", new StringReader(expression))); @@ -186,7 +168,7 @@ static ParseResult parseExpression(String expression, NameValidator validator, E // after parsing the expression the input should end (otherwise it is not "simple") if (parser.peek().type == TokenType.END_OF_INPUT) { result.guessedVariables = new LinkedHashSet<>(); - ExpressionVisitor visitor = new ExpressionVisitor(result, validator, lookup); + ConditionalExpressionVisitor visitor = new ConditionalExpressionVisitor(result, validator); result.ok = atom.accept(visitor); result.invalidMessage = visitor.invalidMessage; if (result.ok) { @@ -211,17 +193,6 @@ static String toEncodedValueClassName(String arg) { return Character.toUpperCase(clazz.charAt(0)) + clazz.substring(1); } - static class ParseResult { - StringBuilder converted; - boolean ok; - String invalidMessage; - Set guessedVariables; - } - - interface NameValidator { - boolean isValid(String name); - } - static class Replacement { int start; int oldLength; diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java index cba92ecb3ea..ae308da14e7 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java @@ -17,15 +17,11 @@ */ package com.graphhopper.routing.weighting.custom; +import com.graphhopper.json.MinMax; import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.weighting.TurnCostProvider; -import com.graphhopper.util.CustomModel; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.JsonFeature; +import com.graphhopper.util.*; import com.graphhopper.util.shapes.BBox; import com.graphhopper.util.shapes.Polygon; import org.codehaus.commons.compiler.CompileException; @@ -45,7 +41,7 @@ public class CustomModelParser { private static final AtomicLong longVal = new AtomicLong(1); static final String IN_AREA_PREFIX = "in_"; - private static final Set allowedNames = new HashSet<>(Arrays.asList("edge", "Math")); + static final String BACKWARD_PREFIX = "backward_"; private static final boolean JANINO_DEBUG = Boolean.getBoolean(Scanner.SYSTEM_PROPERTY_SOURCE_DEBUGGING_ENABLE); private static final String SCRIPT_FILE_DIR = System.getProperty(Scanner.SYSTEM_PROPERTY_SOURCE_DEBUGGING_DIR, "./src/main/java/com/graphhopper/routing/weighting/custom"); @@ -70,45 +66,43 @@ private CustomModelParser() { // utility class } - public static CustomWeighting createWeighting(FlagEncoder baseFlagEncoder, EncodedValueLookup lookup, - TurnCostProvider turnCostProvider, CustomModel customModel) { + public static CustomWeighting createWeighting(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, DecimalEncodedValue priorityEnc, + EncodedValueLookup lookup, TurnCostProvider turnCostProvider, CustomModel customModel) { if (customModel == null) throw new IllegalStateException("CustomModel cannot be null"); - DecimalEncodedValue avgSpeedEnc = lookup.getDecimalEncodedValue(EncodingManager.getKey(baseFlagEncoder.toString(), "average_speed")); - final String pKey = EncodingManager.getKey(baseFlagEncoder.toString(), "priority"); - DecimalEncodedValue priorityEnc = lookup.hasEncodedValue(pKey) ? lookup.getDecimalEncodedValue(pKey) : null; - - CustomWeighting.Parameters parameters = createWeightingParameters(customModel, lookup, - avgSpeedEnc, baseFlagEncoder.getMaxSpeed(), priorityEnc); - return new CustomWeighting(baseFlagEncoder, turnCostProvider, parameters); + double maxSpeed = speedEnc.getMaxOrMaxStorableDecimal(); + CustomWeighting.Parameters parameters = createWeightingParameters(customModel, lookup, speedEnc, maxSpeed, priorityEnc); + return new CustomWeighting(accessEnc, speedEnc, turnCostProvider, parameters); } /** * This method compiles a new subclass of CustomWeightingHelper composed from the provided CustomModel caches this * and returns an instance. + * * @param priorityEnc can be null */ static CustomWeighting.Parameters createWeightingParameters(CustomModel customModel, EncodedValueLookup lookup, DecimalEncodedValue avgSpeedEnc, double globalMaxSpeed, DecimalEncodedValue priorityEnc) { - final double maxSpeed = customModel.findMaxSpeed(globalMaxSpeed); // globalMaxSpeed can be lower than avgSpeedEnc.getMaxDecimal() - final double maxPriority = customModel.findMaxPriority(priorityEnc == null ? 1 : priorityEnc.getMaxDecimal()); - String key = customModel.toString() + ",maxSpeed:" + maxSpeed + ",maxPriority:" + maxPriority; + double globalMaxPriority = priorityEnc == null ? 1 : priorityEnc.getMaxStorableDecimal(); + // if the same custom model is used with a different base profile we cannot use the cached version + String key = customModel + ",speed:" + avgSpeedEnc.getName() + ",global_max_speed:" + globalMaxSpeed + + (priorityEnc == null ? "" : "prio:" + priorityEnc.getName() + ",global_max_priority:" + globalMaxPriority); if (key.length() > 100_000) throw new IllegalArgumentException("Custom Model too big: " + key.length()); Class clazz = customModel.isInternal() ? INTERNAL_CACHE.get(key) : null; if (CACHE_SIZE > 0 && clazz == null) clazz = CACHE.get(key); if (clazz == null) { - clazz = createClazz(customModel, lookup, maxSpeed); + clazz = createClazz(customModel, lookup, globalMaxSpeed, globalMaxPriority); if (customModel.isInternal()) { INTERNAL_CACHE.put(key, clazz); if (INTERNAL_CACHE.size() > 100) { CACHE.putAll(INTERNAL_CACHE); INTERNAL_CACHE.clear(); LoggerFactory.getLogger(CustomModelParser.class).warn("Internal cache must stay small but was " - + INTERNAL_CACHE.size() + ". Cleared it. Misuse of CustomModel::__internal_cache?"); + + INTERNAL_CACHE.size() + ". Cleared it. Misuse of CustomModel::internal?"); } } else if (CACHE_SIZE > 0) { CACHE.put(key, clazz); @@ -118,25 +112,58 @@ static CustomWeighting.Parameters createWeightingParameters(CustomModel customMo try { // The class does not need to be thread-safe as we create an instance per request CustomWeightingHelper prio = (CustomWeightingHelper) clazz.getDeclaredConstructor().newInstance(); - prio.init(lookup, avgSpeedEnc, priorityEnc, customModel.getAreas()); - return new CustomWeighting.Parameters(prio::getSpeed, prio::getPriority, maxSpeed, maxPriority, - customModel.getDistanceInfluence(), customModel.getHeadingPenalty()); + prio.init(lookup, avgSpeedEnc, priorityEnc, CustomModel.getAreasAsMap(customModel.getAreas())); + return new CustomWeighting.Parameters(prio::getSpeed, prio::getPriority, prio.getMaxSpeed(), prio.getMaxPriority(), + customModel.getDistanceInfluence() == null ? 0 : customModel.getDistanceInfluence(), customModel.getHeadingPenalty()); } catch (ReflectiveOperationException ex) { throw new IllegalArgumentException("Cannot compile expression " + ex.getMessage(), ex); } } - private static Class createClazz(CustomModel customModel, EncodedValueLookup lookup, double globalMaxSpeed) { + /** + * This method does the following: + *

    + *
  • 0. optionally we already checked the right-hand side expressions before this method call in FindMinMax.checkLMConstraints + * (only the client-side custom model statements) + *
  • + *
  • 1. determine minimum and maximum values via parsing the right-hand side expression -> done in ValueExpressionVisitor. + * We need the maximum values for a simple negative check AND for the CustomWeighting.Parameters which is for + * Weighting.getMinWeight which is for A*. Note: we could make this step optional somehow for other algorithms, + * but parsing would be still required in the next step for security reasons. + *
  • + *
  • 2. parse condition value of priority and speed statements -> done in ConditionalExpressionVisitor (don't parse RHS expressions again) + *
  • + *
  • 3. create class template as String, inject the created statements and create the Class + *
  • + *
+ */ + private static Class createClazz(CustomModel customModel, EncodedValueLookup lookup, + double globalMaxSpeed, double globalMaxPriority) { try { HashSet priorityVariables = new LinkedHashSet<>(); + // initial value of minimum has to be >0 so that multiple_by with a negative value leads to a negative value and not 0 + MinMax minMaxPriority = new MinMax(1, globalMaxPriority); + FindMinMax.findMinMax(priorityVariables, minMaxPriority, customModel.getPriority(), lookup); + if (minMaxPriority.min < 0) + throw new IllegalArgumentException("priority has to be >=0 but can be negative (" + minMaxPriority.min + ")"); + if (minMaxPriority.max < 0) + throw new IllegalArgumentException("maximum priority has to be >=0 but was " + minMaxPriority.max); List priorityStatements = createGetPriorityStatements(priorityVariables, customModel, lookup); + HashSet speedVariables = new LinkedHashSet<>(); - List speedStatements = createGetSpeedStatements(speedVariables, customModel, lookup, globalMaxSpeed); + MinMax minMaxSpeed = new MinMax(1, globalMaxSpeed); + FindMinMax.findMinMax(speedVariables, minMaxSpeed, customModel.getSpeed(), lookup); + if (minMaxSpeed.min < 0) + throw new IllegalArgumentException("speed has to be >=0 but can be negative (" + minMaxSpeed.min + ")"); + if (minMaxSpeed.max <= 0) + throw new IllegalArgumentException("maximum speed has to be >0 but was " + minMaxSpeed.max); + List speedStatements = createGetSpeedStatements(speedVariables, customModel, lookup); // Create different class name, which is required only for debugging. // TODO does it improve performance too? I.e. it could be that the JIT is confused if different classes // have the same name and it mixes performance stats. See https://github.com/janino-compiler/janino/issues/137 long counter = longVal.incrementAndGet(); - String classTemplate = createClassTemplate(counter, priorityVariables, speedVariables, lookup, customModel); + String classTemplate = createClassTemplate(counter, priorityVariables, minMaxPriority.max, speedVariables, minMaxSpeed.max, + lookup, CustomModel.getAreasAsMap(customModel.getAreas())); Java.CompilationUnit cu = (Java.CompilationUnit) new Parser(new Scanner("source", new StringReader(classTemplate))). parseAbstractCompilationUnit(); cu = injectStatements(priorityStatements, speedStatements, cu); @@ -144,8 +171,6 @@ private static Class createClazz(CustomModel customModel, EncodedValueLookup return sc.getClassLoader().loadClass("com.graphhopper.routing.weighting.custom.JaninoCustomWeightingHelperSubclass" + counter); } catch (Exception ex) { String errString = "Cannot compile expression"; - if (ex instanceof CompileException) - errString += ", in " + ((CompileException) ex).getLocation().getFileName(); throw new IllegalArgumentException(errString + ": " + ex.getMessage(), ex); } } @@ -156,11 +181,9 @@ private static Class createClazz(CustomModel customModel, EncodedValueLookup * @return the created statements (parsed expressions) */ private static List createGetSpeedStatements(Set speedVariables, - CustomModel customModel, EncodedValueLookup lookup, - double globalMaxSpeed) throws Exception { - List speedStatements = new ArrayList<>(); - speedStatements.addAll(verifyExpressions(new StringBuilder(), "in 'speed' entry, ", speedVariables, - customModel.getSpeed(), lookup, "return Math.min(value, " + globalMaxSpeed + ");\n")); + CustomModel customModel, EncodedValueLookup lookup) throws Exception { + List speedStatements = new ArrayList<>(verifyExpressions(new StringBuilder(), + "speed entry", speedVariables, customModel.getSpeed(), lookup)); String speedMethodStartBlock = "double value = super.getRawSpeed(edge, reverse);\n"; // a bit inefficient to possibly define variables twice, but for now we have two separate methods for (String arg : speedVariables) { @@ -178,9 +201,8 @@ private static List createGetSpeedStatements(Set sp */ private static List createGetPriorityStatements(Set priorityVariables, CustomModel customModel, EncodedValueLookup lookup) throws Exception { - List priorityStatements = new ArrayList<>(); - priorityStatements.addAll(verifyExpressions(new StringBuilder(), "in 'priority' entry, ", - priorityVariables, customModel.getPriority(), lookup, "return value;")); + List priorityStatements = new ArrayList<>(verifyExpressions(new StringBuilder(), + "priority entry", priorityVariables, customModel.getPriority(), lookup)); String priorityMethodStartBlock = "double value = super.getRawPriority(edge, reverse);\n"; for (String arg : priorityVariables) { priorityMethodStartBlock += getVariableDeclaration(lookup, arg); @@ -190,21 +212,27 @@ private static List createGetPriorityStatements(Set return priorityStatements; } - static boolean isValidVariableName(String name) { - return name.startsWith(IN_AREA_PREFIX) || allowedNames.contains(name); - } - /** * For the methods getSpeed and getPriority we declare variables that contain the encoded value of the current edge * or if an area contains the current edge. */ - private static String getVariableDeclaration(EncodedValueLookup lookup, String arg) { + private static String getVariableDeclaration(EncodedValueLookup lookup, final String arg) { if (lookup.hasEncodedValue(arg)) { EncodedValue enc = lookup.getEncodedValue(arg, EncodedValue.class); return getReturnType(enc) + " " + arg + " = reverse ? " + "edge.getReverse((" + getInterface(enc) + ") this." + arg + "_enc) : " + "edge.get((" + getInterface(enc) + ") this." + arg + "_enc);\n"; - } else if (isValidVariableName(arg)) { + } else if (arg.startsWith(BACKWARD_PREFIX)) { + final String argSubstr = arg.substring(BACKWARD_PREFIX.length()); + if (lookup.hasEncodedValue(argSubstr)) { + EncodedValue enc = lookup.getEncodedValue(argSubstr, EncodedValue.class); + return getReturnType(enc) + " " + arg + " = reverse ? " + + "edge.get((" + getInterface(enc) + ") this." + argSubstr + "_enc) : " + + "edge.getReverse((" + getInterface(enc) + ") this." + argSubstr + "_enc);\n"; + } else { + throw new IllegalArgumentException("Not supported for backward: " + argSubstr); + } + } else if (arg.startsWith(IN_AREA_PREFIX)) { return ""; } else { throw new IllegalArgumentException("Not supported " + arg); @@ -238,8 +266,10 @@ private static String getReturnType(EncodedValue encodedValue) { * means that the source file is free from user input and could be directly compiled. Before we do this we still * have to inject that parsed and safe user expressions in a later step. */ - private static String createClassTemplate(long counter, Set priorityVariables, Set speedVariables, - EncodedValueLookup lookup, CustomModel customModel) { + private static String createClassTemplate(long counter, + Set priorityVariables, double maxPriority, + Set speedVariables, double maxSpeed, + EncodedValueLookup lookup, Map areas) { final StringBuilder importSourceCode = new StringBuilder("import com.graphhopper.routing.ev.*;\n"); importSourceCode.append("import java.util.Map;\n"); final StringBuilder classSourceCode = new StringBuilder(100); @@ -247,13 +277,16 @@ private static String createClassTemplate(long counter, Set priorityVari final StringBuilder initSourceCode = new StringBuilder("this.avg_speed_enc = avgSpeedEnc;\n"); initSourceCode.append("this.priority_enc = priorityEnc;\n"); - Set set = new HashSet<>(priorityVariables); - set.addAll(speedVariables); + Set set = new HashSet<>(); + for (String prioVar : priorityVariables) + set.add(prioVar.startsWith(BACKWARD_PREFIX) ? prioVar.substring(BACKWARD_PREFIX.length()) : prioVar); + for (String speedVar : speedVariables) + set.add(speedVar.startsWith(BACKWARD_PREFIX) ? speedVar.substring(BACKWARD_PREFIX.length()) : speedVar); + for (String arg : set) { if (lookup.hasEncodedValue(arg)) { EncodedValue enc = lookup.getEncodedValue(arg, EncodedValue.class); classSourceCode.append("protected " + getInterface(enc) + " " + arg + "_enc;\n"); - initSourceCode.append("if (lookup.hasEncodedValue(\"" + arg + "\")) "); initSourceCode.append("this." + arg + "_enc = (" + getInterface(enc) + ") lookup.getEncodedValue(\"" + arg + "\", EncodedValue.class);\n"); } else if (arg.startsWith(IN_AREA_PREFIX)) { @@ -267,29 +300,29 @@ private static String createClassTemplate(long counter, Set priorityVari includedAreaImports = true; } - String id = arg.substring(IN_AREA_PREFIX.length()); - if (!EncodingManager.isValidEncodedValue(id)) + if (!JsonFeature.isValidId(arg)) throw new IllegalArgumentException("Area has invalid name: " + arg); - JsonFeature feature = customModel.getAreas().get(id); + String id = arg.substring(IN_AREA_PREFIX.length()); + JsonFeature feature = areas.get(id); if (feature == null) throw new IllegalArgumentException("Area '" + id + "' wasn't found"); if (feature.getGeometry() == null) throw new IllegalArgumentException("Area '" + id + "' does not contain a geometry"); if (!(feature.getGeometry() instanceof Polygonal)) throw new IllegalArgumentException("Currently only type=Polygon is supported for areas but was " + feature.getGeometry().getGeometryType()); - if (feature.getProperties() != null && !feature.getProperties().isEmpty() || feature.getBBox() != null) - throw new IllegalArgumentException("Bounding box and properties of area " + id + " must be empty"); + if (feature.getBBox() != null) + throw new IllegalArgumentException("Bounding box of area " + id + " must be empty"); classSourceCode.append("protected " + Polygon.class.getSimpleName() + " " + arg + ";\n"); initSourceCode.append("JsonFeature feature_" + id + " = (JsonFeature) areas.get(\"" + id + "\");\n"); initSourceCode.append("this." + arg + " = new Polygon(new PreparedPolygon((Polygonal) feature_" + id + ".getGeometry()));\n"); } else { - if (!isValidVariableName(arg)) + if (!arg.startsWith(IN_AREA_PREFIX)) throw new IllegalArgumentException("Variable not supported: " + arg); } } return "" - + "package com.graphhopper.routing.weighting.custom;" + + "package com.graphhopper.routing.weighting.custom;\n" + "import " + CustomWeightingHelper.class.getName() + ";\n" + "import " + EncodedValueLookup.class.getName() + ";\n" + "import " + EdgeIteratorState.class.getName() + ";\n" @@ -310,6 +343,14 @@ private static String createClassTemplate(long counter, Set priorityVari + " public double getSpeed(EdgeIteratorState edge, boolean reverse) {\n" + " return getRawSpeed(edge, reverse); //will be overwritten by code injected in DeepCopier\n" + " }\n" + + " @Override\n" + + " protected double getMaxSpeed() {\n" + + " return " + maxSpeed + ";" + + " }\n" + + " @Override\n" + + " protected double getMaxPriority() {\n" + + " return " + maxPriority + ";" + + " }\n" + "}"; } @@ -322,18 +363,45 @@ private static String createClassTemplate(long counter, Set priorityVari * @return the created if-then, else and elseif statements */ private static List verifyExpressions(StringBuilder expressions, String info, Set createObjects, - List list, EncodedValueLookup lookup, - String lastStmt) throws Exception { - // allow variables, all encoded values, constants - ExpressionVisitor.NameValidator nameInConditionValidator = name -> lookup.hasEncodedValue(name) - || name.toUpperCase(Locale.ROOT).equals(name) || isValidVariableName(name); - ExpressionVisitor.parseExpressions(expressions, nameInConditionValidator, info, createObjects, list, lookup, lastStmt); + List list, EncodedValueLookup lookup) throws Exception { + // allow variables, all encoded values, constants and special variables like in_xyarea or backward_car_access + NameValidator nameInConditionValidator = name -> lookup.hasEncodedValue(name) + || name.toUpperCase(Locale.ROOT).equals(name) || name.startsWith(IN_AREA_PREFIX) + || name.startsWith(BACKWARD_PREFIX) && lookup.hasEncodedValue(name.substring(BACKWARD_PREFIX.length())); + + parseExpressions(expressions, nameInConditionValidator, info, createObjects, list); return new Parser(new org.codehaus.janino.Scanner(info, new StringReader(expressions.toString()))). parseBlockStatements(); } + static void parseExpressions(StringBuilder expressions, NameValidator nameInConditionValidator, + String exceptionInfo, Set createObjects, List list) { + + for (Statement statement : list) { + // avoid parsing the RHS value expression again as we just did it to get the maximum values in createClazz + if (statement.getKeyword() == Statement.Keyword.ELSE) { + if (!Helper.isEmpty(statement.getCondition())) + throw new IllegalArgumentException("condition must be empty but was " + statement.getCondition()); + + expressions.append("else {").append(statement.getOperation().build(statement.getValue())).append("; }\n"); + } else if (statement.getKeyword() == Statement.Keyword.ELSEIF || statement.getKeyword() == Statement.Keyword.IF) { + ParseResult parseResult = ConditionalExpressionVisitor.parse(statement.getCondition(), nameInConditionValidator); + if (!parseResult.ok) + throw new IllegalArgumentException(exceptionInfo + " invalid condition \"" + statement.getCondition() + "\"" + + (parseResult.invalidMessage == null ? "" : ": " + parseResult.invalidMessage)); + createObjects.addAll(parseResult.guessedVariables); + if (statement.getKeyword() == Statement.Keyword.ELSEIF) + expressions.append("else "); + expressions.append("if (").append(parseResult.converted).append(") {").append(statement.getOperation().build(statement.getValue())).append("; }\n"); + } else { + throw new IllegalArgumentException("The statement must be either 'if', 'else_if' or 'else'"); + } + } + expressions.append("return value;\n"); + } + /** - * Injects the already parsed expressions (converted to BlockStatement) via janinos DeepCopier to the provided + * Injects the already parsed expressions (converted to BlockStatement) via Janino's DeepCopier to the provided * CompilationUnit cu (a class file). */ private static Java.CompilationUnit injectStatements(List priorityStatements, diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java index 6d18985ef1e..48830f3ace3 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java @@ -18,7 +18,7 @@ package com.graphhopper.routing.weighting.custom; import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.weighting.AbstractWeighting; import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.util.CustomModel; @@ -77,7 +77,6 @@ public final class CustomWeighting extends AbstractWeighting { * costs or traffic light costs etc) */ private final static double SPEED_CONV = 3.6; - private final BooleanEncodedValue baseVehicleAccessEnc; private final double maxSpeed; private final double maxPriority; private final double distanceInfluence; @@ -85,11 +84,10 @@ public final class CustomWeighting extends AbstractWeighting { private final EdgeToDoubleMapping edgeToSpeedMapping; private final EdgeToDoubleMapping edgeToPriorityMapping; - public CustomWeighting(FlagEncoder baseFlagEncoder, TurnCostProvider turnCostProvider, Parameters parameters) { - super(baseFlagEncoder, turnCostProvider); + public CustomWeighting(BooleanEncodedValue baseAccessEnc, DecimalEncodedValue baseSpeedEnc, TurnCostProvider turnCostProvider, Parameters parameters) { + super(baseAccessEnc, baseSpeedEnc, turnCostProvider); this.edgeToSpeedMapping = parameters.getEdgeToSpeedMapping(); this.edgeToPriorityMapping = parameters.getEdgeToPriorityMapping(); - this.baseVehicleAccessEnc = baseFlagEncoder.getAccessEnc(); this.headingPenaltySeconds = parameters.getHeadingPenaltySeconds(); this.maxSpeed = parameters.getMaxSpeed() / SPEED_CONV; this.maxPriority = parameters.getMaxPriority(); @@ -109,12 +107,12 @@ public double getMinWeight(double distance) { public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { final double distance = edgeState.getDistance(); double seconds = calcSeconds(distance, edgeState, reverse); - if (Double.isInfinite(seconds)) - return Double.POSITIVE_INFINITY; + if (Double.isInfinite(seconds)) return Double.POSITIVE_INFINITY; double distanceCosts = distance * distanceInfluence; - if (Double.isInfinite(distanceCosts)) - return Double.POSITIVE_INFINITY; + if (Double.isInfinite(distanceCosts)) return Double.POSITIVE_INFINITY; double priority = edgeToPriorityMapping.get(edgeState, reverse); + // special case to avoid NaN for barrier edges (where time is often 0s) + if (priority == 0 && seconds == 0) return Double.POSITIVE_INFINITY; return seconds / priority + distanceCosts; } @@ -124,7 +122,7 @@ public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { reverse = false; // TODO see #1835 - if (reverse ? !edgeState.getReverse(baseVehicleAccessEnc) : !edgeState.get(baseVehicleAccessEnc)) + if (reverse ? !edgeState.getReverse(accessEnc) : !edgeState.get(accessEnc)) return Double.POSITIVE_INFINITY; double speed = edgeToSpeedMapping.get(edgeState, reverse); @@ -142,6 +140,7 @@ public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { @Override public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { + // we truncate to long here instead of rounding to make it consistent with FastestWeighting, maybe change to rounding later return Math.round(calcSeconds(edgeState.getDistance(), edgeState, reverse) * 1000); } @@ -155,7 +154,7 @@ public interface EdgeToDoubleMapping { double get(EdgeIteratorState edge, boolean reverse); } - static class Parameters { + public static class Parameters { private final EdgeToDoubleMapping edgeToSpeedMapping; private final EdgeToDoubleMapping edgeToPriorityMapping; private final double maxSpeed; @@ -163,7 +162,7 @@ static class Parameters { private final double distanceInfluence; private final double headingPenaltySeconds; - Parameters(EdgeToDoubleMapping edgeToSpeedMapping, EdgeToDoubleMapping edgeToPriorityMapping, + public Parameters(EdgeToDoubleMapping edgeToSpeedMapping, EdgeToDoubleMapping edgeToPriorityMapping, double maxSpeed, double maxPriority, double distanceInfluence, double headingPenaltySeconds) { this.edgeToSpeedMapping = edgeToSpeedMapping; this.edgeToPriorityMapping = edgeToPriorityMapping; diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelper.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelper.java index d295143626d..bdd66c8cea0 100644 --- a/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelper.java +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelper.java @@ -67,11 +67,20 @@ protected final double getRawPriority(EdgeIteratorState edge, boolean reverse) { return priority; } + protected double getMaxPriority() { + return 1; + } + + protected double getMaxSpeed() { + return 1; + } + public static boolean in(Polygon p, EdgeIteratorState edge) { - BBox bbox = GHUtility.createBBox(edge); - if (!p.getBounds().intersects(bbox)) + BBox edgeBBox = GHUtility.createBBox(edge); + BBox polyBBOX = p.getBounds(); + if (!polyBBOX.intersects(edgeBBox)) return false; - if (p.isRectangle()) + if (p.isRectangle() && polyBBOX.contains(edgeBBox)) return true; return p.intersects(edge.fetchWayGeometry(FetchMode.ALL).makeImmutable()); // TODO PERF: cache bbox and edge wayGeometry for multiple area } diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/FindMinMax.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/FindMinMax.java new file mode 100644 index 00000000000..9aa5a1c8eed --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/FindMinMax.java @@ -0,0 +1,102 @@ +package com.graphhopper.routing.weighting.custom; + +import com.graphhopper.json.MinMax; +import com.graphhopper.json.Statement; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.util.CustomModel; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.graphhopper.json.Statement.Keyword.ELSE; +import static com.graphhopper.json.Statement.Keyword.IF; + +public class FindMinMax { + + /** + * This method throws an exception when this CustomModel would decrease the edge weight compared to the specified + * baseModel as in such a case the optimality of A* with landmarks can no longer be guaranteed (as the preparation + * is based on baseModel). + */ + public static void checkLMConstraints(CustomModel baseModel, CustomModel queryModel, EncodedValueLookup lookup) { + if (queryModel.isInternal()) + throw new IllegalArgumentException("CustomModel of query cannot be internal"); + if (queryModel.getDistanceInfluence() != null) { + double bmDI = baseModel.getDistanceInfluence() == null ? 0 : baseModel.getDistanceInfluence(); + if (queryModel.getDistanceInfluence() < bmDI) + throw new IllegalArgumentException("CustomModel in query can only use distance_influence bigger or equal to " + + bmDI + ", but was: " + queryModel.getDistanceInfluence()); + } + + checkMultiplyValue(queryModel.getPriority(), lookup); + checkMultiplyValue(queryModel.getSpeed(), lookup); + } + + private static void checkMultiplyValue(List list, EncodedValueLookup lookup) { + Set createdObjects = new HashSet<>(); + for (Statement statement : list) { + if (statement.getOperation() == Statement.Op.MULTIPLY) { + MinMax minMax = ValueExpressionVisitor.findMinMax(createdObjects, statement.getValue(), lookup); + if (minMax.max > 1) + throw new IllegalArgumentException("maximum of value '" + statement.getValue() + "' cannot be larger than 1, but was: " + minMax.max); + else if (minMax.min < 0) + throw new IllegalArgumentException("minimum of value '" + statement.getValue() + "' cannot be smaller than 0, but was: " + minMax.min); + } + } + } + + /** + * This method returns the smallest value possible in "min" and the smallest value that cannot be + * exceeded by any edge in max. + */ + static MinMax findMinMax(Set createdObjects, MinMax minMax, List statements, EncodedValueLookup lookup) { + // 'blocks' of the statements are applied one after the other. A block consists of one (if) or more statements (elseif+else) + List> blocks = splitIntoBlocks(statements); + for (List block : blocks) findMinMaxForBlock(createdObjects, minMax, block, lookup); + return minMax; + } + + private static void findMinMaxForBlock(Set createdObjects, final MinMax minMax, List block, EncodedValueLookup lookup) { + if (block.isEmpty() || !IF.equals(block.get(0).getKeyword())) + throw new IllegalArgumentException("Every block must start with an if-statement"); + + MinMax minMaxBlock; + if (block.get(0).getCondition().trim().equals("true")) { + minMaxBlock = block.get(0).getOperation().apply(minMax, ValueExpressionVisitor.findMinMax(createdObjects, block.get(0).getValue(), lookup)); + } else { + minMaxBlock = new MinMax(Double.MAX_VALUE, 0); + boolean foundElse = false; + for (Statement s : block) { + if (s.getKeyword() == ELSE) foundElse = true; + MinMax tmp = s.getOperation().apply(minMax, ValueExpressionVisitor.findMinMax(createdObjects, s.getValue(), lookup)); + minMaxBlock.min = Math.min(minMaxBlock.min, tmp.min); + minMaxBlock.max = Math.max(minMaxBlock.max, tmp.max); + } + + // if there is no 'else' statement it's like there is a 'neutral' branch that leaves the initial value as is + if (!foundElse) { + minMaxBlock.min = Math.min(minMaxBlock.min, minMax.min); + minMaxBlock.max = Math.max(minMaxBlock.max, minMax.max); + } + } + + minMax.min = minMaxBlock.min; + minMax.max = minMaxBlock.max; + } + + /** + * Splits the specified list into several list of statements starting with if + */ + private static List> splitIntoBlocks(List statements) { + List> result = new ArrayList<>(); + List block = null; + for (Statement st : statements) { + if (IF.equals(st.getKeyword())) result.add(block = new ArrayList<>()); + if (block == null) throw new IllegalArgumentException("Every block must start with an if-statement"); + block.add(st); + } + return result; + } +} diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/NameValidator.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/NameValidator.java new file mode 100644 index 00000000000..058e7ce8022 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/NameValidator.java @@ -0,0 +1,5 @@ +package com.graphhopper.routing.weighting.custom; + +interface NameValidator { + boolean isValid(String name); +} diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/ParseResult.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/ParseResult.java new file mode 100644 index 00000000000..9adaca5e75e --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/ParseResult.java @@ -0,0 +1,11 @@ +package com.graphhopper.routing.weighting.custom; + +import java.util.Set; + +class ParseResult { + StringBuilder converted; + boolean ok; + String invalidMessage; + Set guessedVariables; + Set operators; +} diff --git a/core/src/main/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitor.java b/core/src/main/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitor.java new file mode 100644 index 00000000000..2e626b10b68 --- /dev/null +++ b/core/src/main/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitor.java @@ -0,0 +1,194 @@ +package com.graphhopper.routing.weighting.custom; + +import com.graphhopper.json.MinMax; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EncodedValue; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.IntEncodedValue; +import org.codehaus.commons.compiler.CompileException; +import org.codehaus.janino.*; + +import java.io.StringReader; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Expression visitor for right-hand side value of limit_to or multiply_by. + */ +public class ValueExpressionVisitor implements Visitor.AtomVisitor { + + private static final Set allowedMethodParents = new HashSet<>(Arrays.asList("Math")); + private static final Set allowedMethods = new HashSet<>(Arrays.asList("sqrt")); + private final ParseResult result; + private final NameValidator variableValidator; + private String invalidMessage; + + public ValueExpressionVisitor(ParseResult result, NameValidator variableValidator) { + this.result = result; + this.variableValidator = variableValidator; + } + + // allow only methods and other identifiers (constants and encoded values) + boolean isValidIdentifier(String identifier) { + if (variableValidator.isValid(identifier)) { + if (!Character.isUpperCase(identifier.charAt(0))) + result.guessedVariables.add(identifier); + return true; + } + return false; + } + + @Override + public Boolean visitRvalue(Java.Rvalue rv) throws Exception { + if (rv instanceof Java.AmbiguousName) { + Java.AmbiguousName n = (Java.AmbiguousName) rv; + if (n.identifiers.length == 1) { + String arg = n.identifiers[0]; + // e.g. like road_class + if (isValidIdentifier(arg)) return true; + invalidMessage = "'" + arg + "' not available"; + return false; + } + invalidMessage = "identifier " + n + " invalid"; + return false; + } + if (rv instanceof Java.Literal) { + return true; + } else if (rv instanceof Java.UnaryOperation) { + Java.UnaryOperation uop = (Java.UnaryOperation) rv; + result.operators.add(uop.operator); + if (uop.operator.equals("-")) + return uop.operand.accept(this); + return false; + } else if (rv instanceof Java.MethodInvocation) { + Java.MethodInvocation mi = (Java.MethodInvocation) rv; + if (allowedMethods.contains(mi.methodName)) { + // skip methods like this.in() + if (mi.target != null) { + // edge.getDistance(), Math.sqrt(2) => check target name (edge or Math) + Java.AmbiguousName n = (Java.AmbiguousName) mi.target.toRvalue(); + if (n.identifiers.length == 2) { + if (allowedMethodParents.contains(n.identifiers[0])) { + // edge.getDistance(), Math.sqrt(x) => check target name i.e. edge or Math + if (mi.arguments.length == 0) { + result.guessedVariables.add(n.identifiers[0]); // return "edge" + return true; + } else if (mi.arguments.length == 1) { + // return "x" but verify before + return mi.arguments[0].accept(this); + } + } + // TODO unlike in ConditionalExpressionVisitor we don't support a call like road_class.ordinal() + // as this is currently unsupported in FindMinMax + } + } + } + invalidMessage = mi.methodName + " is an illegal method in a value expression"; + return false; + } else if (rv instanceof Java.ParenthesizedExpression) { + return ((Java.ParenthesizedExpression) rv).value.accept(this); + } else if (rv instanceof Java.BinaryOperation) { + Java.BinaryOperation binOp = (Java.BinaryOperation) rv; + String op = binOp.operator; + result.operators.add(op); + if (op.equals("*") || op.equals("+") || binOp.operator.equals("-")) { + return binOp.lhs.accept(this) && binOp.rhs.accept(this); + } + invalidMessage = "invalid operation '" + op + "'"; + return false; + } + return false; + } + + @Override + public Boolean visitPackage(Java.Package p) { + return false; + } + + @Override + public Boolean visitType(Java.Type t) { + return false; + } + + @Override + public Boolean visitConstructorInvocation(Java.ConstructorInvocation ci) { + return false; + } + + static ParseResult parse(String expression, NameValidator variableValidator) { + ParseResult result = new ParseResult(); + try { + Parser parser = new Parser(new Scanner("ignore", new StringReader(expression))); + Java.Atom atom = parser.parseConditionalExpression(); + if (parser.peek().type == TokenType.END_OF_INPUT) { + result.guessedVariables = new LinkedHashSet<>(); + result.operators = new LinkedHashSet<>(); + ValueExpressionVisitor visitor = new ValueExpressionVisitor(result, variableValidator); + result.ok = atom.accept(visitor); + result.invalidMessage = visitor.invalidMessage; + } + } catch (Exception ex) { + } + return result; + } + + static MinMax findMinMax(Set createdObjects, String valueExpression, EncodedValueLookup lookup) { + ParseResult result = parse(valueExpression, lookup::hasEncodedValue); + if (!result.ok) + throw new IllegalArgumentException(result.invalidMessage); + if (result.guessedVariables.size() > 1) + throw new IllegalArgumentException("Currently only a single EncodedValue is allowed on the right-hand side, but was " + result.guessedVariables.size() + ". Value expression: " + valueExpression); + + try { + // Speed optimization for numbers only as its over 200x faster than ExpressionEvaluator+cook+evaluate! + // We still call the parse() method before as it is only ~3x slower and might increase security slightly. Because certain + // expressions are accepted from Double.parseDouble but parse() rejects them. With this call order we avoid unexpected security problems. + double val = Double.parseDouble(valueExpression); + return new MinMax(val, val); + } catch (NumberFormatException ex) { + } + + try { + if (result.guessedVariables.isEmpty()) { // without encoded values + ExpressionEvaluator ee = new ExpressionEvaluator(); + ee.cook(valueExpression); + double val = ((Number) ee.evaluate()).doubleValue(); + return new MinMax(val, val); + } + + createdObjects.addAll(result.guessedVariables); + if (lookup.hasEncodedValue(valueExpression)) { // speed up for common case that complete right-hand side is the encoded value + EncodedValue enc = lookup.getEncodedValue(valueExpression, EncodedValue.class); + double min = getMin(enc), max = getMax(enc); + return new MinMax(min, max); + } + + ExpressionEvaluator ee = new ExpressionEvaluator(); + String var = result.guessedVariables.iterator().next(); + ee.setParameters(new String[]{var}, new Class[]{double.class}); + ee.cook(valueExpression); + double max = getMax(lookup.getEncodedValue(var, EncodedValue.class)); + Number val1 = (Number) ee.evaluate(max); + double min = getMin(lookup.getEncodedValue(var, EncodedValue.class)); + Number val2 = (Number) ee.evaluate(min); + return new MinMax(Math.min(val1.doubleValue(), val2.doubleValue()), Math.max(val1.doubleValue(), val2.doubleValue())); + } catch (CompileException | InvocationTargetException ex) { + throw new IllegalArgumentException(ex); + } + } + + static double getMin(EncodedValue enc) { + if (enc instanceof DecimalEncodedValue) return ((DecimalEncodedValue) enc).getMinStorableDecimal(); + else if (enc instanceof IntEncodedValue) return ((IntEncodedValue) enc).getMinStorableInt(); + throw new IllegalArgumentException("Cannot use non-number data '" + enc.getName() + "' in value expression"); + } + + static double getMax(EncodedValue enc) { + if (enc instanceof DecimalEncodedValue) return ((DecimalEncodedValue) enc).getMaxStorableDecimal(); + else if (enc instanceof IntEncodedValue) return ((IntEncodedValue) enc).getMaxStorableInt(); + throw new IllegalArgumentException("Cannot use non-number data '" + enc.getName() + "' in value expression"); + } +} diff --git a/core/src/main/java/com/graphhopper/search/KVStorage.java b/core/src/main/java/com/graphhopper/search/KVStorage.java new file mode 100644 index 00000000000..3e7a4060105 --- /dev/null +++ b/core/src/main/java/com/graphhopper/search/KVStorage.java @@ -0,0 +1,543 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.search; + +import com.graphhopper.storage.DataAccess; +import com.graphhopper.storage.Directory; +import com.graphhopper.util.BitUtil; +import com.graphhopper.util.Constants; +import com.graphhopper.util.GHUtility; +import com.graphhopper.util.Helper; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * This class stores key-value pairs in an append-only manner. + * + * @author Peter Karich + */ +public class KVStorage { + + private static final long EMPTY_POINTER = 0, START_POINTER = 1; + // Store the key index in 2 bytes. Use first 2 bits for marking fwd+bwd existence. + static final int MAX_UNIQUE_KEYS = (1 << 14); + // Store string value as byte array and store the length into 1 byte + private static final int MAX_LENGTH = (1 << 8) - 1; + + private final Directory dir; + // It stores the mapping of "key to index" in the keys DataAccess. E.g. if your first key is "some" then we will + // store the mapping "1->some" there (the 0th index is skipped on purpose). As this map is 'small' the keys + // DataAccess is only used for long term storage, i.e. only in loadExisting and flush. For add and getAll we use + // keyToIndex, indexToClass and indexToClass. + private final DataAccess keys; + + // The storage layout in the vals DataAccess for one Map of key-value pairs. For example the map: + // map = new HashMap(); map.put("some", "value"); map.put("some2", "value2"); is added via the method add, then we store: + // 2 (the size of the Map, 1 byte) + // --- now the first key-value pair: + // 1 (the keys index for "some", 2 byte) + // 4 (the length of the bytes from "some") + // "some" (the bytes from "some") + // --- second key-value pair: + // 2 (the keys index for "some2") + // 5 (the length of the bytes from "some2") + // "some2" (the bytes from "some2") + + // So more generic: the values could be of dynamic length, fixed length like int or be duplicates: + // vals count (1 byte) + // --- 1. key-value pair (store String or byte[] with dynamic length) + // key_idx_0 (2 byte, of which the first 2bits are to know if this is valid for fwd and/or bwd direction) + // val_length_0 (1 byte) + // val_0 (x bytes) + // --- 2. key-value pair (store int with fixed length) + // key_idx_1 (2 byte) + // int (4 byte) + // + // Notes: + // 1. The key strings are limited MAX_UNIQUE_KEYS. A dynamic value has a maximum byte length of 255. + // 2. Every key can store values only of the same type + // 3. We need to loop through X entries to get the start val_x. + // 4. The key index (14 bits) is stored along with the availability (2 bits), i.e. whether they KeyValue is available in forward and/or backward directions + private final DataAccess vals; + private final Map keyToIndex = new HashMap<>(); + private final List> indexToClass = new ArrayList<>(); + private final List indexToKey = new ArrayList<>(); + private final BitUtil bitUtil = BitUtil.LITTLE; + private long bytePointer = START_POINTER; + private long lastEntryPointer = -1; + private List lastEntries; + + /** + * Specify a larger cacheSize to reduce disk usage. Note that this increases the memory usage of this object. + */ + public KVStorage(Directory dir, boolean edge) { + this.dir = dir; + if (edge) { + this.keys = dir.create("edgekv_keys", 10 * 1024); + this.vals = dir.create("edgekv_vals"); + } else { + this.keys = dir.create("nodekv_keys", 10 * 1024); + this.vals = dir.create("nodekv_vals"); + } + } + + public KVStorage create(long initBytes) { + keys.create(initBytes); + vals.create(initBytes); + // add special empty case to have a reliable duplicate detection via negative keyIndex + keyToIndex.put("", 0); + indexToKey.add(""); + indexToClass.add(String.class); + return this; + } + + public boolean loadExisting() { + if (vals.loadExisting()) { + if (!keys.loadExisting()) throw new IllegalStateException("Loaded values but cannot load keys"); + bytePointer = bitUtil.combineIntsToLong(vals.getHeader(0), vals.getHeader(4)); + GHUtility.checkDAVersion(vals.getName(), Constants.VERSION_KV_STORAGE, vals.getHeader(8)); + GHUtility.checkDAVersion(keys.getName(), Constants.VERSION_KV_STORAGE, keys.getHeader(0)); + + // load keys into memory + int count = keys.getShort(0); + long keyBytePointer = 2; + for (int i = 0; i < count; i++) { + int keyLength = keys.getShort(keyBytePointer); + keyBytePointer += 2; + byte[] keyBytes = new byte[keyLength]; + keys.getBytes(keyBytePointer, keyBytes, keyLength); + String valueStr = new String(keyBytes, Helper.UTF_CS); + keyBytePointer += keyLength; + + keyToIndex.put(valueStr, keyToIndex.size()); + indexToKey.add(valueStr); + + int shortClassNameLength = 1; + byte[] classBytes = new byte[shortClassNameLength]; + keys.getBytes(keyBytePointer, classBytes, shortClassNameLength); + keyBytePointer += shortClassNameLength; + indexToClass.add(shortNameToClass(new String(classBytes, Helper.UTF_CS))); + } + return true; + } + + return false; + } + + Collection getKeys() { + return indexToKey; + } + + private long setKVList(long currentPointer, final List entries) { + if (currentPointer == EMPTY_POINTER) return currentPointer; + currentPointer += 1; // skip stored count + for (KeyValue entry : entries) { + String key = entry.key; + if (key == null) throw new IllegalArgumentException("key cannot be null"); + Object value = entry.value; + if (value == null) throw new IllegalArgumentException("value for key " + key + " cannot be null"); + if (!entry.fwd && !entry.bwd) + throw new IllegalArgumentException("Do not add KeyValue pair where fwd and bwd is false"); + Integer keyIndex = keyToIndex.get(key); + Class clazz; + if (keyIndex == null) { + keyIndex = keyToIndex.size(); + if (keyIndex >= MAX_UNIQUE_KEYS) + throw new IllegalArgumentException("Cannot store more than " + MAX_UNIQUE_KEYS + " unique keys"); + keyToIndex.put(key, keyIndex); + indexToKey.add(key); + indexToClass.add(clazz = value.getClass()); + } else { + clazz = indexToClass.get(keyIndex); + if (clazz != value.getClass()) + throw new IllegalArgumentException("Class of value for key " + key + " must be " + clazz.getSimpleName() + " but was " + value.getClass().getSimpleName()); + } + + boolean hasDynLength = hasDynLength(clazz); + if (hasDynLength) { + // optimization for empty string or empty byte array + if (clazz.equals(String.class) && ((String) value).isEmpty() + || clazz.equals(byte[].class) && ((byte[]) value).length == 0) { + vals.ensureCapacity(currentPointer + 3); + vals.setShort(currentPointer, keyIndex.shortValue()); + // ensure that also in case of MMap value is set to 0 + vals.setByte(currentPointer + 2, (byte) 0); + currentPointer += 3; + continue; + } + } + + final byte[] valueBytes = getBytesForValue(clazz, value); + vals.ensureCapacity(currentPointer + 2 + 1 + valueBytes.length); + vals.setShort(currentPointer, (short) (keyIndex << 2 | (entry.fwd ? 2 : 0) | (entry.bwd ? 1 : 0))); + currentPointer += 2; + if (hasDynLength) { + vals.setByte(currentPointer, (byte) valueBytes.length); + currentPointer++; + } + vals.setBytes(currentPointer, valueBytes, valueBytes.length); + currentPointer += valueBytes.length; + } + return currentPointer; + } + + /** + * This method writes the specified entryMap (key-value pairs) into the storage. Please note that null keys or null + * values are rejected. The Class of a value can be only: byte[], String, int, long, float or double + * (or more precisely, their wrapper equivalent). For all other types an exception is thrown. The first call of add + * assigns a Class to every key in the Map and future calls of add will throw an exception if this Class differs. + * + * @return entryPointer with which you can later fetch the entryMap via the get or getAll method + */ + public long add(final List entries) { + if (entries == null) throw new IllegalArgumentException("specified List must not be null"); + if (entries.isEmpty()) return EMPTY_POINTER; + else if (entries.size() > 200) + throw new IllegalArgumentException("Cannot store more than 200 entries per entry"); + + // This is a very important "compression" mechanism because one OSM way is split into multiple edges and so we + // can often re-use the serialized key-value pairs of the previous edge. + if (isEquals(entries, lastEntries)) return lastEntryPointer; + + // If the Class of a value is unknown it should already fail here, before we modify internal data. (see #2597#discussion_r896469840) + for (KeyValue kv : entries) + if (keyToIndex.get(kv.key) != null) + getBytesForValue(indexToClass.get(keyToIndex.get(kv.key)), kv.value); + + lastEntries = entries; + lastEntryPointer = bytePointer; + vals.ensureCapacity(bytePointer + 1); + vals.setByte(bytePointer, (byte) entries.size()); + bytePointer = setKVList(bytePointer, entries); + if (bytePointer < 0) + throw new IllegalStateException("Negative bytePointer in KVStorage"); + return lastEntryPointer; + } + + // compared to entries.equals(lastEntries) this method avoids a NPE if a value is null and throws an IAE instead + private boolean isEquals(List entries, List lastEntries) { + if (lastEntries != null && entries.size() == lastEntries.size()) { + for (int i = 0; i < entries.size(); i++) { + KeyValue kv = entries.get(i); + if (kv.value == null) + throw new IllegalArgumentException("value for key " + kv.key + " cannot be null"); + if (!kv.equals(lastEntries.get(i))) return false; + } + return true; + } + return false; + } + + public List getAll(final long entryPointer) { + if (entryPointer < 0) + throw new IllegalStateException("Pointer to access KVStorage cannot be negative:" + entryPointer); + + if (entryPointer == EMPTY_POINTER) return Collections.emptyList(); + + int keyCount = vals.getByte(entryPointer) & 0xFF; + if (keyCount == 0) return Collections.emptyList(); + + List list = new ArrayList<>(keyCount); + long tmpPointer = entryPointer + 1; + AtomicInteger sizeOfObject = new AtomicInteger(); + for (int i = 0; i < keyCount; i++) { + int currentKeyIndexRaw = vals.getShort(tmpPointer); + boolean bwd = (currentKeyIndexRaw & 1) == 1; + boolean fwd = (currentKeyIndexRaw & 2) == 2; + int currentKeyIndex = currentKeyIndexRaw >>> 2; + tmpPointer += 2; + + Object object = deserializeObj(sizeOfObject, tmpPointer, indexToClass.get(currentKeyIndex)); + tmpPointer += sizeOfObject.get(); + String key = indexToKey.get(currentKeyIndex); + list.add(new KeyValue(key, object, fwd, bwd)); + } + + return list; + } + + /** + * Please note that this method ignores potentially different tags for forward and backward direction. To avoid this + * use {@link #getAll(long)} instead. + */ + public Map getMap(final long entryPointer) { + if (entryPointer < 0) + throw new IllegalStateException("Pointer to access KVStorage cannot be negative:" + entryPointer); + + if (entryPointer == EMPTY_POINTER) return Collections.emptyMap(); + + int keyCount = vals.getByte(entryPointer) & 0xFF; + if (keyCount == 0) return Collections.emptyMap(); + + HashMap map = new HashMap<>(keyCount); + long tmpPointer = entryPointer + 1; + AtomicInteger sizeOfObject = new AtomicInteger(); + for (int i = 0; i < keyCount; i++) { + int currentKeyIndexRaw = vals.getShort(tmpPointer); + int currentKeyIndex = currentKeyIndexRaw >>> 2; + tmpPointer += 2; + + Object object = deserializeObj(sizeOfObject, tmpPointer, indexToClass.get(currentKeyIndex)); + tmpPointer += sizeOfObject.get(); + String key = indexToKey.get(currentKeyIndex); + map.put(key, object); + } + + return map; + } + + private boolean hasDynLength(Class clazz) { + return clazz.equals(String.class) || clazz.equals(byte[].class); + } + + private int getFixLength(Class clazz) { + if (clazz.equals(Integer.class) || clazz.equals(Float.class)) return 4; + else if (clazz.equals(Long.class) || clazz.equals(Double.class)) return 8; + else throw new IllegalArgumentException("unknown class " + clazz); + } + + private byte[] getBytesForValue(Class clazz, Object value) { + byte[] bytes; + if (clazz.equals(String.class)) { + bytes = ((String) value).getBytes(Helper.UTF_CS); + if (bytes.length > MAX_LENGTH) + throw new IllegalArgumentException("bytes.length cannot be > " + MAX_LENGTH + " but was " + bytes.length + ". String:" + value); + } else if (clazz.equals(byte[].class)) { + bytes = (byte[]) value; + if (bytes.length > MAX_LENGTH) + throw new IllegalArgumentException("bytes.length cannot be > " + MAX_LENGTH + " but was " + bytes.length); + } else if (clazz.equals(Integer.class)) { + return bitUtil.fromInt((int) value); + } else if (clazz.equals(Long.class)) { + return bitUtil.fromLong((long) value); + } else if (clazz.equals(Float.class)) { + return bitUtil.fromFloat((float) value); + } else if (clazz.equals(Double.class)) { + return bitUtil.fromDouble((double) value); + } else + throw new IllegalArgumentException("The Class of a value was " + clazz.getSimpleName() + ", currently supported: byte[], String, int, long, float and double"); + return bytes; + } + + private String classToShortName(Class clazz) { + if (clazz.equals(String.class)) return "S"; + else if (clazz.equals(Integer.class)) return "i"; + else if (clazz.equals(Long.class)) return "l"; + else if (clazz.equals(Float.class)) return "f"; + else if (clazz.equals(Double.class)) return "d"; + else if (clazz.equals(byte[].class)) return "["; + else throw new IllegalArgumentException("Cannot find short name. Unknown class " + clazz); + } + + private Class shortNameToClass(String name) { + if (name.equals("S")) return String.class; + else if (name.equals("i")) return Integer.class; + else if (name.equals("l")) return Long.class; + else if (name.equals("f")) return Float.class; + else if (name.equals("d")) return Double.class; + else if (name.equals("[")) return byte[].class; + else throw new IllegalArgumentException("Cannot find class. Unknown short name " + name); + } + + /** + * This method creates an Object (type Class) which is located at the specified pointer + */ + private Object deserializeObj(AtomicInteger sizeOfObject, long pointer, Class clazz) { + if (hasDynLength(clazz)) { + int valueLength = vals.getByte(pointer) & 0xFF; + pointer++; + byte[] valueBytes = new byte[valueLength]; + vals.getBytes(pointer, valueBytes, valueBytes.length); + if (sizeOfObject != null) + sizeOfObject.set(1 + valueLength); // For String and byte[] we store the length and the value + if (clazz.equals(String.class)) return new String(valueBytes, Helper.UTF_CS); + else if (clazz.equals(byte[].class)) return valueBytes; + throw new IllegalArgumentException(); + } else { + byte[] valueBytes = new byte[getFixLength(clazz)]; + vals.getBytes(pointer, valueBytes, valueBytes.length); + if (clazz.equals(Integer.class)) { + if (sizeOfObject != null) sizeOfObject.set(4); + return bitUtil.toInt(valueBytes, 0); + } else if (clazz.equals(Long.class)) { + if (sizeOfObject != null) sizeOfObject.set(8); + return bitUtil.toLong(valueBytes, 0); + } else if (clazz.equals(Float.class)) { + if (sizeOfObject != null) sizeOfObject.set(4); + return bitUtil.toFloat(valueBytes, 0); + } else if (clazz.equals(Double.class)) { + if (sizeOfObject != null) sizeOfObject.set(8); + return bitUtil.toDouble(valueBytes, 0); + } else { + throw new IllegalArgumentException("unknown class " + clazz); + } + } + } + + public Object get(final long entryPointer, String key, boolean reverse) { + if (entryPointer < 0) + throw new IllegalStateException("Pointer to access KVStorage cannot be negative:" + entryPointer); + + if (entryPointer == EMPTY_POINTER) return null; + + Integer keyIndex = keyToIndex.get(key); + if (keyIndex == null) return null; // key wasn't stored before + + int keyCount = vals.getByte(entryPointer) & 0xFF; + if (keyCount == 0) return null; // no entries + + long tmpPointer = entryPointer + 1; + for (int i = 0; i < keyCount; i++) { + int currentKeyIndexRaw = vals.getShort(tmpPointer); + boolean bwd = (currentKeyIndexRaw & 1) == 1; + boolean fwd = (currentKeyIndexRaw & 2) == 2; + int currentKeyIndex = currentKeyIndexRaw >>> 2; + + assert currentKeyIndex < indexToKey.size() : "invalid key index " + currentKeyIndex + ">=" + indexToKey.size() + ", entryPointer=" + entryPointer + ", max=" + bytePointer; + tmpPointer += 2; + if ((!reverse && fwd || reverse && bwd) && currentKeyIndex == keyIndex) + return deserializeObj(null, tmpPointer, indexToClass.get(keyIndex)); + + // skip to next entry of same edge via skipping the real value + Class clazz = indexToClass.get(currentKeyIndex); + int valueLength = hasDynLength(clazz) ? 1 + vals.getByte(tmpPointer) & 0xFF : getFixLength(clazz); + tmpPointer += valueLength; + } + + // value for specified key does not exist for the specified pointer + return null; + } + + public void flush() { + keys.ensureCapacity(2); + keys.setShort(0, (short) keyToIndex.size()); + long keyBytePointer = 2; + for (int i = 0; i < indexToKey.size(); i++) { + String key = indexToKey.get(i); + byte[] keyBytes = getBytesForValue(String.class, key); + keys.ensureCapacity(keyBytePointer + 2 + keyBytes.length); + keys.setShort(keyBytePointer, (short) keyBytes.length); + keyBytePointer += 2; + + keys.setBytes(keyBytePointer, keyBytes, keyBytes.length); + keyBytePointer += keyBytes.length; + + Class clazz = indexToClass.get(i); + byte[] clazzBytes = getBytesForValue(String.class, classToShortName(clazz)); + if (clazzBytes.length != 1) + throw new IllegalArgumentException("class name byte length must be 1 but was " + clazzBytes.length); + keys.ensureCapacity(keyBytePointer + 1); + keys.setBytes(keyBytePointer, clazzBytes, 1); + keyBytePointer += 1; + } + keys.setHeader(0, Constants.VERSION_KV_STORAGE); + keys.flush(); + + vals.setHeader(0, bitUtil.getIntLow(bytePointer)); + vals.setHeader(4, bitUtil.getIntHigh(bytePointer)); + vals.setHeader(8, Constants.VERSION_KV_STORAGE); + vals.flush(); + } + + public void clear() { + dir.remove(keys.getName()); + dir.remove(vals.getName()); + } + + public void close() { + keys.close(); + vals.close(); + } + + public boolean isClosed() { + return vals.isClosed() && keys.isClosed(); + } + + public long getCapacity() { + return vals.getCapacity() + keys.getCapacity(); + } + + public static class KeyValue { + public static final String STREET_NAME = "street_name"; + public static final String STREET_REF = "street_ref"; + public static final String STREET_DESTINATION = "street_destination"; + public static final String STREET_DESTINATION_REF = "street_destination_ref"; + + public String key; + public Object value; + public boolean fwd, bwd; + + public KeyValue(String key, Object value) { + this.key = key; + this.value = value; + this.fwd = true; + this.bwd = true; + } + + public Object getValue() { + return value; + } + + public String getKey() { + return key; + } + + public KeyValue(String key, Object value, boolean fwd, boolean bwd) { + this.key = key; + this.value = value; + this.fwd = fwd; + this.bwd = bwd; + } + + public static List createKV(String key, Object value) { + return Collections.singletonList(new KeyValue(key, value)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + KeyValue keyValue = (KeyValue) o; + return key.equals(keyValue.key) + && fwd == keyValue.fwd + && bwd == keyValue.bwd + && (value instanceof byte[] && keyValue.value instanceof byte[] && + Arrays.equals((byte[]) value, (byte[]) keyValue.value) || value.equals(keyValue.value)); + } + + @Override + public int hashCode() { + return Objects.hash(key, value, fwd, bwd); + } + + @Override + public String toString() { + return key + '=' + value + " (" + fwd + "|" + bwd + ")"; + } + } + + /** + * This method limits the specified String value to the length currently accepted for values in the KVStorage. + */ + public static String cutString(String value) { + byte[] bytes = value.getBytes(Helper.UTF_CS); + // See #2609 and test why we use a value < 255 + return bytes.length > 250 ? new String(bytes, 0, 250, Helper.UTF_CS) : value; + } +} diff --git a/core/src/main/java/com/graphhopper/search/StringIndex.java b/core/src/main/java/com/graphhopper/search/StringIndex.java deleted file mode 100644 index 1b5a720285d..00000000000 --- a/core/src/main/java/com/graphhopper/search/StringIndex.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.search; - -import com.graphhopper.storage.DataAccess; -import com.graphhopper.storage.Directory; -import com.graphhopper.util.BitUtil; -import com.graphhopper.util.Constants; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.Helper; - -import java.util.*; - -/** - * @author Peter Karich - */ -public class StringIndex { - private static final long EMPTY_POINTER = 0, START_POINTER = 1; - // Store the key index in 2 bytes. Use negative values for marking the value as duplicate. - static final int MAX_UNIQUE_KEYS = (1 << 15); - // Store string value as byte array and store the length into 1 byte - private static final int MAX_LENGTH = (1 << 8) - 1; - boolean throwExceptionIfTooLong = false; - private final DataAccess keys; - // storage layout per entry: - // 1 byte | 2 bytes | 1 byte | x | 2 bytes | 1 byte | x | 2 bytes (dup example) | 4 bytes | ... - // vals count| key_idx_0| val_length_0| val_0| key_idx_1| val_length_1| val_1| -key_idx_2 | delta_2 | key_idx_3 | val_length_3 | val_3 - // Drawback: we need to loop through the entries to get the start of val_x. - // Note, that we detect duplicate values via smallCache and then use the negative key index as 'duplicate' marker. - // We then store only the delta (signed int) instead the absolute unsigned long value to reduce memory usage when duplicate entries. - private final DataAccess vals; - // array.indexOf could be faster than hashmap.get if not too many keys or even sort keys and use binarySearch - private final Map keysInMem = new LinkedHashMap<>(); - private final List keyList = new ArrayList<>(); - private final Map smallCache; - private long bytePointer = START_POINTER; - private long lastEntryPointer = -1; - private Map lastEntryMap; - - /** - * Specify a larger cacheSize to reduce disk usage. Note that this increases the memory usage of this object. - */ - public StringIndex(Directory dir, final int cacheSize, final int segmentSize) { - keys = dir.create("string_index_keys", segmentSize); - vals = dir.create("string_index_vals", segmentSize); - smallCache = new LinkedHashMap(cacheSize, 0.75f, true) { - @Override - protected boolean removeEldestEntry(Map.Entry entry) { - return size() > cacheSize; - } - }; - } - - public StringIndex create(long initBytes) { - keys.create(initBytes); - vals.create(initBytes); - // add special empty case to have a reliable duplicate detection via negative keyIndex - keysInMem.put("", 0); - keyList.add(""); - return this; - } - - public boolean loadExisting() { - if (vals.loadExisting()) { - if (!keys.loadExisting()) - throw new IllegalStateException("Loaded values but cannot load keys"); - int stringIndexKeysVersion = keys.getHeader(0); - int stringIndexValsVersion = vals.getHeader(0); - GHUtility.checkDAVersion(keys.getName(), Constants.VERSION_STRING_IDX, stringIndexKeysVersion); - GHUtility.checkDAVersion(vals.getName(), Constants.VERSION_STRING_IDX, stringIndexValsVersion); - bytePointer = BitUtil.LITTLE.combineIntsToLong(vals.getHeader(4), vals.getHeader(8)); - - // load keys into memory - int count = keys.getShort(0); - long keyBytePointer = 2; - for (int i = 0; i < count; i++) { - int keyLength = keys.getShort(keyBytePointer); - keyBytePointer += 2; - - byte[] keyBytes = new byte[keyLength]; - keys.getBytes(keyBytePointer, keyBytes, keyLength); - String valueStr = new String(keyBytes, Helper.UTF_CS); - keysInMem.put(valueStr, keysInMem.size()); - keyList.add(valueStr); - keyBytePointer += keyLength; - } - return true; - } - - return false; - } - - Set getKeys() { - return keysInMem.keySet(); - } - - /** - * This method writes the specified key-value pairs into the storage. - * - * @return entryPointer to later fetch the entryMap via get - */ - public long add(Map entryMap) { - if (entryMap.isEmpty()) - return EMPTY_POINTER; - else if (entryMap.size() > 200) - throw new IllegalArgumentException("Cannot store more than 200 entries per entry"); - - // This is a very important compressing mechanism due to the nature of OSM. Make this faster via precalculated hastcodes? - if (entryMap.equals(lastEntryMap)) - return lastEntryPointer; - - lastEntryMap = entryMap; - lastEntryPointer = bytePointer; - // while adding there could be exceptions and we need to avoid that the bytePointer is modified - long currentPointer = bytePointer; - - vals.ensureCapacity(currentPointer + 1); - vals.setByte(currentPointer, (byte) entryMap.size()); - currentPointer += 1; - for (Map.Entry entry : entryMap.entrySet()) { - String key = entry.getKey(), value = entry.getValue(); - Integer keyIndex = keysInMem.get(key); - if (keyIndex == null) { - keyIndex = keysInMem.size(); - if (keyIndex >= MAX_UNIQUE_KEYS) - throw new IllegalArgumentException("Cannot store more than " + MAX_UNIQUE_KEYS + " unique keys"); - keysInMem.put(key, keyIndex); - keyList.add(key); - } - - if (value == null || value.isEmpty()) { - vals.ensureCapacity(currentPointer + 3); - vals.setShort(currentPointer, keyIndex.shortValue()); - // ensure that also in case of MMap value is set to 0 - vals.setByte(currentPointer + 2, (byte) 0); - currentPointer += 3; - } else { - Long existingRef = smallCache.get(value); - if (existingRef != null) { - long delta = lastEntryPointer - existingRef; - if (delta < Integer.MAX_VALUE && delta > Integer.MIN_VALUE) { - vals.ensureCapacity(currentPointer + 2 + 4); - vals.setShort(currentPointer, (short) -keyIndex); - currentPointer += 2; - // do not store valueBytes.length as we know it already: it is 4! - byte[] valueBytes = new byte[4]; - BitUtil.LITTLE.fromInt(valueBytes, (int) delta); - vals.setBytes(currentPointer, valueBytes, valueBytes.length); - currentPointer += valueBytes.length; - continue; - } else { - smallCache.remove(value); - } - } - - byte[] valueBytes = getBytesForString("Value for key" + key, value); - // only cache value if storing via duplicate marker is valuable (the delta costs 4 bytes minus 1 due to omitted valueBytes.length storage) - if (valueBytes.length > 3) - smallCache.put(value, currentPointer); - - vals.ensureCapacity(currentPointer + 2 + 1 + valueBytes.length); - vals.setShort(currentPointer, keyIndex.shortValue()); - currentPointer += 2; - vals.setByte(currentPointer, (byte) valueBytes.length); - currentPointer++; - vals.setBytes(currentPointer, valueBytes, valueBytes.length); - currentPointer += valueBytes.length; - } - } - bytePointer = currentPointer; - return lastEntryPointer; - } - - public Map getAll(final long entryPointer) { - if (entryPointer < 0) - throw new IllegalStateException("Pointer to access StringIndex cannot be negative:" + entryPointer); - - if (entryPointer == EMPTY_POINTER) - return Collections.emptyMap(); - - int keyCount = vals.getByte(entryPointer) & 0xFF; - if (keyCount == 0) - return Collections.emptyMap(); - - Map map = new LinkedHashMap<>(keyCount); - long tmpPointer = entryPointer + 1; - for (int i = 0; i < keyCount; i++) { - int currentKeyIndex = vals.getShort(tmpPointer); - tmpPointer += 2; - - if (currentKeyIndex < 0) { - currentKeyIndex = -currentKeyIndex; - byte[] valueBytes = new byte[4]; - vals.getBytes(tmpPointer, valueBytes, valueBytes.length); - tmpPointer += 4; - - long dupPointer = entryPointer - BitUtil.LITTLE.toInt(valueBytes); - dupPointer += 2; - if (dupPointer > bytePointer) - throw new IllegalStateException("dup marker should exist but points into not yet allocated area " + dupPointer + " > " + bytePointer); - putIntoMap(map, dupPointer, currentKeyIndex); - } else { - int valueLength = putIntoMap(map, tmpPointer, currentKeyIndex); - tmpPointer += 1 + valueLength; - } - } - - // value for specified key does not existing for the specified pointer - return map; - } - - private int putIntoMap(Map map, long tmpPointer, int currentKeyIndex) { - int valueLength = vals.getByte(tmpPointer) & 0xFF; - tmpPointer++; - String valueStr; - if (valueLength == 0) { - valueStr = ""; - } else { - byte[] valueBytes = new byte[valueLength]; - vals.getBytes(tmpPointer, valueBytes, valueBytes.length); - valueStr = new String(valueBytes, Helper.UTF_CS); - } - - map.put(keyList.get(currentKeyIndex), valueStr); - return valueLength; - } - - public String get(final long entryPointer, String key) { - if (entryPointer < 0) - throw new IllegalStateException("Pointer to access StringIndex cannot be negative:" + entryPointer); - - if (entryPointer == EMPTY_POINTER) - return ""; - - int keyCount = vals.getByte(entryPointer) & 0xFF; - if (keyCount == 0) - return null; - - Integer keyIndex = keysInMem.get(key); - // specified key is not known to the StringIndex - if (keyIndex == null) - return null; - - long tmpPointer = entryPointer + 1; - for (int i = 0; i < keyCount; i++) { - int currentKeyIndex = vals.getShort(tmpPointer); - tmpPointer += 2; - if (Math.abs(currentKeyIndex) == keyIndex) { - if (currentKeyIndex < 0) { - byte[] valueBytes = new byte[4]; - vals.getBytes(tmpPointer, valueBytes, valueBytes.length); - tmpPointer = entryPointer - BitUtil.LITTLE.toInt(valueBytes); - tmpPointer += 2; - if (tmpPointer > bytePointer) - throw new IllegalStateException("dup marker " + bytePointer + " should exist but points into not yet allocated area " + tmpPointer); - } - - int valueLength = vals.getByte(tmpPointer) & 0xFF; - if (valueLength == 0) - return ""; - - tmpPointer++; - byte[] valueBytes = new byte[valueLength]; - vals.getBytes(tmpPointer, valueBytes, valueBytes.length); - return new String(valueBytes, Helper.UTF_CS); - } - int valueLength = vals.getByte(tmpPointer) & 0xFF; - tmpPointer += 1 + valueLength; - } - - // value for specified key does not exist for the specified pointer - return null; - } - - private byte[] getBytesForString(String info, String name) { - byte[] bytes = name.getBytes(Helper.UTF_CS); - if (bytes.length > MAX_LENGTH) { - String newString = new String(bytes, 0, MAX_LENGTH, Helper.UTF_CS); - if (throwExceptionIfTooLong) - throw new IllegalStateException(info + " is too long: " + name + " truncated to " + newString); - return newString.getBytes(Helper.UTF_CS); - } - - return bytes; - } - - public void flush() { - keys.setHeader(0, Constants.VERSION_STRING_IDX); - keys.ensureCapacity(2); - keys.setShort(0, (short) keysInMem.size()); - long keyBytePointer = 2; - for (String key : keysInMem.keySet()) { - byte[] keyBytes = getBytesForString("key", key); - keys.ensureCapacity(keyBytePointer + 2 + keyBytes.length); - keys.setShort(keyBytePointer, (short) keyBytes.length); - keyBytePointer += 2; - - keys.setBytes(keyBytePointer, keyBytes, keyBytes.length); - keyBytePointer += keyBytes.length; - } - keys.flush(); - - vals.setHeader(0, Constants.VERSION_STRING_IDX); - vals.setHeader(4, BitUtil.LITTLE.getIntLow(bytePointer)); - vals.setHeader(8, BitUtil.LITTLE.getIntHigh(bytePointer)); - vals.flush(); - } - - public void close() { - keys.close(); - vals.close(); - } - - public boolean isClosed() { - return vals.isClosed() && keys.isClosed(); - } - - public long getCapacity() { - return vals.getCapacity() + keys.getCapacity(); - } - -} diff --git a/core/src/main/java/com/graphhopper/storage/AbstractDataAccess.java b/core/src/main/java/com/graphhopper/storage/AbstractDataAccess.java index 2b4f378ce91..12f6ec6b1b9 100644 --- a/core/src/main/java/com/graphhopper/storage/AbstractDataAccess.java +++ b/core/src/main/java/com/graphhopper/storage/AbstractDataAccess.java @@ -105,7 +105,7 @@ protected long readHeader(RandomAccessFile raFile) throws IOException { String versionHint = raFile.readUTF(); if (!"GH".equals(versionHint)) - throw new IllegalArgumentException("Not a GraphHopper file! Expected 'GH' as file marker but was " + versionHint); + throw new IllegalArgumentException("Not a GraphHopper file " + getFullName() + "! Expected 'GH' as file marker but was " + versionHint); long bytes = raFile.readLong(); setSegmentSize(raFile.readInt()); diff --git a/core/src/main/java/com/graphhopper/storage/BaseGraph.java b/core/src/main/java/com/graphhopper/storage/BaseGraph.java index 37159ad5c03..26984d1d8e1 100644 --- a/core/src/main/java/com/graphhopper/storage/BaseGraph.java +++ b/core/src/main/java/com/graphhopper/storage/BaseGraph.java @@ -22,12 +22,12 @@ import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.search.StringIndex; +import com.graphhopper.search.KVStorage; import com.graphhopper.util.*; import com.graphhopper.util.shapes.BBox; import java.io.Closeable; -import java.util.Collections; +import java.util.List; import static com.graphhopper.util.Helper.nf; @@ -39,14 +39,14 @@ * Note: A RAM DataAccess Object is thread-safe in itself but if used in this Graph implementation * it is not write thread safe. *

- * Life cycle: (1) object creation, (2) configuration via setters & getters, (3) create or + * Life cycle: (1) object creation, (2) configuration via setters & getters, (3) create or * loadExisting, (4) usage, (5) flush, (6) close */ public class BaseGraph implements Graph, Closeable { - private final static String STRING_IDX_NAME_KEY = "name"; + final static long MAX_UNSIGNED_INT = 0xFFFF_FFFFL; final BaseGraphNodesAndEdges store; final NodeAccess nodeAccess; - final StringIndex stringIndex; + final KVStorage edgeKVStorage; // can be null if turn costs are not supported final TurnCostStorage turnCostStorage; final BitUtil bitUtil; @@ -62,7 +62,7 @@ public BaseGraph(Directory dir, int intsForFlags, boolean withElevation, boolean this.dir = dir; this.bitUtil = BitUtil.LITTLE; this.wayGeometry = dir.create("geometry", segmentSize); - this.stringIndex = new StringIndex(dir, 1000, segmentSize); + this.edgeKVStorage = new KVStorage(dir, true); this.store = new BaseGraphNodesAndEdges(dir, intsForFlags, withElevation, withTurnCosts, segmentSize); this.nodeAccess = new GHNodeAccess(store); this.segmentSize = segmentSize; @@ -84,6 +84,10 @@ private static boolean isTestingEnabled() { return enableIfAssert; } + public void debugPrint() { + store.debugPrint(); + } + @Override public BaseGraph getBaseGraph() { return this; @@ -159,7 +163,7 @@ public BaseGraph create(long initSize) { initSize = Math.min(initSize, 2000); wayGeometry.create(initSize); - stringIndex.create(initSize); + edgeKVStorage.create(initSize); if (supportsTurnCosts()) { turnCostStorage.create(initSize); } @@ -169,23 +173,27 @@ public BaseGraph create(long initSize) { return this; } - String toDetailsString() { + public int getIntsForFlags() { + return store.getIntsForFlags(); + } + + public String toDetailsString() { return store.toDetailsString() + ", " - + "name:(" + stringIndex.getCapacity() / Helper.MB + "MB), " + + "name:(" + edgeKVStorage.getCapacity() / Helper.MB + "MB), " + "geo:" + nf(maxGeoRef) + "(" + wayGeometry.getCapacity() / Helper.MB + "MB)"; } /** - * Flush and free resources that are not needed for post-processing (way geometries and StringIndex). + * Flush and free resources that are not needed for post-processing (way geometries and KVStorage for edges). */ - void flushAndCloseGeometryAndNameStorage() { + public void flushAndCloseGeometryAndNameStorage() { setWayGeometryHeader(); wayGeometry.flush(); wayGeometry.close(); - stringIndex.flush(); - stringIndex.close(); + edgeKVStorage.flush(); + edgeKVStorage.close(); } public void flush() { @@ -194,8 +202,8 @@ public void flush() { wayGeometry.flush(); } - if (!stringIndex.isClosed()) - stringIndex.flush(); + if (!edgeKVStorage.isClosed()) + edgeKVStorage.flush(); store.flush(); if (supportsTurnCosts()) { @@ -207,8 +215,8 @@ public void flush() { public void close() { if (!wayGeometry.isClosed()) wayGeometry.close(); - if (!stringIndex.isClosed()) - stringIndex.close(); + if (!edgeKVStorage.isClosed()) + edgeKVStorage.close(); store.close(); if (supportsTurnCosts()) { turnCostStorage.close(); @@ -216,7 +224,7 @@ public void close() { } public long getCapacity() { - return store.getCapacity() + stringIndex.getCapacity() + return store.getCapacity() + edgeKVStorage.getCapacity() + wayGeometry.getCapacity() + (supportsTurnCosts() ? turnCostStorage.getCapacity() : 0); } @@ -224,23 +232,24 @@ long getMaxGeoRef() { return maxGeoRef; } - public void loadExisting() { + public boolean loadExisting() { checkNotInitialized(); if (!store.loadExisting()) - throw new IllegalStateException("Cannot load edges or nodes. corrupt file or directory? " + dir); + return false; if (!wayGeometry.loadExisting()) - throw new IllegalStateException("Cannot load geometry. corrupt file or directory? " + dir); + return false; - if (!stringIndex.loadExisting()) - throw new IllegalStateException("Cannot load name index. corrupt file or directory? " + dir); + if (!edgeKVStorage.loadExisting()) + return false; if (supportsTurnCosts() && !turnCostStorage.loadExisting()) - throw new IllegalStateException("Cannot load turn cost storage. corrupt file or directory? " + dir); + return false; setInitialized(); loadWayGeometryHeader(); + return true; } /** @@ -254,7 +263,7 @@ EdgeIteratorState copyProperties(EdgeIteratorState from, EdgeIteratorStateImpl t // copy the rest with higher level API to.setDistance(from.getDistance()). - setName(from.getName()). + setKeyValues(from.getKeyValues()). setWayGeometry(from.fetchWayGeometry(FetchMode.PILLAR_ONLY)); return to; @@ -354,6 +363,22 @@ private void setWayGeometry_(PointList pillarNodes, long edgePointer, boolean re } } + public EdgeIntAccess createEdgeIntAccess() { + return new EdgeIntAccess() { + @Override + public int getInt(int edgeId, int index) { + long edgePointer = store.toEdgePointer(edgeId); + return store.getFlagInt(edgePointer, index); + } + + @Override + public void setInt(int edgeId, int index, int value) { + long edgePointer = store.toEdgePointer(edgeId); + store.setFlagInt(edgePointer, index, value); + } + }; + } + private void setWayGeometryAtGeoRef(PointList pillarNodes, long edgePointer, boolean reverse, long geoRef) { int len = pillarNodes.size(); int dim = nodeAccess.getDimension(); @@ -459,13 +484,6 @@ static int getPointListLength(int pillarNodes, FetchMode mode) { throw new IllegalArgumentException("Mode isn't handled " + mode); } - private void setName(long edgePointer, String name) { - int stringIndexRef = (int) stringIndex.add(Collections.singletonMap(STRING_IDX_NAME_KEY, name)); - if (stringIndexRef < 0) - throw new IllegalStateException("Too many names are stored, currently limited to int pointer"); - store.setNameRef(edgePointer, stringIndexRef); - } - private void ensureGeometry(long bytePos, int byteLength) { wayGeometry.ensureCapacity(bytePos + byteLength); } @@ -473,7 +491,7 @@ private void ensureGeometry(long bytePos, int byteLength) { private long nextGeoRef(int arrayLength) { long tmp = maxGeoRef; maxGeoRef += arrayLength + 1L; - if (maxGeoRef >= 0xFFFFffffL) + if (maxGeoRef > MAX_UNSIGNED_INT) throw new IllegalStateException("Geometry too large, does not fit in 32 bits " + maxGeoRef); return tmp; @@ -553,7 +571,6 @@ protected static class EdgeIteratorImpl extends EdgeIteratorStateImpl implements public EdgeIteratorImpl(BaseGraph baseGraph, EdgeFilter filter) { super(baseGraph); - if (filter == null) throw new IllegalArgumentException("Instead null filter use EdgeFilter.ALL_EDGES"); this.filter = filter; @@ -583,7 +600,6 @@ void goToNext() { boolean baseNodeIsNodeA = baseNode == nodeA; adjNode = baseNodeIsNodeA ? store.getNodeB(edgePointer) : nodeA; reverse = !baseNodeIsNodeA; - freshFlags = false; // position to next edge nextEdgeId = baseNodeIsNodeA ? store.getLinkA(edgePointer) : store.getLinkB(edgePointer); @@ -620,7 +636,6 @@ public boolean next() { edgePointer = store.toEdgePointer(edgeId); baseNode = store.getNodeA(edgePointer); adjNode = store.getNodeB(edgePointer); - freshFlags = false; reverse = false; return true; } @@ -654,13 +669,12 @@ static class EdgeIteratorStateImpl implements EdgeIteratorState { int adjNode; // we need reverse if detach is called boolean reverse = false; - boolean freshFlags; int edgeId = -1; - private final IntsRef edgeFlags; + private final EdgeIntAccess edgeIntAccess; public EdgeIteratorStateImpl(BaseGraph baseGraph) { this.baseGraph = baseGraph; - this.edgeFlags = new IntsRef(baseGraph.store.getIntsForFlags()); + edgeIntAccess = baseGraph.createEdgeIntAccess(); store = baseGraph.store; } @@ -674,7 +688,6 @@ final boolean init(int edgeId, int expectedAdjNode) { edgePointer = store.toEdgePointer(edgeId); baseNode = store.getNodeA(edgePointer); adjNode = store.getNodeB(edgePointer); - freshFlags = false; if (expectedAdjNode == adjNode || expectedAdjNode == Integer.MIN_VALUE) { reverse = false; @@ -699,7 +712,6 @@ final void init(int edgeKey) { edgePointer = store.toEdgePointer(edgeId); baseNode = store.getNodeA(edgePointer); adjNode = store.getNodeB(edgePointer); - freshFlags = false; if (edgeKey % 2 == 0 || baseNode == adjNode) { reverse = false; @@ -734,10 +746,8 @@ public EdgeIteratorState setDistance(double dist) { @Override public IntsRef getFlags() { - if (!freshFlags) { - store.readFlags(edgePointer, edgeFlags); - freshFlags = true; - } + IntsRef edgeFlags = new IntsRef(store.getIntsForFlags()); + store.readFlags(edgePointer, edgeFlags); return edgeFlags; } @@ -745,34 +755,28 @@ public IntsRef getFlags() { public final EdgeIteratorState setFlags(IntsRef edgeFlags) { assert edgeId < store.getEdges() : "must be edge but was shortcut: " + edgeId + " >= " + store.getEdges() + ". Use setFlagsAndWeight"; store.writeFlags(edgePointer, edgeFlags); - for (int i = 0; i < edgeFlags.ints.length; i++) { - this.edgeFlags.ints[i] = edgeFlags.ints[i]; - } - freshFlags = true; return this; } @Override public boolean get(BooleanEncodedValue property) { - return property.getBool(reverse, getFlags()); + return property.getBool(reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { - property.setBool(reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setBool(reverse, edgeId, edgeIntAccess, value); return this; } @Override public boolean getReverse(BooleanEncodedValue property) { - return property.getBool(!reverse, getFlags()); + return property.getBool(!reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) { - property.setBool(!reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setBool(!reverse, edgeId, edgeIntAccess, value); return this; } @@ -780,33 +784,30 @@ public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) public EdgeIteratorState set(BooleanEncodedValue property, boolean fwd, boolean bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setBool(reverse, getFlags(), fwd); - property.setBool(!reverse, getFlags(), bwd); - store.writeFlags(edgePointer, getFlags()); + property.setBool(reverse, edgeId, edgeIntAccess, fwd); + property.setBool(!reverse, edgeId, edgeIntAccess, bwd); return this; } @Override public int get(IntEncodedValue property) { - return property.getInt(reverse, getFlags()); + return property.getInt(reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState set(IntEncodedValue property, int value) { - property.setInt(reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setInt(reverse, edgeId, edgeIntAccess, value); return this; } @Override public int getReverse(IntEncodedValue property) { - return property.getInt(!reverse, getFlags()); + return property.getInt(!reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState setReverse(IntEncodedValue property, int value) { - property.setInt(!reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setInt(!reverse, edgeId, edgeIntAccess, value); return this; } @@ -814,33 +815,30 @@ public EdgeIteratorState setReverse(IntEncodedValue property, int value) { public EdgeIteratorState set(IntEncodedValue property, int fwd, int bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setInt(reverse, getFlags(), fwd); - property.setInt(!reverse, getFlags(), bwd); - store.writeFlags(edgePointer, getFlags()); + property.setInt(reverse, edgeId, edgeIntAccess, fwd); + property.setInt(!reverse, edgeId, edgeIntAccess, bwd); return this; } @Override public double get(DecimalEncodedValue property) { - return property.getDecimal(reverse, getFlags()); + return property.getDecimal(reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState set(DecimalEncodedValue property, double value) { - property.setDecimal(reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setDecimal(reverse, edgeId, edgeIntAccess, value); return this; } @Override public double getReverse(DecimalEncodedValue property) { - return property.getDecimal(!reverse, getFlags()); + return property.getDecimal(!reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) { - property.setDecimal(!reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setDecimal(!reverse, edgeId, edgeIntAccess, value); return this; } @@ -848,33 +846,30 @@ public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) public EdgeIteratorState set(DecimalEncodedValue property, double fwd, double bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setDecimal(reverse, getFlags(), fwd); - property.setDecimal(!reverse, getFlags(), bwd); - store.writeFlags(edgePointer, getFlags()); + property.setDecimal(reverse, edgeId, edgeIntAccess, fwd); + property.setDecimal(!reverse, edgeId, edgeIntAccess, bwd); return this; } @Override public > T get(EnumEncodedValue property) { - return property.getEnum(reverse, getFlags()); + return property.getEnum(reverse, edgeId, edgeIntAccess); } @Override public > EdgeIteratorState set(EnumEncodedValue property, T value) { - property.setEnum(reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setEnum(reverse, edgeId, edgeIntAccess, value); return this; } @Override public > T getReverse(EnumEncodedValue property) { - return property.getEnum(!reverse, getFlags()); + return property.getEnum(!reverse, edgeId, edgeIntAccess); } @Override public > EdgeIteratorState setReverse(EnumEncodedValue property, T value) { - property.setEnum(!reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setEnum(!reverse, edgeId, edgeIntAccess, value); return this; } @@ -882,33 +877,30 @@ public > EdgeIteratorState setReverse(EnumEncodedValue prop public > EdgeIteratorState set(EnumEncodedValue property, T fwd, T bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setEnum(reverse, getFlags(), fwd); - property.setEnum(!reverse, getFlags(), bwd); - store.writeFlags(edgePointer, getFlags()); + property.setEnum(reverse, edgeId, edgeIntAccess, fwd); + property.setEnum(!reverse, edgeId, edgeIntAccess, bwd); return this; } @Override public String get(StringEncodedValue property) { - return property.getString(reverse, getFlags()); + return property.getString(reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState set(StringEncodedValue property, String value) { - property.setString(reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setString(reverse, edgeId, edgeIntAccess, value); return this; } @Override public String getReverse(StringEncodedValue property) { - return property.getString(!reverse, getFlags()); + return property.getString(!reverse, edgeId, edgeIntAccess); } @Override public EdgeIteratorState setReverse(StringEncodedValue property, String value) { - property.setString(!reverse, getFlags(), value); - store.writeFlags(edgePointer, getFlags()); + property.setString(!reverse, edgeId, edgeIntAccess, value); return this; } @@ -916,9 +908,8 @@ public EdgeIteratorState setReverse(StringEncodedValue property, String value) { public EdgeIteratorState set(StringEncodedValue property, String fwd, String bwd) { if (!property.isStoreTwoDirections()) throw new IllegalArgumentException("EncodedValue " + property.getName() + " supports only one direction"); - property.setString(reverse, getFlags(), fwd); - property.setString(!reverse, getFlags(), bwd); - store.writeFlags(edgePointer, getFlags()); + property.setString(reverse, edgeId, edgeIntAccess, fwd); + property.setString(!reverse, edgeId, edgeIntAccess, bwd); return this; } @@ -945,23 +936,42 @@ public int getEdge() { @Override public int getEdgeKey() { - return GHUtility.createEdgeKey(edgeId, reverse); + return GHUtility.createEdgeKey(edgeId, baseNode == adjNode, reverse); } @Override - public String getName() { - int stringIndexRef = store.getNameRef(edgePointer); - String name = baseGraph.stringIndex.get(stringIndexRef, STRING_IDX_NAME_KEY); - // preserve backward compatibility (returns null if not explicitly set) - return name == null ? "" : name; + public int getReverseEdgeKey() { + return baseNode == adjNode ? getEdgeKey() : GHUtility.reverseEdgeKey(getEdgeKey()); } @Override - public EdgeIteratorState setName(String name) { - baseGraph.setName(edgePointer, name); + public EdgeIteratorState setKeyValues(List entries) { + long pointer = baseGraph.edgeKVStorage.add(entries); + if (pointer > MAX_UNSIGNED_INT) + throw new IllegalStateException("Too many key value pairs are stored, currently limited to " + MAX_UNSIGNED_INT + " was " + pointer); + store.setKeyValuesRef(edgePointer, Helper.toSignedInt(pointer)); return this; } + @Override + public List getKeyValues() { + long kvEntryRef = Helper.toUnsignedLong(store.getKeyValuesRef(edgePointer)); + return baseGraph.edgeKVStorage.getAll(kvEntryRef); + } + + @Override + public Object getValue(String key) { + long kvEntryRef = Helper.toUnsignedLong(store.getKeyValuesRef(edgePointer)); + return baseGraph.edgeKVStorage.get(kvEntryRef, key, reverse); + } + + @Override + public String getName() { + String name = (String) getValue(KVStorage.KeyValue.STREET_NAME); + // preserve backward compatibility (returns empty string if name tag missing) + return name == null ? "" : name; + } + @Override public EdgeIteratorState detach(boolean reverseArg) { if (!EdgeIterator.Edge.isValid(edgeId)) diff --git a/core/src/main/java/com/graphhopper/storage/BaseGraphNodesAndEdges.java b/core/src/main/java/com/graphhopper/storage/BaseGraphNodesAndEdges.java index d6b04c6cdd2..22100ff69d1 100644 --- a/core/src/main/java/com/graphhopper/storage/BaseGraphNodesAndEdges.java +++ b/core/src/main/java/com/graphhopper/storage/BaseGraphNodesAndEdges.java @@ -48,7 +48,7 @@ class BaseGraphNodesAndEdges { // edges private final DataAccess edges; - private final int E_NODEA, E_NODEB, E_LINKA, E_LINKB, E_FLAGS, E_DIST, E_GEO, E_NAME; + private final int E_NODEA, E_NODEB, E_LINKA, E_LINKB, E_FLAGS, E_DIST, E_GEO, E_KV; private final int intsForFlags; private int edgeEntryBytes; private int edgeCount; @@ -86,8 +86,8 @@ public BaseGraphNodesAndEdges(Directory dir, int intsForFlags, boolean withEleva E_FLAGS = 16; E_DIST = E_FLAGS + intsForFlags * 4; E_GEO = E_DIST + 4; - E_NAME = E_GEO + 4; - edgeEntryBytes = E_NAME + 4; + E_KV = E_GEO + 4; + edgeEntryBytes = E_KV + 4; } public void create(long initSize) { @@ -243,13 +243,21 @@ public long toEdgePointer(int edge) { public void readFlags(long edgePointer, IntsRef edgeFlags) { int size = edgeFlags.ints.length; for (int i = 0; i < size; ++i) - edgeFlags.ints[i] = edges.getInt(edgePointer + E_FLAGS + i * 4); + edgeFlags.ints[i] = getFlagInt(edgePointer, i); } public void writeFlags(long edgePointer, IntsRef edgeFlags) { int size = edgeFlags.ints.length; for (int i = 0; i < size; ++i) - edges.setInt(edgePointer + E_FLAGS + i * 4, edgeFlags.ints[i]); + setFlagInt(edgePointer, i, edgeFlags.ints[i]); + } + + public int getFlagInt(long edgePointer, int index) { + return edges.getInt(edgePointer + E_FLAGS + index * 4); + } + + public void setFlagInt(long edgePointer, int index, int value) { + edges.setInt(edgePointer + E_FLAGS + index * 4, value); } public void setNodeA(long edgePointer, int nodeA) { @@ -276,8 +284,8 @@ public void setGeoRef(long edgePointer, int geoRef) { edges.setInt(edgePointer + E_GEO, geoRef); } - public void setNameRef(long edgePointer, int nameRef) { - edges.setInt(edgePointer + E_NAME, nameRef); + public void setKeyValuesRef(long edgePointer, int nameRef) { + edges.setInt(edgePointer + E_KV, nameRef); } public int getNodeA(long edgePointer) { @@ -306,8 +314,8 @@ public int getGeoRef(long edgePointer) { return edges.getInt(edgePointer + E_GEO); } - public int getNameRef(long edgePointer) { - return edges.getInt(edgePointer + E_NAME); + public int getKeyValuesRef(long edgePointer) { + return edges.getInt(edgePointer + E_KV); } public void setEdgeRef(long nodePointer, int edgeRef) { diff --git a/core/src/main/java/com/graphhopper/storage/CHStorage.java b/core/src/main/java/com/graphhopper/storage/CHStorage.java index d55eb955be0..31c4a3fc771 100644 --- a/core/src/main/java/com/graphhopper/storage/CHStorage.java +++ b/core/src/main/java/com/graphhopper/storage/CHStorage.java @@ -60,7 +60,7 @@ public class CHStorage { // shortcuts private final DataAccess shortcuts; - private final int S_NODEA, S_NODEB, S_WEIGHT, S_DISTANCE,S_TIME, S_SKIP_EDGE1, S_SKIP_EDGE2, S_ORIG_FIRST, S_ORIG_LAST; + private final int S_NODEA, S_NODEB, S_WEIGHT, S_DISTANCE, S_TIME, S_SKIP_EDGE1, S_SKIP_EDGE2, S_ORIG_KEY_FIRST, S_ORIG_KEY_LAST; private int shortcutEntryBytes; private int shortcutCount = 0; @@ -94,40 +94,38 @@ public static CHStorage fromGraph(BaseGraph baseGraph, CHConfig chConfig) { " nodeA (" + nodeAccess.getLat(s.nodeA) + "," + nodeAccess.getLon(s.nodeA) + " nodeB " + nodeAccess.getLat(s.nodeB) + "," + nodeAccess.getLon(s.nodeB)); }); - store.create(); // we use a rather small value here. this might result in more allocations later, but they should // not matter that much. if we expect a too large value the shortcuts DataAccess will end up // larger than needed, because we do not do something like trimToSize in the end. double expectedShortcuts = 0.3 * baseGraph.getEdges(); - store.init(baseGraph.getNodes(), (int) expectedShortcuts); + store.create(baseGraph.getNodes(), (int) expectedShortcuts); return store; } - public CHStorage(Directory dir, String name, int segmentSize, boolean edgeBased) { this.edgeBased = edgeBased; this.nodesCH = dir.create("nodes_ch_" + name, dir.getDefaultType("nodes_ch_" + name, true), segmentSize); this.shortcuts = dir.create("shortcuts_" + name, dir.getDefaultType("shortcuts_" + name, true), segmentSize); - // NODEA | NODEB | WEIGHT | DISTANCE | TIME | SKIP_EDGE1 | SKIP_EDGE2 | S_ORIG_FIRST | S_ORIG_LAST - S_NODEA = 0; - S_NODEB = S_NODEA + 4; - S_WEIGHT = S_NODEB + 4; - S_DISTANCE = S_WEIGHT + 4; - S_TIME = S_DISTANCE + 4; - S_SKIP_EDGE1 = S_TIME + 4; - S_SKIP_EDGE2 = S_SKIP_EDGE1 + 4; - S_ORIG_FIRST = S_SKIP_EDGE2 + (edgeBased ? 4 : 0); - S_ORIG_LAST = S_ORIG_FIRST + (edgeBased ? 4 : 0); - shortcutEntryBytes = S_ORIG_LAST + 4; + // NODEA | NODEB | WEIGHT | DISTANCE | TIME | SKIP_EDGE1 | SKIP_EDGE2 | S_ORIG_FIRST | S_ORIG_LAST + S_NODEA = 0; + S_NODEB = S_NODEA + 4; + S_WEIGHT = S_NODEB + 4; + S_DISTANCE = S_WEIGHT + 4; + S_TIME = S_DISTANCE + 4; + S_SKIP_EDGE1 = S_TIME + 4; + S_SKIP_EDGE2 = S_SKIP_EDGE1 + 4; + S_ORIG_KEY_FIRST = S_SKIP_EDGE2 + (edgeBased ? 4 : 0); + S_ORIG_KEY_LAST = S_ORIG_KEY_FIRST + (edgeBased ? 4 : 0); + shortcutEntryBytes = S_ORIG_KEY_LAST + 4; - // nodes/levels are stored consecutively using this layout: - // LEVEL | N_LAST_SC - N_LEVEL = 0; - N_LAST_SC = N_LEVEL + 4; - nodeCHEntryBytes = N_LAST_SC + 4; + // nodes/levels are stored consecutively using this layout: + // LEVEL | N_LAST_SC + N_LEVEL = 0; + N_LAST_SC = N_LEVEL + 4; + nodeCHEntryBytes = N_LAST_SC + 4; } @@ -141,30 +139,21 @@ public void setLowShortcutWeightConsumer(Consumer lowWeightSh /** * Creates a new storage. Alternatively we could load an existing one using {@link #loadExisting()}}. - */ - public void create() { - // We have to create the DataAccess here before we flush it. Otherwise we get an error when calling - // loadExisting() later, see #2384 - nodesCH.create(0); - shortcuts.create(0); - } - - /** - * Initializes the storage. The number of nodes must be given here while the expected number of shortcuts can + * The number of nodes must be given here while the expected number of shortcuts can * be given to prevent some memory allocations, but is not a requirement. When in doubt rather use a small value * so the resulting files/byte arrays won't be unnecessarily large. * todo: we could also trim down the shortcuts DataAccess when we are done adding shortcuts */ - public void init(int nodes, int expectedShortcuts) { + public void create(int nodes, int expectedShortcuts) { if (nodeCount >= 0) - throw new IllegalStateException("CHStorage can only be initialized once"); + throw new IllegalStateException("CHStorage can only be created once"); if (nodes < 0) - throw new IllegalStateException("CHStorage must be initialized with a positive number of nodes"); - nodesCH.ensureCapacity((long) nodes * nodeCHEntryBytes); + throw new IllegalStateException("CHStorage must be created with a positive number of nodes"); + nodesCH.create((long) nodes * nodeCHEntryBytes); nodeCount = nodes; - shortcuts.ensureCapacity((long) expectedShortcuts * shortcutEntryBytes); for (int node = 0; node < nodes; node++) setLastShortcut(toNodePointer(node), -1); + shortcuts.create((long) expectedShortcuts * shortcutEntryBytes); } public void flush() { @@ -217,37 +206,36 @@ public int shortcutNodeBased(int nodeA, int nodeB, int accessFlags, double weigh if (edgeBased) throw new IllegalArgumentException("Cannot add node-based shortcuts to edge-based CH"); - return shortcut(nodeA, nodeB, accessFlags, weight,0,0, skip1, skip2); + return shortcut(nodeA, nodeB, accessFlags, weight, 0, 0, skip1, skip2); } - public int shortcutEdgeBased(int nodeA, int nodeB, int accessFlags, double weight, int skip1, int skip2, int origFirst, int origLast) { + public int shortcutEdgeBased(int nodeA, int nodeB, int accessFlags, double weight, int skip1, int skip2, int origKeyFirst, int origKeyLast) { if (!edgeBased) throw new IllegalArgumentException("Cannot add edge-based shortcuts to node-based CH"); - int shortcut = shortcut(nodeA, nodeB, accessFlags, weight,0,0, skip1, skip2); - setOrigEdges(toShortcutPointer(shortcut), origFirst, origLast); + int shortcut = shortcut(nodeA, nodeB, accessFlags, weight, 0, 0, skip1, skip2); + setOrigEdgeKeys(toShortcutPointer(shortcut), origKeyFirst, origKeyLast); return shortcut; } public int shortcutNodeBased(int nodeA, int nodeB, int accessFlags, double weight, double distance, long time, - int skip1, int skip2) { + int skip1, int skip2) { if (edgeBased) throw new IllegalArgumentException("Cannot add node-based shortcuts to edge-based CH"); - return shortcut(nodeA, nodeB, accessFlags, weight, distance,time, skip1, skip2); + return shortcut(nodeA, nodeB, accessFlags, weight, distance, time, skip1, skip2); } public int shortcutEdgeBased(int nodeA, int nodeB, int accessFlags, double weight, double distance, long time, - int skip1, int skip2, int origFirst, int origLast) { + int skip1, int skip2, int origFirst, int origLast) { if (!edgeBased) throw new IllegalArgumentException("Cannot add edge-based shortcuts to node-based CH"); - - int shortcut = shortcut(nodeA, nodeB, accessFlags, weight, distance,time, skip1, skip2); - setOrigEdges(toShortcutPointer(shortcut), origFirst, origLast); + int shortcut = shortcut(nodeA, nodeB, accessFlags, weight, distance, time, skip1, skip2); + setOrigEdgeKeys(toShortcutPointer(shortcut), origFirst, origLast); return shortcut; } @@ -266,8 +254,8 @@ private int shortcut(int nodeA, int nodeB, int accessFlags, double weight, doubl int distanceInt = distanceFromDouble(distance); int timeInt = timeFromLong(time); setNodesAB(shortcutPointer, nodeA, nodeB, accessFlags); - setDistanceInt(shortcutPointer,distanceInt); - setTimeInt(shortcutPointer,timeInt); + setDistanceInt(shortcutPointer, distanceInt); + setTimeInt(shortcutPointer, timeInt); setWeightInt(shortcutPointer, weightInt); setSkippedEdges(shortcutPointer, skip1, skip2); return shortcutCount - 1; @@ -357,11 +345,11 @@ public void setSkippedEdges(long shortcutPointer, int edge1, int edge2) { shortcuts.setInt(shortcutPointer + S_SKIP_EDGE2, edge2); } - public void setOrigEdges(long shortcutPointer, int origFirst, int origLast) { + public void setOrigEdgeKeys(long shortcutPointer, int origKeyFirst, int origKeyLast) { if (!edgeBased) - throw new IllegalArgumentException("Setting orig edges is only possible for edge-based CH"); - shortcuts.setInt(shortcutPointer + S_ORIG_FIRST, origFirst); - shortcuts.setInt(shortcutPointer + S_ORIG_LAST, origLast); + throw new IllegalArgumentException("Setting orig edge keys is only possible for edge-based CH"); + shortcuts.setInt(shortcutPointer + S_ORIG_KEY_FIRST, origKeyFirst); + shortcuts.setInt(shortcutPointer + S_ORIG_KEY_LAST, origKeyLast); } public int getNodeA(long shortcutPointer) { @@ -392,14 +380,14 @@ public int getSkippedEdge2(long shortcutPointer) { return shortcuts.getInt(shortcutPointer + S_SKIP_EDGE2); } - public int getOrigEdgeFirst(long shortcutPointer) { - assert edgeBased : "orig edges are only available for edge-based CH"; - return shortcuts.getInt(shortcutPointer + S_ORIG_FIRST); + public int getOrigEdgeKeyFirst(long shortcutPointer) { + assert edgeBased : "orig edge keys are only available for edge-based CH"; + return shortcuts.getInt(shortcutPointer + S_ORIG_KEY_FIRST); } - public int getOrigEdgeLast(long shortcutPointer) { - assert edgeBased : "orig edges are only available for edge-based CH"; - return shortcuts.getInt(shortcutPointer + S_ORIG_LAST); + public int getOrigEdgeKeyLast(long shortcutPointer) { + assert edgeBased : "orig edge keys are only available for edge-based CH"; + return shortcuts.getInt(shortcutPointer + S_ORIG_KEY_LAST); } public NodeOrderingProvider getNodeOrderingProvider() { @@ -446,8 +434,8 @@ public void debugPrint() { getSkippedEdge2(ptr)); if (edgeBased) { edgeString += String.format(Locale.ROOT, formatShortcutExt, - getOrigEdgeFirst(ptr), - getOrigEdgeLast(ptr)); + getOrigEdgeKeyFirst(ptr), + getOrigEdgeKeyLast(ptr)); } System.out.println(edgeString); } @@ -538,7 +526,7 @@ public LowWeightShortcut(int nodeA, int nodeB, int shortcut, double weight, doub } /* STUART Custom Methods*/ - public double getDistance(long shortcutPointer){ + public double getDistance(long shortcutPointer) { return distanceToDouble(shortcuts.getInt(shortcutPointer + S_DISTANCE)); } @@ -556,7 +544,7 @@ private double distanceToDouble(int intDistance) { } - public long getTime(long shortcutPointer){ + public long getTime(long shortcutPointer) { return timeToLong(shortcuts.getInt(shortcutPointer + S_TIME)); } diff --git a/core/src/main/java/com/graphhopper/storage/CHStorageBuilder.java b/core/src/main/java/com/graphhopper/storage/CHStorageBuilder.java index 93d32b0e849..172feafd49d 100644 --- a/core/src/main/java/com/graphhopper/storage/CHStorageBuilder.java +++ b/core/src/main/java/com/graphhopper/storage/CHStorageBuilder.java @@ -31,9 +31,7 @@ public class CHStorageBuilder { private final CHStorage storage; public CHStorageBuilder(CHStorage chStorage) { - // todo: currently we create a GraphHopperStorage which creates a CHStorage for us, but really we should rather - // be able to create a GraphHopperStorage and build a CHStorage on top. So if anything CHStorageBuilder should - // create CHStorage, not receive it here. + // todo: maybe CHStorageBuilder should create CHStorage, not receive it here this.storage = chStorage; } @@ -74,15 +72,16 @@ public int addShortcutNodeBased(int a, int b, int accessFlags, } /** - * @param origFirst The first original edge that is skipped by this shortcut. For example for the following shortcut - * edge from x to y, which itself skips the shortcuts x->v and v->y the first original edge would - * be x->u: x->u->v->w->y - * @param origLast like origFirst, but the last orig edge, i.e w->y in above example + * @param origKeyFirst The first original edge key that is skipped by this shortcut *in the direction of the shortcut*. + * This definition assumes that edge-based shortcuts are one-directional, and they are. + * For example for the following shortcut edge from x to y: x->u->v->w->y , + * which skips the shortcuts x->v and v->y the first original edge key would be the one of the edge x->u + * @param origKeyLast like origKeyFirst, but the last orig edge key, i.e. the key of w->y in above example */ public int addShortcutEdgeBased(int a, int b, int accessFlags, double weight, int skippedEdge1, int skippedEdge2, - int origFirst, int origLast) { + int origKeyFirst, int origKeyLast) { checkNewShortcut(a, b); - int shortcut = storage.shortcutEdgeBased(a, b, accessFlags, weight, skippedEdge1, skippedEdge2, origFirst, origLast); + int shortcut = storage.shortcutEdgeBased(a, b, accessFlags, weight, skippedEdge1, skippedEdge2, origKeyFirst, origKeyLast); setLastShortcut(a, shortcut); return shortcut; } diff --git a/core/src/main/java/com/graphhopper/storage/GHNodeAccess.java b/core/src/main/java/com/graphhopper/storage/GHNodeAccess.java index 9d0d396e4a0..ace800d23d7 100644 --- a/core/src/main/java/com/graphhopper/storage/GHNodeAccess.java +++ b/core/src/main/java/com/graphhopper/storage/GHNodeAccess.java @@ -18,9 +18,6 @@ package com.graphhopper.storage; /** - * A helper class for GraphHopperStorage for its node access. - *

- * * @author Peter Karich */ class GHNodeAccess implements NodeAccess { diff --git a/core/src/main/java/com/graphhopper/storage/GraphBuilder.java b/core/src/main/java/com/graphhopper/storage/GraphBuilder.java deleted file mode 100644 index 57e39f2308c..00000000000 --- a/core/src/main/java/com/graphhopper/storage/GraphBuilder.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.storage; - -import com.graphhopper.routing.util.EncodingManager; - -/** - * Used to build {@link GraphHopperStorage} - * - * @author Peter Karich - * @author easbar - */ -public class GraphBuilder { - private final EncodingManager encodingManager; - private Directory dir = new RAMDirectory(); - private boolean elevation; - private boolean turnCosts; - private long bytes = 100; - private int segmentSize = -1; - - public static GraphBuilder start(EncodingManager encodingManager) { - return new GraphBuilder(encodingManager); - } - - public GraphBuilder(EncodingManager encodingManager) { - this.encodingManager = encodingManager; - this.turnCosts = encodingManager.needsTurnCostsSupport(); - } - - public GraphBuilder setDir(Directory dir) { - this.dir = dir; - return this; - } - - public GraphBuilder setMMap(String location) { - return setDir(new MMapDirectory(location)); - } - - public GraphBuilder setRAM() { - return setDir(new RAMDirectory()); - } - - public GraphBuilder setRAM(String location) { - return setDir(new RAMDirectory(location)); - } - - public GraphBuilder setRAM(String location, boolean store) { - return setDir(new RAMDirectory(location, store)); - } - - public GraphBuilder set3D(boolean withElevation) { - this.elevation = withElevation; - return this; - } - - public GraphBuilder withTurnCosts(boolean turnCosts) { - this.turnCosts = turnCosts; - return this; - } - - public GraphBuilder setSegmentSize(int segmentSize) { - this.segmentSize = segmentSize; - return this; - } - - /** - * Default graph is a {@link GraphHopperStorage} with an in memory directory and disabled storing on flush. - * Afterwards you'll need to call {@link GraphHopperStorage#create} to have a usable object. Better use - * {@link #create} directly. - */ - public GraphHopperStorage build() { - return new GraphHopperStorage(dir, encodingManager, elevation, turnCosts, segmentSize); - } - - /** - * Default graph is a {@link GraphHopperStorage} with an in memory directory and disabled storing on flush. - */ - public GraphHopperStorage create() { - return build().create(bytes); - } - -} diff --git a/core/src/main/java/com/graphhopper/storage/GraphEdgeIdFinder.java b/core/src/main/java/com/graphhopper/storage/GraphEdgeIdFinder.java deleted file mode 100644 index 894e6e95497..00000000000 --- a/core/src/main/java/com/graphhopper/storage/GraphEdgeIdFinder.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.storage; - -import com.carrotsearch.hppc.cursors.IntCursor; -import com.graphhopper.coll.GHIntHashSet; -import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.storage.index.LocationIndex; -import com.graphhopper.util.*; -import com.graphhopper.util.shapes.*; -import org.locationtech.jts.algorithm.RectangleLineIntersector; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import static com.graphhopper.util.shapes.BBox.toEnvelope; - -/** - * This class allows to find edges (or construct shapes) from shape filter. - * - * @author Robin Boldt - */ -public class GraphEdgeIdFinder { - - private static final int P_RADIUS = 5; - private final Graph graph; - private final LocationIndex locationIndex; - - public GraphEdgeIdFinder(Graph graph, LocationIndex locationIndex) { - this.graph = graph; - this.locationIndex = locationIndex; - } - - static double calculateArea(Shape shape) { - if (shape instanceof BBox) - return calculateArea((BBox) shape); - if (shape instanceof Polygon) - return calculateArea((Polygon) shape); - if (shape instanceof Circle) - return calculateArea((Circle) shape); - throw new IllegalStateException("Unsupported shape: " + shape); - } - - /** - * @param bBox - * @return an estimated area in m^2 using the mean value of latitudes for longitude distance - */ - static double calculateArea(BBox bBox) { - double meanLat = (bBox.maxLat + bBox.minLat) / 2; - return DistancePlaneProjection.DIST_PLANE.calcDist(meanLat, bBox.minLon, meanLat, bBox.maxLon) - // left side should be equal to right side no mean value necessary - * DistancePlaneProjection.DIST_PLANE.calcDist(bBox.minLat, bBox.minLon, bBox.maxLat, bBox.minLon); - } - - static double calculateArea(Polygon polygon) { - // for estimation use bounding box as reference: - return calculateArea(polygon.getBounds()) * polygon.envelope.getArea() / polygon.prepPolygon.getGeometry().getArea(); - } - - static double calculateArea(Circle circle) { - return Math.PI * circle.radiusInMeter * circle.radiusInMeter; - } - - /** - * This method creates an edgeIds hashset with edgeIds found inside the specified shape - */ - private GHIntHashSet findEdgesInShape(final Shape shape, EdgeFilter filter) { - GHIntHashSet edgeIds = new GHIntHashSet(); - locationIndex.query(shape.getBounds(), edgeId -> { - EdgeIteratorState edge = graph.getEdgeIteratorStateForKey(edgeId * 2); - if (filter.accept(edge) && shape.intersects(edge.fetchWayGeometry(FetchMode.ALL).makeImmutable())) - edgeIds.add(edge.getEdge()); - }); - return edgeIds; - } - - public static GraphEdgeIdFinder.BlockArea createBlockArea(Graph graph, LocationIndex locationIndex, - List points, PMap hints, EdgeFilter edgeFilter) { - String blockAreaStr = hints.getString(Parameters.Routing.BLOCK_AREA, ""); - GraphEdgeIdFinder.BlockArea blockArea = new GraphEdgeIdFinder(graph, locationIndex). - parseBlockArea(blockAreaStr, edgeFilter, hints.getDouble(Parameters.Routing.BLOCK_AREA + ".edge_id_max_area", 1000 * 1000)); - for (GHPoint p : points) { - if (blockArea.contains(p)) - throw new IllegalArgumentException("Request with " + Parameters.Routing.BLOCK_AREA + " contained query point " + p + ". This is not allowed."); - } - return blockArea; - } - - /** - * This method reads the blockAreaString and creates a Collection of Shapes or a set of found edges if area is small enough. - * - * @param useEdgeIdsUntilAreaSize until the specified area (specified in m²) use the findEdgesInShape method - */ - public BlockArea parseBlockArea(String blockAreaString, EdgeFilter filter, double useEdgeIdsUntilAreaSize) { - final String objectSeparator = ";"; - final String innerObjSep = ","; - BlockArea blockArea = new BlockArea(graph); - - // Add blocked circular areas or points - if (!blockAreaString.isEmpty()) { - String[] blockedCircularAreasArr = blockAreaString.split(objectSeparator); - for (int i = 0; i < blockedCircularAreasArr.length; i++) { - String objectAsString = blockedCircularAreasArr[i]; - String[] splittedObject = objectAsString.split(innerObjSep); - - Shape shape; - boolean point = false; - // always add the shape as we'll need this for virtual edges and for debugging. - if (splittedObject.length > 4) { - shape = Polygon.parsePoints(objectAsString); - } else if (splittedObject.length == 4) { - final BBox bbox = BBox.parseTwoPoints(objectAsString); - final RectangleLineIntersector cachedIntersector = new RectangleLineIntersector(toEnvelope(bbox)); - shape = new BBox(bbox.minLon, bbox.maxLon, bbox.minLat, bbox.maxLat) { - @Override - public boolean intersects(PointList pointList) { - return BBox.intersects(cachedIntersector, pointList); - } - }; - } else if (splittedObject.length == 3) { - double lat = Double.parseDouble(splittedObject[0]); - double lon = Double.parseDouble(splittedObject[1]); - int radius = Integer.parseInt(splittedObject[2]); - shape = new Circle(lat, lon, radius); - } else if (splittedObject.length == 2) { - double lat = Double.parseDouble(splittedObject[0]); - double lon = Double.parseDouble(splittedObject[1]); - shape = new Circle(lat, lon, P_RADIUS); - point = true; - } else { - throw new IllegalArgumentException(objectAsString + " at index " + i + " need to be defined as lat,lon " - + "or as a circle lat,lon,radius or rectangular lat1,lon1,lat2,lon2"); - } - - - if (point || calculateArea(shape) <= useEdgeIdsUntilAreaSize) { - GHIntHashSet blockedEdges = findEdgesInShape(shape, filter); - if (!blockedEdges.isEmpty()) { - blockArea.add(shape, blockedEdges); - } - } else { - blockArea.add(shape); - } - } - } - return blockArea; - } - - /** - * This class handles edges and areas where access should be blocked. - */ - public static class BlockArea { - private final List edgesList = new ArrayList<>(); - private final List blockedShapes = new ArrayList<>(); - private final int baseEdgeCount; - - public BlockArea(Graph g) { - baseEdgeCount = g.getAllEdges().length(); - } - - public boolean hasCachedEdgeIds(int shapeIndex) { - return !edgesList.get(shapeIndex).isEmpty(); - } - - public String toString(int shapeIndex) { - List returnList = new ArrayList<>(); - for (IntCursor intCursor : edgesList.get(shapeIndex)) { - returnList.add(intCursor.value); - } - Collections.sort(returnList); - return returnList.toString(); - } - - public void add(Shape shape) { - add(shape, new GHIntHashSet()); - } - - public void add(Shape shape, GHIntHashSet blockedEdgeIds) { - blockedShapes.add(shape); - edgesList.add(Objects.requireNonNull(blockedEdgeIds)); - } - - public final boolean contains(GHPoint point) { - for (Shape shape : blockedShapes) { - if (shape.contains(point.lat, point.lon)) - return true; - } - return false; - } - - /** - * @return true if the specified edgeState is part of this BlockArea - */ - public final boolean intersects(EdgeIteratorState edgeState) { - PointList pointList = null; - BBox bbox = null; - for (int shapeIdx = 0; shapeIdx < blockedShapes.size(); shapeIdx++) { - GHIntHashSet blockedEdges = edgesList.get(shapeIdx); - // blockedEdges acts as cache that is only useful when filled and for non-virtual edges - if (!blockedEdges.isEmpty() && edgeState.getEdge() < baseEdgeCount) { - if (blockedEdges.contains(edgeState.getEdge())) - return true; - continue; - } - - // compromise: avoid expensive fetch of pillar nodes, which isn't yet fast for being used in Weighting.calc - if (bbox == null) - bbox = GHUtility.createBBox(edgeState); - - Shape shape = blockedShapes.get(shapeIdx); - if (shape.getBounds().intersects(bbox)) { - if (shape instanceof Polygon && ((Polygon) shape).isRectangle()) - return true; - if (pointList == null) - pointList = edgeState.fetchWayGeometry(FetchMode.ALL).makeImmutable(); - if (shape.intersects(pointList)) - return true; - } - } - return false; - } - } -} \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java b/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java deleted file mode 100644 index 9824ab67d46..00000000000 --- a/core/src/main/java/com/graphhopper/storage/GraphHopperStorage.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.storage; - -import com.graphhopper.routing.util.AllEdgesIterator; -import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.util.Constants; -import com.graphhopper.util.EdgeExplorer; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.shapes.BBox; - -import java.io.Closeable; - -/** - * This class manages all storage related methods and delegates the calls to the associated graphs. - * The associated graphs manage their own necessary data structures and are used to provide e.g. - * different traversal methods. By default this class implements the graph interface and results in - * identical behavior as the Graph instance from getBaseGraph() - *

- * - * @author Peter Karich - */ -public final class GraphHopperStorage implements Graph, Closeable { - private final Directory dir; - private final EncodingManager encodingManager; - private final StorableProperties properties; - private final BaseGraph baseGraph; - - /** - * Use {@link GraphBuilder} to create a graph - */ - public GraphHopperStorage(Directory dir, EncodingManager encodingManager, boolean withElevation, boolean withTurnCosts, int segmentSize) { - if (encodingManager == null) - throw new IllegalArgumentException("EncodingManager needs to be non-null since 0.7. Create one using EncodingManager.create or EncodingManager.create(flagEncoderFactory, ghLocation)"); - - this.encodingManager = encodingManager; - this.dir = dir; - this.properties = new StorableProperties(dir); - baseGraph = new BaseGraph(dir, encodingManager.getIntsForFlags(), withElevation, withTurnCosts, segmentSize); - } - - /** - * @return the directory where this graph is stored. - */ - public Directory getDirectory() { - return dir; - } - - /** - * After configuring this storage you need to create it explicitly. - */ - public GraphHopperStorage create(long byteCount) { - baseGraph.checkNotInitialized(); - if (encodingManager == null) - throw new IllegalStateException("EncodingManager can only be null if you call loadExisting"); - - dir.create(); - - properties.create(100); - properties.put("graph.encoded_values", encodingManager.toEncodedValuesAsString()); - properties.put("graph.flag_encoders", encodingManager.toFlagEncodersAsString()); - - baseGraph.create(Math.max(byteCount, 100)); - return this; - } - - public EncodingManager getEncodingManager() { - return encodingManager; - } - - public StorableProperties getProperties() { - return properties; - } - - public boolean loadExisting() { - baseGraph.checkNotInitialized(); - if (properties.loadExisting()) { - if (properties.containsVersion()) - throw new IllegalStateException("The GraphHopper file format is not compatible with the data you are " + - "trying to load. You either need to use an older version of GraphHopper or run a new import"); - // check encoding for compatibility - String flagEncodersStr = properties.get("graph.flag_encoders"); - - if (!encodingManager.toFlagEncodersAsString().equalsIgnoreCase(flagEncodersStr)) { - throw new IllegalStateException("Encoding does not match:" - + "\nGraphhopper config: " + encodingManager.toFlagEncodersAsString() - + "\nGraph: " + flagEncodersStr - + "\nChange configuration to match the graph or delete " + dir.getLocation()); - } - - String encodedValueStr = properties.get("graph.encoded_values"); - if (!encodingManager.toEncodedValuesAsString().equalsIgnoreCase(encodedValueStr)) { - throw new IllegalStateException("Encoded values do not match:" - + "\nGraphhopper config: " + encodingManager.toEncodedValuesAsString() - + "\nGraph: " + encodedValueStr - + "\nChange configuration to match the graph or delete " + dir.getLocation()); - } - baseGraph.loadExisting(); - return true; - } - return false; - } - - public void flush() { - baseGraph.flush(); - properties.flush(); - } - - @Override - public void close() { - properties.close(); - baseGraph.close(); - } - - public boolean isClosed() { - return baseGraph.isClosed(); - } - - public long getCapacity() { - return baseGraph.getCapacity() + properties.getCapacity(); - } - - /** - * Avoid that edges and nodes of the base graph are further modified. Necessary as hook for e.g. - * ch graphs on top to initialize themselves - */ - public synchronized void freeze() { - if (isFrozen()) - return; - baseGraph.freeze(); - } - - public boolean isFrozen() { - return baseGraph.isFrozen(); - } - - public String toDetailsString() { - return baseGraph.toDetailsString(); - } - - @Override - public String toString() { - return encodingManager - + "|" + getDirectory().getDefaultType() - + "|" + baseGraph.nodeAccess.getDimension() + "D" - + "|" + (baseGraph.supportsTurnCosts() ? baseGraph.turnCostStorage : "no_turn_cost") - + "|" + getVersionsString(); - } - - private String getVersionsString() { - return "nodes:" + Constants.VERSION_NODE + - ",edges:" + Constants.VERSION_EDGE + - ",geometry:" + Constants.VERSION_GEOMETRY + - ",location_index:" + Constants.VERSION_LOCATION_IDX + - ",string_index:" + Constants.VERSION_STRING_IDX + - ",nodesCH:" + Constants.VERSION_NODE_CH + - ",shortcuts:" + Constants.VERSION_SHORTCUT; - } - - // now delegate all Graph methods to BaseGraph to avoid ugly programming flow ala - // GraphHopperStorage storage = ..; - // Graph g = storage.getBaseGraph(); - // instead directly the storage can be used to traverse the base graph - @Override - public BaseGraph getBaseGraph() { - return baseGraph; - } - - @Override - public int getNodes() { - return baseGraph.getNodes(); - } - - @Override - public int getEdges() { - return baseGraph.getEdges(); - } - - @Override - public NodeAccess getNodeAccess() { - return baseGraph.getNodeAccess(); - } - - @Override - public BBox getBounds() { - return baseGraph.getBounds(); - } - - @Override - public EdgeIteratorState edge(int a, int b) { - return baseGraph.edge(a, b); - } - - @Override - public EdgeIteratorState getEdgeIteratorState(int edgeId, int adjNode) { - return baseGraph.getEdgeIteratorState(edgeId, adjNode); - } - - @Override - public EdgeIteratorState getEdgeIteratorStateForKey(int edgeKey) { - return baseGraph.getEdgeIteratorStateForKey(edgeKey); - } - - @Override - public AllEdgesIterator getAllEdges() { - return baseGraph.getAllEdges(); - } - - @Override - public EdgeExplorer createEdgeExplorer(EdgeFilter filter) { - return baseGraph.createEdgeExplorer(filter); - } - - @Override - public TurnCostStorage getTurnCostStorage() { - return baseGraph.getTurnCostStorage(); - } - - @Override - public Weighting wrapWeighting(Weighting weighting) { - return weighting; - } - - @Override - public int getOtherNode(int edge, int node) { - return baseGraph.getOtherNode(edge, node); - } - - @Override - public boolean isAdjacentToNode(int edge, int node) { - return baseGraph.isAdjacentToNode(edge, node); - } - - /** - * Flush and free base graph resources like way geometries and StringIndex - */ - public void flushAndCloseGeometryAndNameStorage() { - baseGraph.flushAndCloseGeometryAndNameStorage(); - } - -} diff --git a/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorImpl.java b/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorImpl.java index f0245508d97..907fac4062e 100644 --- a/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorImpl.java +++ b/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorImpl.java @@ -94,7 +94,7 @@ public boolean next() { @Override public String toString() { - return baseIterator.toString(); + return getEdge() + " " + getBaseNode() + "-" + getAdjNode(); } private boolean finiteWeight(boolean reverse) { diff --git a/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorState.java b/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorState.java index 97e82994000..45ae5e6eda4 100644 --- a/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorState.java +++ b/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorState.java @@ -33,16 +33,22 @@ public interface RoutingCHEdgeIteratorState { int getOrigEdge(); /** - * For shortcuts of an edge-based CH graph this is the ID of the first original edge of this edge state, otherwise - * it is the same as {@link #getOrigEdge()}} + * For shortcuts of an edge-based CH graph this is the key of the first original edge of this edge state + * *in the direction of the shortcut*, i.e. the one this shortcut starts with. Otherwise it is the key of the + * original/base/query graph edge this CH edge state represents. + * It is not so obvious how the direction of this key shall be defined. For base graph edges it is clear as we use + * the storage direction and the value of the key simply depends on which node is the base node + * (the one stored first or second). For shortcut edges we use the direction of the shortcut to define the direction + * of the first/last original edge key. */ - int getOrigEdgeFirst(); + int getOrigEdgeKeyFirst(); /** - * For shortcuts of an edge-based CH graph this is the ID of the last original edge of this edge state, otherwise - * it is the same as {@link #getOrigEdge()}} + * @see #getOrigEdgeKeyFirst(), but for the last edge, i.e. the one the shortcut points to. + * For shortcuts of an edge-based CH graph this is the key of the last original edge of this edge state, otherwise + * it is the key of the original/base/query graph edge this CH edge state represents. */ - int getOrigEdgeLast(); + int getOrigEdgeKeyLast(); int getBaseNode(); diff --git a/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorStateImpl.java b/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorStateImpl.java index 2a743f212d4..2e6942a582e 100644 --- a/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorStateImpl.java +++ b/core/src/main/java/com/graphhopper/storage/RoutingCHEdgeIteratorStateImpl.java @@ -74,17 +74,17 @@ public int getOrigEdge() { } @Override - public int getOrigEdgeFirst() { + public int getOrigEdgeKeyFirst() { if (!isShortcut() || !store.isEdgeBased()) - return getEdge(); - return store.getOrigEdgeFirst(shortcutPointer); + return edgeState().getEdgeKey(); + return store.getOrigEdgeKeyFirst(shortcutPointer); } @Override - public int getOrigEdgeLast() { + public int getOrigEdgeKeyLast() { if (!isShortcut() || !store.isEdgeBased()) - return getEdge(); - return store.getOrigEdgeLast(shortcutPointer); + return edgeState().getEdgeKey(); + return store.getOrigEdgeKeyLast(shortcutPointer); } @Override diff --git a/core/src/main/java/com/graphhopper/storage/RoutingCHGraph.java b/core/src/main/java/com/graphhopper/storage/RoutingCHGraph.java index f56f846083d..f0c004f0924 100644 --- a/core/src/main/java/com/graphhopper/storage/RoutingCHGraph.java +++ b/core/src/main/java/com/graphhopper/storage/RoutingCHGraph.java @@ -35,7 +35,7 @@ public interface RoutingCHGraph { RoutingCHEdgeExplorer createInEdgeExplorer(); /** - * @see #createInEdgeExplorer(), but here the shortcuts/edges are going out of the given node. + * @see #createInEdgeExplorer() but here the shortcuts/edges are going out of the given node. */ RoutingCHEdgeExplorer createOutEdgeExplorer(); diff --git a/core/src/main/java/com/graphhopper/storage/StorableProperties.java b/core/src/main/java/com/graphhopper/storage/StorableProperties.java index d928385c629..73519d33a94 100644 --- a/core/src/main/java/com/graphhopper/storage/StorableProperties.java +++ b/core/src/main/java/com/graphhopper/storage/StorableProperties.java @@ -102,12 +102,7 @@ public synchronized StorableProperties put(String key, Object val) { public synchronized String get(String key) { if (!key.equals(toLowerCase(key))) throw new IllegalArgumentException("Do not use upper case keys (" + key + ") for StorableProperties since 0.7"); - - String ret = map.get(key); - if (ret == null) - return ""; - - return ret; + return map.getOrDefault(key, ""); } public synchronized Map getAll() { diff --git a/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java b/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java index 8464898290b..bf1c987d985 100644 --- a/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java +++ b/core/src/main/java/com/graphhopper/storage/TurnCostStorage.java @@ -18,17 +18,19 @@ package com.graphhopper.storage; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.IntsRefEdgeIntAccess; import com.graphhopper.routing.ev.TurnCost; import com.graphhopper.util.EdgeIterator; /** - * A key/value store, where the unique keys are turn relations, and the values are IntRefs. - * A turn relation is a triple (fromEdge, viaNode, toEdge), + * A key/value store, where the unique keys are turn cost relations, and the values are IntRefs. + * A turn cost relation is a triple (fromEdge, viaNode, toEdge), * and refers to one of the possible ways of crossing an intersection. *

* Like IntRefs on edges, this can in principle be used to store values of any kind. *

- * In practice, the IntRefs are used to store generalized travel costs per turn relation per vehicle type. + * In practice, the IntRefs are used to store generalized travel costs per turn cost relation per vehicle type. * In practice, we only store 0 or infinity. (Can turn, or cannot turn.) * * @author Karl Hübner @@ -45,8 +47,8 @@ public class TurnCostStorage { private static final int TC_NEXT = 12; private static final int BYTES_PER_ENTRY = 16; - private BaseGraph baseGraph; - private DataAccess turnCosts; + private final BaseGraph baseGraph; + private final DataAccess turnCosts; private int turnCostsCount; public TurnCostStorage(BaseGraph baseGraph, DataAccess turnCosts) { @@ -86,13 +88,14 @@ public boolean loadExisting() { /** * Sets the turn cost at the viaNode when going from "fromEdge" to "toEdge" - * WARNING: It is tacitly assumed that for every encoder, this method is only called once per turn relation. - * Subsequent calls for the same encoder and the same turn relation will have undefined results. + * WARNING: It is tacitly assumed that for every encoder, this method is only called once per turn cost relation. + * Subsequent calls for the same encoder and the same turn cost relation will have undefined results. * (The implementation below ORs the new bits into the existing bits.) */ public void set(DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode, int toEdge, double cost) { IntsRef tcFlags = TurnCost.createFlags(); - turnCostEnc.setDecimal(false, tcFlags, cost); + IntsRefEdgeIntAccess intAccess = new IntsRefEdgeIntAccess(tcFlags); + turnCostEnc.setDecimal(false, -1, intAccess, cost); merge(tcFlags, fromEdge, viaNode, toEdge); } @@ -126,7 +129,7 @@ private void merge(IntsRef tcFlags, int fromEdge, int viaNode, int toEdge) { previousEntryIndex = next; // search for the last added cost entry if (i++ > 1000) { - throw new IllegalStateException("Something unexpected happened. A node probably will not have 1000+ relations."); + throw new IllegalStateException("Something unexpected happened. A node probably will not have 1000+ turn cost relations."); } // get index of next turn cost entry next = turnCosts.getInt((long) next * BYTES_PER_ENTRY + TC_NEXT); @@ -156,7 +159,8 @@ private void merge(IntsRef tcFlags, int fromEdge, int viaNode, int toEdge) { */ public double get(DecimalEncodedValue turnCostEnc, int fromEdge, int viaNode, int toEdge) { IntsRef flags = readFlags(fromEdge, viaNode, toEdge); - return turnCostEnc.getDecimal(false, flags); + IntsRefEdgeIntAccess intAccess = new IntsRefEdgeIntAccess(flags); + return turnCostEnc.getDecimal(false, -1, intAccess); } /** @@ -220,11 +224,11 @@ public String toString() { * * @return an iterator over all entries. */ - public TurnRelationIterator getAllTurnRelations() { + public Iterator getAllTurnCosts() { return new Itr(); } - public interface TurnRelationIterator { + public interface Iterator { int getFromEdge(); int getViaNode(); @@ -236,10 +240,11 @@ public interface TurnRelationIterator { boolean next(); } - private class Itr implements TurnRelationIterator { + private class Itr implements Iterator { private int viaNode = -1; private int turnCostIndex = -1; private final IntsRef intsRef = TurnCost.createFlags(); + private final EdgeIntAccess edgeIntAccess = new IntsRefEdgeIntAccess(intsRef); private long turnCostPtr() { return (long) turnCostIndex * BYTES_PER_ENTRY; @@ -263,7 +268,7 @@ public int getToEdge() { @Override public double getCost(DecimalEncodedValue encodedValue) { intsRef.ints[0] = turnCosts.getInt(turnCostPtr() + TC_FLAGS); - return encodedValue.getDecimal(false, intsRef); + return encodedValue.getDecimal(false, -1, edgeIntAccess); } @Override diff --git a/core/src/main/java/com/graphhopper/storage/index/LocationIndexTree.java b/core/src/main/java/com/graphhopper/storage/index/LocationIndexTree.java index 0a0ca9c5092..9243ba7db73 100644 --- a/core/src/main/java/com/graphhopper/storage/index/LocationIndexTree.java +++ b/core/src/main/java/com/graphhopper/storage/index/LocationIndexTree.java @@ -302,8 +302,8 @@ public Snap findClosest(final double queryLat, final double queryLon, final Edge } if (closestMatch.isValid()) { - closestMatch.setQueryDistance(DIST_PLANE.calcDenormalizedDist(closestMatch.getQueryDistance())); closestMatch.calcSnappedPoint(DIST_PLANE); + closestMatch.setQueryDistance(DIST_PLANE.calcDist(closestMatch.getSnappedPoint().lat, closestMatch.getSnappedPoint().lon, queryLat, queryLon)); } return closestMatch; } diff --git a/core/src/main/java/com/graphhopper/storage/index/Snap.java b/core/src/main/java/com/graphhopper/storage/index/Snap.java index ed156967e30..7ef64be1a6a 100644 --- a/core/src/main/java/com/graphhopper/storage/index/Snap.java +++ b/core/src/main/java/com/graphhopper/storage/index/Snap.java @@ -124,8 +124,13 @@ public GHPoint3D getSnappedPoint() { return snappedPoint; } + public void setSnappedPoint(GHPoint3D point) { + this.snappedPoint = point; + } + /** - * Calculates the closest point on the edge from the query point. + * Calculates the closest point on the edge from the query point. If too close to a tower or pillar node this method + * might change the snappedPosition and wayIndex. */ public void calcSnappedPoint(DistanceCalc distCalc) { if (closestEdge == null) @@ -145,12 +150,31 @@ public void calcSnappedPoint(DistanceCalc distCalc) { double queryLat = getQueryPoint().lat, queryLon = getQueryPoint().lon; double adjLat = fullPL.getLat(wayIndex + 1), adjLon = fullPL.getLon(wayIndex + 1); if (distCalc.validEdgeDistance(queryLat, queryLon, tmpLat, tmpLon, adjLat, adjLon)) { - GHPoint tmpPoint = distCalc.calcCrossingPointToEdge(queryLat, queryLon, tmpLat, tmpLon, adjLat, adjLon); + GHPoint crossingPoint = distCalc.calcCrossingPointToEdge(queryLat, queryLon, tmpLat, tmpLon, adjLat, adjLon); double adjEle = fullPL.getEle(wayIndex + 1); - snappedPoint = new GHPoint3D(tmpPoint.lat, tmpPoint.lon, (tmpEle + adjEle) / 2); - } else - // outside of edge boundaries - snappedPoint = new GHPoint3D(tmpLat, tmpLon, tmpEle); + + // We want to prevent extra virtual nodes and very short virtual edges in case the snap/crossing point is + // very close to a tower node. Since we delayed the calculation of the crossing point until here, we need + // to correct the Snap.Position in these cases. Note that it is possible that the query point is very far + // from the tower node, but the crossing point is still very close to it. + if (considerEqual(crossingPoint.lat, crossingPoint.lon, tmpLat, tmpLon)) { + snappedPosition = wayIndex == 0 ? Position.TOWER : Position.PILLAR; + snappedPoint = new GHPoint3D(tmpLat, tmpLon, tmpEle); + } else if (considerEqual(crossingPoint.lat, crossingPoint.lon, adjLat, adjLon)) { + wayIndex++; + snappedPosition = wayIndex == fullPL.size() - 1 ? Position.TOWER : Position.PILLAR; + snappedPoint = new GHPoint3D(adjLat, adjLon, adjEle); + } else { + snappedPoint = new GHPoint3D(crossingPoint.lat, crossingPoint.lon, (tmpEle + adjEle) / 2); + } + } else { + // outside of edge segment [wayIndex, wayIndex+1] should not happen for EDGE + assert false : "incorrect pos: " + snappedPosition + " for " + snappedPoint + ", " + fullPL + ", " + wayIndex; + } + } + + public static boolean considerEqual(double lat, double lon, double lat2, double lon2) { + return Math.abs(lat - lat2) < 1e-6 && Math.abs(lon - lon2) < 1e-6; } @Override diff --git a/core/src/main/java/com/graphhopper/util/Constants.java b/core/src/main/java/com/graphhopper/util/Constants.java index 3935b1e1ca5..f881a1f9f8d 100644 --- a/core/src/main/java/com/graphhopper/util/Constants.java +++ b/core/src/main/java/com/graphhopper/util/Constants.java @@ -30,12 +30,12 @@ */ public class Constants { /** - * The value of System.getProperty("java.version"). * + * The value of System.getProperty("java.version"). * */ public static final String JAVA_VERSION = System.getProperty("java.version"); /** - * The value of System.getProperty("os.name"). * + * The value of System.getProperty("os.name"). * */ public static final String OS_NAME = System.getProperty("os.name"); /** @@ -68,18 +68,20 @@ public class Constants { public static final int VERSION_NODE = 9; public static final int VERSION_EDGE = 21; - public static final int VERSION_SHORTCUT = 8; + // this should be increased whenever the format of the serialized EncodingManager is changed + public static final int VERSION_EM = 2; + public static final int VERSION_SHORTCUT = 9; public static final int VERSION_NODE_CH = 0; public static final int VERSION_GEOMETRY = 6; public static final int VERSION_LOCATION_IDX = 5; - public static final int VERSION_STRING_IDX = 6; + public static final int VERSION_KV_STORAGE = 2; /** * The version without the snapshot string */ public static final String VERSION; public static final String BUILD_DATE; /** - * Details about the git commit this artifact was build for, can be null (if not build using maven) + * Details about the git commit this artifact was built for, can be null (if not built using maven) */ public static final GitInfo GIT_INFO; public static final boolean SNAPSHOT; @@ -145,7 +147,7 @@ public class Constants { public static String getVersions() { return VERSION_NODE + "," + VERSION_EDGE + "," + VERSION_GEOMETRY + "," + VERSION_LOCATION_IDX - + "," + VERSION_STRING_IDX + "," + VERSION_SHORTCUT; + + "," + VERSION_KV_STORAGE + "," + VERSION_SHORTCUT; } public static String getMajorVersion() { diff --git a/core/src/main/java/com/graphhopper/util/DistanceCalcEarth.java b/core/src/main/java/com/graphhopper/util/DistanceCalcEarth.java index 040f2c3590f..8ea8bfb08ec 100644 --- a/core/src/main/java/com/graphhopper/util/DistanceCalcEarth.java +++ b/core/src/main/java/com/graphhopper/util/DistanceCalcEarth.java @@ -40,7 +40,7 @@ public class DistanceCalcEarth implements DistanceCalc { public final static double C = 2 * PI * R; public final static double KM_MILE = 1.609344; public final static double METERS_PER_DEGREE = C / 360.0; - public static final DistanceCalc DIST_EARTH = new DistanceCalcEarth(); + public static final DistanceCalcEarth DIST_EARTH = new DistanceCalcEarth(); /** * Calculates distance of (from, to) in meter. @@ -298,33 +298,41 @@ public GHPoint intermediatePoint(double f, double lat1, double lon1, double lat2 double sinHalfDeltaLon = sin(deltaLon / 2); double a = sinHalfDeltaLat * sinHalfDeltaLat + cosLat1 * cosLat2 * sinHalfDeltaLon * sinHalfDeltaLon; - double angularDistance = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + double angularDistance = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); double sinDistance = sin(angularDistance); if (angularDistance == 0) return new GHPoint(lat1, lon1); - double A = Math.sin((1-f)*angularDistance) / sinDistance; - double B = Math.sin(f*angularDistance) / sinDistance; + double A = Math.sin((1 - f) * angularDistance) / sinDistance; + double B = Math.sin(f * angularDistance) / sinDistance; double x = A * cosLat1 * cos(lon1radians) + B * cosLat2 * cos(lon2radians); double y = A * cosLat1 * sin(lon1radians) + B * cosLat2 * sin(lon2radians); double z = A * sin(lat1radians) + B * sin(lat2radians); - double midLat = Math.toDegrees(Math.atan2(z, Math.sqrt(x*x + y*y))); + double midLat = Math.toDegrees(Math.atan2(z, Math.sqrt(x * x + y * y))); double midLon = Math.toDegrees(Math.atan2(y, x)); return new GHPoint(midLat, midLon); } @Override - public final double calcDistance(PointList pointList) { + public double calcDistance(PointList pointList) { + return internCalcDistance(pointList, pointList.is3D()); + } + + public static double calcDistance(PointList pointList, boolean is3d) { + return DistanceCalcEarth.DIST_EARTH.internCalcDistance(pointList, is3d); + } + + private double internCalcDistance(PointList pointList, boolean is3d) { double prevLat = Double.NaN; double prevLon = Double.NaN; double prevEle = Double.NaN; double dist = 0; for (int i = 0; i < pointList.size(); i++) { if (i > 0) { - if (pointList.is3D()) + if (is3d) dist += calcDist3D(prevLat, prevLon, prevEle, pointList.getLat(i), pointList.getLon(i), pointList.getEle(i)); else dist += calcDist(prevLat, prevLon, pointList.getLat(i), pointList.getLon(i)); diff --git a/core/src/main/java/com/graphhopper/util/EdgeIteratorState.java b/core/src/main/java/com/graphhopper/util/EdgeIteratorState.java index d40c1fdf53f..44ca68898a3 100644 --- a/core/src/main/java/com/graphhopper/util/EdgeIteratorState.java +++ b/core/src/main/java/com/graphhopper/util/EdgeIteratorState.java @@ -18,9 +18,12 @@ package com.graphhopper.util; import com.graphhopper.routing.ev.*; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.Graph; import com.graphhopper.storage.IntsRef; +import java.util.List; + /** * This interface represents an edge and is one possible state of an EdgeIterator. * Example: @@ -53,17 +56,12 @@ public String getName() { } @Override - public int getVersion() { - return 1; - } - - @Override - public boolean getBool(boolean reverse, IntsRef ref) { + public boolean getBool(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess) { return reverse; } @Override - public void setBool(boolean reverse, IntsRef ref, boolean value) { + public void setBool(boolean reverse, int edgeId, EdgeIntAccess edgeIntAccess, boolean value) { throw new IllegalStateException("reverse state cannot be modified"); } @@ -89,6 +87,11 @@ public boolean isStoreTwoDirections() { */ int getEdgeKey(); + /** + * Like #getEdgeKey, but returns the reverse key. For loops the reverse key is the same as the key. + */ + int getReverseEdgeKey(); + /** * Returns the node used to instantiate the EdgeIterator. Often only used for convenience reasons. * Do not confuse this with a source node of a directed edge. @@ -180,20 +183,47 @@ public boolean isStoreTwoDirections() { > EdgeIteratorState setReverse(EnumEncodedValue property, T value); > EdgeIteratorState set(EnumEncodedValue property, T fwd, T bwd); - + String get(StringEncodedValue property); - + EdgeIteratorState set(StringEncodedValue property, String value); - + String getReverse(StringEncodedValue property); - + EdgeIteratorState setReverse(StringEncodedValue property, String value); - + EdgeIteratorState set(StringEncodedValue property, String fwd, String bwd); + /** + * Identical to calling getKeyValues().get("name") if name is stored for both directions. Note that for backward + * compatibility this method returns an empty String instead of null if there was no KeyPair with key==name stored. + * + * @return the stored value for the key "name" in the KeyValue list of this EdgeIteratorState. + */ String getName(); - EdgeIteratorState setName(String name); + /** + * This stores the specified key-value pairs in the storage of this EdgeIteratorState. This is more flexible + * compared to the mechanism of flags and EncodedValue and allows storing sparse key value pairs more efficient. + * But it might be slow and more inefficient on retrieval. Call this setKeyValues method only once per + * EdgeIteratorState as it allocates new space everytime this method is called. + */ + EdgeIteratorState setKeyValues(List map); + + /** + * This method returns KeyValue pairs for both directions in contrast to {@link #getValue(String)}. + * + * @see #setKeyValues(List) + */ + List getKeyValues(); + + /** + * This method returns the *first* value for the specified key and only if stored for the direction of this + * EdgeIteratorState. If you need more than one value see also {@link #getKeyValues()}. Avoid storing KeyPairs with + * duplicate keys as only the first will be reachable with this method. Currently, there is no support to use this + * method in a custom_model, and you should use EncodedValues instead. + */ + Object getValue(String key); /** * Clones this EdgeIteratorState. diff --git a/core/src/main/java/com/graphhopper/util/GHUtility.java b/core/src/main/java/com/graphhopper/util/GHUtility.java index 39d95ac075e..30f165390cc 100644 --- a/core/src/main/java/com/graphhopper/util/GHUtility.java +++ b/core/src/main/java/com/graphhopper/util/GHUtility.java @@ -23,13 +23,22 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.graphhopper.coll.GHBitSet; import com.graphhopper.coll.GHBitSetImpl; -import com.graphhopper.routing.ev.*; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.Path; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.Country; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.AllEdgesIterator; +import com.graphhopper.routing.util.CustomArea; +import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.*; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; import com.graphhopper.util.shapes.BBox; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,6 +54,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; import static com.graphhopper.util.DistanceCalcEarth.DIST_EARTH; @@ -149,19 +160,11 @@ public static List getEdgeIds(EdgeIterator iter) { return list; } - public static void printGraphForUnitTest(Graph g, FlagEncoder encoder) { - printGraphForUnitTest(g, encoder.getAccessEnc(), encoder.getAverageSpeedEnc()); - } - public static void printGraphForUnitTest(Graph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { printGraphForUnitTest(g, accessEnc, speedEnc, new BBox( Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); } - public static void printGraphForUnitTest(Graph g, FlagEncoder encoder, BBox bBox) { - printGraphForUnitTest(g, encoder.getAccessEnc(), encoder.getAverageSpeedEnc(), bBox); - } - public static void printGraphForUnitTest(Graph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, BBox bBox) { System.out.println("WARNING: printGraphForUnitTest does not pay attention to custom edge speeds at the moment"); NodeAccess na = g.getNodeAccess(); @@ -201,7 +204,7 @@ private static void printUnitTestEdge(BooleanEncodedValue accessEnc, DecimalEnco } /** - * @param speed if null a random speed will be assign to every edge + * @param speed if null a random speed will be assigned to every edge */ public static void buildRandomGraph(Graph graph, Random random, int numNodes, double meanDegree, boolean allowLoops, boolean allowZeroDistance, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, Double speed, @@ -267,12 +270,6 @@ public static double getDistance(int from, int to, NodeAccess nodeAccess) { return DistancePlaneProjection.DIST_PLANE.calcDist(fromLat, fromLon, toLat, toLon); } - public static void addRandomTurnCosts(Graph graph, long seed, EncodingManager em, FlagEncoder encoder, int maxTurnCost, TurnCostStorage turnCostStorage) { - DecimalEncodedValue turnCostEnc = em.getDecimalEncodedValue(TurnCost.key(encoder.toString())); - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - addRandomTurnCosts(graph, seed, accessEnc, turnCostEnc, maxTurnCost, turnCostStorage); - } - public static void addRandomTurnCosts(Graph graph, long seed, BooleanEncodedValue accessEnc, DecimalEncodedValue turnCostEnc, int maxTurnCost, TurnCostStorage turnCostStorage) { Random random = new Random(seed); double pNodeHasTurnCosts = 0.3; @@ -345,6 +342,11 @@ public static Graph shuffle(Graph g, Graph sortedGraph) { */ public static Graph sortDFS(Graph g, Graph sortedGraph) { if (g.getTurnCostStorage() != null) { + // not only would we have to sort the turn cost storage when we re-sort the graph, but we'd also have to make + // sure that the location index always snaps to real edges and not the corresponding artificial edge that we + // introduced to deal with via-way restrictions. Without sorting this works automatically because the real + // edges use lower edge ids. Otherwise we'd probably have to use some kind of is_artificial flag for each + // edge. throw new IllegalArgumentException("Sorting the graph is currently not supported in the presence of turn costs"); } int nodes = g.getNodes(); @@ -423,28 +425,25 @@ static Graph createSortedGraph(Graph fromGraph, Graph toSortedGraph, final IntIn return toSortedGraph; } - static Directory guessDirectory(GraphHopperStorage store) { - if (store.getDirectory() instanceof MMapDirectory) { + static Directory guessDirectory(BaseGraph graph) { + if (graph.getDirectory() instanceof MMapDirectory) { throw new IllegalStateException("not supported yet: mmap will overwrite existing storage at the same location"); } - String location = store.getDirectory().getLocation(); - boolean isStoring = ((GHDirectory) store.getDirectory()).isStoring(); + String location = graph.getDirectory().getLocation(); + boolean isStoring = ((GHDirectory) graph.getDirectory()).isStoring(); return new RAMDirectory(location, isStoring); } /** * Create a new storage from the specified one without copying the data. CHGraphs won't be copied. */ - public static GraphHopperStorage newStorage(GraphHopperStorage store) { - Directory outdir = guessDirectory(store); - boolean is3D = store.getNodeAccess().is3D(); - GraphHopperStorage copy = new GraphBuilder(store.getEncodingManager()) - .withTurnCosts(store.getTurnCostStorage() != null) - .set3D(is3D) + public static BaseGraph newGraph(BaseGraph baseGraph) { + Directory outdir = guessDirectory(baseGraph); + return new BaseGraph.Builder(baseGraph.getIntsForFlags()) + .withTurnCosts(baseGraph.getTurnCostStorage() != null) + .set3D(baseGraph.getNodeAccess().is3D()) .setDir(outdir) .create(); - copy.getProperties().putAll(store.getProperties().getAll()); - return copy; } public static int getAdjNode(Graph g, int edge, int adjNode) { @@ -464,78 +463,8 @@ public static void checkDAVersion(String name, int expectedVersion, int version) } } - public static EdgeIteratorState createMockedEdgeIteratorState(final double distance, final IntsRef flags) { - return createMockedEdgeIteratorState(distance, flags, 0, 1, 2, 3, 4); - } - - public static EdgeIteratorState createMockedEdgeIteratorState(final double distance, final IntsRef flags, - final int base, final int adj, final int edge, final int origFirst, final int origLast) { - return new GHUtility.DisabledEdgeIterator() { - @Override - public double getDistance() { - return distance; - } - - @Override - public IntsRef getFlags() { - return flags; - } - - @Override - public boolean get(BooleanEncodedValue property) { - return property.getBool(false, flags); - } - - @Override - public boolean getReverse(BooleanEncodedValue property) { - return property.getBool(true, flags); - } - - @Override - public double get(DecimalEncodedValue property) { - return property.getDecimal(false, flags); - } - - @Override - public double getReverse(DecimalEncodedValue property) { - return property.getDecimal(true, flags); - } - - @Override - public > T get(EnumEncodedValue property) { - return property.getEnum(false, flags); - } - - @Override - public > T getReverse(EnumEncodedValue property) { - return property.getEnum(true, flags); - } - - @Override - public int getEdge() { - return edge; - } - - @Override - public int getBaseNode() { - return base; - } - - @Override - public int getAdjNode() { - return adj; - } - - @Override - public PointList fetchWayGeometry(FetchMode type) { - return Helper.createPointList(0, 2, 6, 4); - } - - }; - } - /** - * @return the the edge between base and adj, or null if there is no such edge + * @return the edge between base and adj, or null if there is no such edge * @throws IllegalArgumentException when there are multiple edges */ public static EdgeIteratorState getEdge(Graph graph, int base, int adj) { @@ -565,36 +494,17 @@ public static int count(EdgeIterator iterator, int adj) { return count; } - /** - * Creates unique positive number for specified edgeId taking into account the direction defined - * by nodeA, nodeB and reverse. - */ - public static int createEdgeKey(int nodeA, int nodeB, int edgeId, boolean reverse) { - edgeId = edgeId << 1; - if (reverse) - return (nodeA >= nodeB) ? edgeId : edgeId + 1; - return (nodeA > nodeB) ? edgeId + 1 : edgeId; - } - /** * Creates an edge key, i.e. an integer number that encodes an edge ID and the direction of an edge */ - public static int createEdgeKey(int edgeId, boolean reverse) { + public static int createEdgeKey(int edgeId, boolean isLoop, boolean reverse) { // edge state in storage direction -> edge key is even // edge state against storage direction -> edge key is odd - return (edgeId << 1) + (reverse ? 1 : 0); + return (edgeId << 1) + ((reverse && !isLoop) ? 1 : 0); } /** - * Returns if the specified edgeKeys (created by createEdgeKey) are identical regardless of the - * direction. - */ - public static boolean isSameEdgeKeys(int edgeKey1, int edgeKey2) { - return edgeKey1 / 2 == edgeKey2 / 2; - } - - /** - * Returns the edgeKey of the opposite direction + * Returns the edgeKey of the opposite direction, be careful not to use this for loops! */ public static int reverseEdgeKey(int edgeKey) { return edgeKey % 2 == 0 ? edgeKey + 1 : edgeKey - 1; @@ -607,59 +517,32 @@ public static int getEdgeFromEdgeKey(int edgeKey) { return edgeKey / 2; } - public static IntsRef setSpeed(double fwdSpeed, double bwdSpeed, FlagEncoder encoder, IntsRef edgeFlags) { - if (fwdSpeed < 0 || bwdSpeed < 0) - throw new IllegalArgumentException("Speed must be positive but wasn't! fwdSpeed:" + fwdSpeed + ", bwdSpeed:" + bwdSpeed); - - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - DecimalEncodedValue avgSpeedEnc = encoder.getAverageSpeedEnc(); - - avgSpeedEnc.setDecimal(false, edgeFlags, fwdSpeed); - if (fwdSpeed > 0) - accessEnc.setBool(false, edgeFlags, true); - - if (bwdSpeed > 0 && (fwdSpeed != bwdSpeed || avgSpeedEnc.isStoreTwoDirections())) { - if (!avgSpeedEnc.isStoreTwoDirections()) - throw new IllegalArgumentException("EncodedValue " + avgSpeedEnc.getName() + " supports only one direction " + - "but two different speeds were specified " + fwdSpeed + " " + bwdSpeed); - avgSpeedEnc.setDecimal(true, edgeFlags, bwdSpeed); - } - if (bwdSpeed > 0) - accessEnc.setBool(true, edgeFlags, true); - return edgeFlags; - } - - public static void setSpeed(double fwdSpeed, double bwdSpeed, FlagEncoder encoder, EdgeIteratorState... edges) { - setSpeed(fwdSpeed, bwdSpeed, encoder, Arrays.asList(edges)); + public static void setSpeed(double fwdSpeed, double bwdSpeed, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, EdgeIteratorState... edges) { + setSpeed(fwdSpeed, bwdSpeed, accessEnc, speedEnc, Arrays.asList(edges)); } - public static void setSpeed(double fwdSpeed, double bwdSpeed, FlagEncoder encoder, Collection edges) { + public static void setSpeed(double fwdSpeed, double bwdSpeed, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, Collection edges) { if (fwdSpeed < 0 || bwdSpeed < 0) throw new IllegalArgumentException("Speed must be positive but wasn't! fwdSpeed:" + fwdSpeed + ", bwdSpeed:" + bwdSpeed); - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - DecimalEncodedValue avgSpeedEnc = encoder.getAverageSpeedEnc(); for (EdgeIteratorState edge : edges) { - edge.set(avgSpeedEnc, fwdSpeed); + edge.set(speedEnc, fwdSpeed); if (fwdSpeed > 0) edge.set(accessEnc, true); - if (bwdSpeed > 0 && (fwdSpeed != bwdSpeed || avgSpeedEnc.isStoreTwoDirections())) { - if (!avgSpeedEnc.isStoreTwoDirections()) - throw new IllegalArgumentException("EncodedValue " + avgSpeedEnc.getName() + " supports only one direction " + + if (bwdSpeed > 0 && (fwdSpeed != bwdSpeed || speedEnc.isStoreTwoDirections())) { + if (!speedEnc.isStoreTwoDirections()) + throw new IllegalArgumentException("EncodedValue " + speedEnc.getName() + " supports only one direction " + "but two different speeds were specified " + fwdSpeed + " " + bwdSpeed); - edge.setReverse(avgSpeedEnc, bwdSpeed); + edge.setReverse(speedEnc, bwdSpeed); } if (bwdSpeed > 0) edge.setReverse(accessEnc, true); } } - public static EdgeIteratorState setSpeed(double averageSpeed, boolean fwd, boolean bwd, FlagEncoder encoder, EdgeIteratorState edge) { + public static EdgeIteratorState setSpeed(double averageSpeed, boolean fwd, boolean bwd, BooleanEncodedValue accessEnc, DecimalEncodedValue avSpeedEnc, EdgeIteratorState edge) { if (averageSpeed < 0.0001 && (fwd || bwd)) throw new IllegalStateException("Zero speed is only allowed if edge will get inaccessible. Otherwise Weighting can produce inconsistent results"); - - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - DecimalEncodedValue avSpeedEnc = encoder.getAverageSpeedEnc(); edge.set(accessEnc, fwd, bwd); if (fwd) edge.set(avSpeedEnc, averageSpeed); @@ -730,23 +613,59 @@ public static long calcMillisWithTurnMillis(Weighting weighting, EdgeIteratorSta public static List readCountries() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JtsModule()); + + Map map = new HashMap<>(Country.values().length); + for (Country c : Country.values()) map.put(c.getAlpha2(), c); + try (Reader reader = new InputStreamReader(GHUtility.class.getResourceAsStream("/com/graphhopper/countries/countries.geojson"), StandardCharsets.UTF_8)) { JsonFeatureCollection jsonFeatureCollection = objectMapper.readValue(reader, JsonFeatureCollection.class); return jsonFeatureCollection.getFeatures().stream() - .map(CustomArea::fromJsonFeature) + // exclude areas not in the list of Country enums like FX => Metropolitan France + .filter(customArea -> map.get(getIdOrPropertiesId(customArea)) != null) + .map((f) -> { + CustomArea ca = CustomArea.fromJsonFeature(f); + // the Feature does not include "id" but we expect it + if (f.getId() == null) f.setId(getIdOrPropertiesId(f)); + Country country = map.get(f.getId()); + ca.getProperties().put(Country.ISO_ALPHA3, country.name()); + return ca; + }) .collect(Collectors.toList()); } catch (IOException e) { throw new UncheckedIOException(e); } } - public static void runConcurrently(List> callables, int threads) { + private static String getIdOrPropertiesId(JsonFeature feature) { + if (feature.getId() != null) return feature.getId(); + if (feature.getProperties() != null) return (String) feature.getProperties().get("id"); + return null; + } + + public static CustomArea getFirstDuplicateArea(List areas, String id) { + Set result = new HashSet<>(areas.size()); + for (CustomArea area : areas) { + if (area.getProperties() == null) continue; + String countryCode = (String) area.getProperties().get(id); + // in our country file there are not only countries but "subareas" (with ISO3166-2) or other unnamed areas + // like Metropolitan Netherlands + if (countryCode != null && !result.add(countryCode)) + return area; + } + return null; + } + + public static void runConcurrently(Stream> callables, int threads) { ExecutorService executorService = Executors.newFixedThreadPool(threads); ExecutorCompletionService completionService = new ExecutorCompletionService<>(executorService); - callables.forEach(completionService::submit); + AtomicInteger count = new AtomicInteger(); + callables.forEach(c -> { + count.incrementAndGet(); + completionService.submit(c); + }); executorService.shutdown(); try { - for (int i = 0; i < callables.size(); i++) + for (int i = 0; i < count.get(); i++) completionService.take().get(); } catch (Exception e) { executorService.shutdownNow(); @@ -754,217 +673,128 @@ public static void runConcurrently(List> callables, int threads } } - /** - * This edge iterator can be used in tests to mock specific iterator behaviour via overloading - * certain methods. - */ - public static class DisabledEdgeIterator implements EdgeIterator { - @Override - public EdgeIterator detach(boolean reverse) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState setDistance(double dist) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState setFlags(IntsRef flags) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public boolean next() { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public int getEdge() { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public int getEdgeKey() { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public int getBaseNode() { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public int getAdjNode() { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public double getDistance() { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public IntsRef getFlags() { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public PointList fetchWayGeometry(FetchMode type) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState setWayGeometry(PointList list) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public String getName() { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState setName(String name) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public boolean get(BooleanEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public boolean getReverse(BooleanEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState set(BooleanEncodedValue property, boolean fwd, boolean bwd) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public int get(IntEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState set(IntEncodedValue property, int value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public int getReverse(IntEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState setReverse(IntEncodedValue property, int value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState set(IntEncodedValue property, int fwd, int bwd) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public double get(DecimalEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState set(DecimalEncodedValue property, double value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public double getReverse(DecimalEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState set(DecimalEncodedValue property, double fwd, double bwd) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public > T get(EnumEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public > EdgeIteratorState set(EnumEncodedValue property, T value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public > T getReverse(EnumEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public > EdgeIteratorState setReverse(EnumEncodedValue property, T value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public > EdgeIteratorState set(EnumEncodedValue property, T fwd, T bwd) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public String get(StringEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState set(StringEncodedValue property, String value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public String getReverse(StringEncodedValue property) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState setReverse(StringEncodedValue property, String value) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState set(StringEncodedValue property, String fwd, String bwd) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - @Override - public EdgeIteratorState copyPropertiesFrom(EdgeIteratorState edge) { - throw new UnsupportedOperationException("Not supported. Edge is empty."); - } - - } - public static BBox createBBox(EdgeIteratorState edgeState) { PointList towerNodes = edgeState.fetchWayGeometry(FetchMode.TOWER_ONLY); int secondIndex = towerNodes.size() == 1 ? 0 : 1; return BBox.fromPoints(towerNodes.getLat(0), towerNodes.getLon(0), towerNodes.getLat(secondIndex), towerNodes.getLon(secondIndex)); } + + public static JsonFeature createCircle(String id, double centerLat, double centerLon, double radius) { + final int n = 36; + final double delta = 360.0 / n; + Coordinate[] coordinates = IntStream.range(0, n + 1) + .mapToObj(i -> DIST_EARTH.projectCoordinate(centerLat, centerLon, radius, (i * delta) % 360)) + .map(p -> new Coordinate(p.lon, p.lat)).toArray(Coordinate[]::new); + Polygon polygon = new GeometryFactory().createPolygon(coordinates); + JsonFeature result = new JsonFeature(); + result.setId(id); + result.setGeometry(polygon); + return result; + } + + public static JsonFeature createRectangle(String id, double minLat, double minLon, double maxLat, double maxLon) { + Coordinate[] coordinates = new Coordinate[]{ + new Coordinate(minLon, minLat), + new Coordinate(minLon, maxLat), + new Coordinate(maxLon, maxLat), + new Coordinate(maxLon, minLat), + new Coordinate(minLon, minLat) + }; + Polygon polygon = new GeometryFactory().createPolygon(coordinates); + JsonFeature result = new JsonFeature(); + result.setId(id); + result.setGeometry(polygon); + return result; + } + + public static List comparePaths(Path refPath, Path path, int source, int target, long seed) { + List strictViolations = new ArrayList<>(); + double refWeight = refPath.getWeight(); + double weight = path.getWeight(); + if (Math.abs(refWeight - weight) > 1.e-2) { + LOGGER.warn("expected: " + refPath.calcNodes()); + LOGGER.warn("given: " + path.calcNodes()); + LOGGER.warn("seed: " + seed); + fail("wrong weight: " + source + "->" + target + "\nexpected: " + refWeight + "\ngiven: " + weight + "\nseed: " + seed); + } + if (Math.abs(path.getDistance() - refPath.getDistance()) > 1.e-1) { + strictViolations.add("wrong distance " + source + "->" + target + ", expected: " + refPath.getDistance() + ", given: " + path.getDistance()); + } + if (Math.abs(path.getTime() - refPath.getTime()) > 50) { + strictViolations.add("wrong time " + source + "->" + target + ", expected: " + refPath.getTime() + ", given: " + path.getTime()); + } + IntIndexedContainer refNodes = refPath.calcNodes(); + IntIndexedContainer pathNodes = path.calcNodes(); + if (!refNodes.equals(pathNodes)) { + // sometimes paths are only different because of a zero weight loop. we do not consider these as strict + // violations, see: #1864 + boolean isStrictViolation = !ArrayUtil.withoutConsecutiveDuplicates(refNodes).equals(ArrayUtil.withoutConsecutiveDuplicates(pathNodes)); + // sometimes there are paths including an edge a-c that has the same distance as the two edges a-b-c. in this + // case both options are valid best paths. we only check for this most simple and frequent case here... + if (path.getGraph() != refPath.getGraph()) + fail("path and refPath graphs are different"); + if (pathsEqualExceptOneEdge(path.getGraph(), refNodes, pathNodes)) + isStrictViolation = false; + if (isStrictViolation) + strictViolations.add("wrong nodes " + source + "->" + target + "\nexpected: " + refNodes + "\ngiven: " + pathNodes); + } + return strictViolations; + } + + /** + * Sometimes the graph can contain edges like this: + * A--C + * \-B| + * where A-C is the same distance as A-B-C. In this case the shortest path is not well defined in terms of nodes. + * This method checks if two node-paths are equal except for such an edge. + */ + private static boolean pathsEqualExceptOneEdge(Graph graph, IntIndexedContainer p1, IntIndexedContainer p2) { + if (p1.equals(p2)) + throw new IllegalArgumentException("paths are equal"); + if (Math.abs(p1.size() - p2.size()) != 1) + return false; + IntIndexedContainer shorterPath = p1.size() < p2.size() ? p1 : p2; + IntIndexedContainer longerPath = p1.size() < p2.size() ? p2 : p1; + if (shorterPath.size() < 2) + return false; + IntArrayList indicesWithDifferentNodes = new IntArrayList(); + for (int i = 1; i < shorterPath.size(); i++) { + if (shorterPath.get(i - indicesWithDifferentNodes.size()) != longerPath.get(i)) { + indicesWithDifferentNodes.add(i); + } + } + if (indicesWithDifferentNodes.size() != 1) + return false; + int b = indicesWithDifferentNodes.get(0); + int a = b - 1; + int c = b + 1; + assert shorterPath.get(a) == longerPath.get(a); + assert shorterPath.get(b) != longerPath.get(b); + if (shorterPath.get(b) != longerPath.get(c)) + return false; + double distABC = getMinDist(graph, longerPath.get(a), longerPath.get(b)) + getMinDist(graph, longerPath.get(b), longerPath.get(c)); + + double distAC = getMinDist(graph, shorterPath.get(a), longerPath.get(c)); + if (Math.abs(distABC - distAC) > 0.1) + return false; + LOGGER.info("Distance " + shorterPath.get(a) + "-" + longerPath.get(c) + " is the same as distance " + + longerPath.get(a) + "-" + longerPath.get(b) + "-" + longerPath.get(c) + " -> there are multiple possibilities " + + "for shortest paths"); + return true; + } + + private static double getMinDist(Graph graph, int p, int q) { + EdgeExplorer explorer = graph.createEdgeExplorer(); + EdgeIterator iter = explorer.setBaseNode(p); + double distance = Double.MAX_VALUE; + while (iter.next()) + if (iter.getAdjNode() == q) + distance = Math.min(distance, iter.getDistance()); + return distance; + } + + private static void fail(String message) { + throw new AssertionError(message); + } } diff --git a/core/src/main/java/com/graphhopper/util/PathMerger.java b/core/src/main/java/com/graphhopper/util/PathMerger.java index c603943978e..1142d749ea3 100644 --- a/core/src/main/java/com/graphhopper/util/PathMerger.java +++ b/core/src/main/java/com/graphhopper/util/PathMerger.java @@ -45,13 +45,13 @@ * @author Robin Boldt */ public class PathMerger { - private static final DouglasPeucker DP = new DouglasPeucker(); + private static final RamerDouglasPeucker RDP = new RamerDouglasPeucker(); private final Graph graph; private final Weighting weighting; private boolean enableInstructions = true; private boolean simplifyResponse = true; - private DouglasPeucker douglasPeucker = DP; + private RamerDouglasPeucker ramerDouglasPeucker = RDP; private boolean calcPoints = true; private PathDetailsBuilderFactory pathBuilderFactory; private List requestedPathDetails = Collections.emptyList(); @@ -67,8 +67,8 @@ public PathMerger setCalcPoints(boolean calcPoints) { return this; } - public PathMerger setDouglasPeucker(DouglasPeucker douglasPeucker) { - this.douglasPeucker = douglasPeucker; + public PathMerger setRamerDouglasPeucker(RamerDouglasPeucker ramerDouglasPeucker) { + this.ramerDouglasPeucker = ramerDouglasPeucker; return this; } @@ -99,6 +99,7 @@ public ResponsePath doWork(PointList waypoints, List paths, EncodedValueLo InstructionList fullInstructions = new InstructionList(tr); PointList fullPoints = PointList.EMPTY; List description = new ArrayList<>(); + List wayPointIndices = new ArrayList<>(); for (int pathIndex = 0; pathIndex < paths.size(); pathIndex++) { Path path = paths.get(pathIndex); if (!path.isFound()) { @@ -135,18 +136,18 @@ public ResponsePath doWork(PointList waypoints, List paths, EncodedValueLo } fullPoints.add(tmpPoints); - responsePath.addPathDetails(PathDetailsFromEdges.calcDetails(path, evLookup, weighting, requestedPathDetails, pathBuilderFactory, origPoints)); + responsePath.addPathDetails(PathDetailsFromEdges.calcDetails(path, evLookup, weighting, requestedPathDetails, pathBuilderFactory, origPoints, graph)); + wayPointIndices.add(origPoints); + if (pathIndex == paths.size() - 1) + wayPointIndices.add(fullPoints.size() - 1); origPoints = fullPoints.size(); } allFound = allFound && path.isFound(); } - if (!fullPoints.isEmpty()) { - responsePath.addDebugInfo("simplify (" + origPoints + "->" + fullPoints.size() + ")"); - if (fullPoints.is3D) - calcAscendDescend(responsePath, fullPoints); - } + if (!fullPoints.isEmpty() && fullPoints.is3D) + calcAscendDescend(responsePath, fullPoints); if (enableInstructions) { fullInstructions = updateInstructionsWithContext(fullInstructions); @@ -154,7 +155,16 @@ public ResponsePath doWork(PointList waypoints, List paths, EncodedValueLo } if (!allFound) { - responsePath.addError(new ConnectionNotFoundException("Connection between locations not found", Collections.emptyMap())); + responsePath.addError(new ConnectionNotFoundException("Connection between locations not found", Collections.emptyMap())); + } + + // make sure the way point indices actually point to the points in waypoints... + if (allFound && !waypoints.isEmpty()) { // we use empty waypoints for map-matching... + for (int i = 0; i < wayPointIndices.size(); i++) { + int index = wayPointIndices.get(i); + if (waypoints.getLat(i) != fullPoints.getLat(index) || waypoints.getLon(i) != fullPoints.getLon(index)) + throw new IllegalStateException("waypoints are not included in points, or waypoint indices are wrong"); + } } responsePath.setDescription(description). @@ -162,10 +172,11 @@ public ResponsePath doWork(PointList waypoints, List paths, EncodedValueLo setRouteWeight(fullWeight). setDistance(fullDistance). setTime(fullTimeInMillis). - setWaypoints(waypoints); + setWaypoints(waypoints). + setWaypointIndices(wayPointIndices); if (allFound && simplifyResponse && (calcPoints || enableInstructions)) { - PathSimplification.simplify(responsePath, douglasPeucker, enableInstructions); + PathSimplification.simplify(responsePath, ramerDouglasPeucker, enableInstructions); } return responsePath; } diff --git a/core/src/main/java/com/graphhopper/util/PathSimplification.java b/core/src/main/java/com/graphhopper/util/PathSimplification.java index 393e4e91c28..c54efd20e1c 100644 --- a/core/src/main/java/com/graphhopper/util/PathSimplification.java +++ b/core/src/main/java/com/graphhopper/util/PathSimplification.java @@ -25,7 +25,7 @@ import java.util.Map; /** - * This class simplifies the path, using {@link DouglasPeucker}, but also considers a given list of partitions of + * This class simplifies the path, using {@link RamerDouglasPeucker}, but also considers a given list of partitions of * the path. Each partition separates the points of the path into non-overlapping intervals and the simplification is * done such that we never simplify across the boundaries of these intervals. This is important, because the points * at the interval boundaries must not be removed, e.g. when they are referenced by instructions. @@ -49,7 +49,7 @@ public class PathSimplification { * @see PathSimplification */ private final List partitions; - private final DouglasPeucker douglasPeucker; + private final RamerDouglasPeucker ramerDouglasPeucker; // temporary variables used when traversing the different partitions private final int numPartitions; @@ -57,16 +57,41 @@ public class PathSimplification { private final int[] currIntervalStart; private final int[] currIntervalEnd; private final boolean[] partitionFinished; - // keep track of how many points were removed by Douglas-Peucker in the current and previous intervals + // keep track of how many points were removed by Ramer-Douglas-Peucker in the current and previous intervals private final int[] removedPointsInCurrInterval; private final int[] removedPointsInPrevIntervals; /** * Convenience method used to obtain the partitions from a calculated path with details and instructions */ - public static PointList simplify(ResponsePath responsePath, DouglasPeucker douglasPeucker, boolean enableInstructions) { + public static PointList simplify(ResponsePath responsePath, RamerDouglasPeucker ramerDouglasPeucker, boolean enableInstructions) { final PointList pointList = responsePath.getPoints(); List partitions = new ArrayList<>(); + + // make sure all waypoints are retained in the simplified point list + // we copy the waypoint indices into temporary intervals where they will be mutated by the simplification, + // afterwards we need to update the way point indices accordingly. + List intervals = new ArrayList<>(); + for (int i = 0; i < responsePath.getWaypointIndices().size() - 1; i++) + intervals.add(new Interval(responsePath.getWaypointIndices().get(i), responsePath.getWaypointIndices().get(i + 1))); + partitions.add(new Partition() { + @Override + public int size() { + return intervals.size(); + } + + @Override + public int getIntervalLength(int index) { + return intervals.get(index).end - intervals.get(index).start; + } + + @Override + public void setInterval(int index, int start, int end) { + intervals.get(index).start = start; + intervals.get(index).end = end; + } + }); + // todo: maybe this code can be simplified if path details and instructions would be merged, see #1121 if (enableInstructions) { final InstructionList instructions = responsePath.getInstructions(); @@ -123,21 +148,28 @@ public void setInterval(int index, int start, int end) { }); } - simplify(responsePath.getPoints(), partitions, douglasPeucker); + simplify(responsePath.getPoints(), partitions, ramerDouglasPeucker); + + List simplifiedWaypointIndices = new ArrayList<>(); + simplifiedWaypointIndices.add(intervals.get(0).start); + for (Interval interval : intervals) + simplifiedWaypointIndices.add(interval.end); + responsePath.setWaypointIndices(simplifiedWaypointIndices); + assertConsistencyOfPathDetails(responsePath.getPathDetails()); if (enableInstructions) assertConsistencyOfInstructions(responsePath.getInstructions(), responsePath.getPoints().size()); return pointList; } - public static void simplify(PointList pointList, List partitions, DouglasPeucker douglasPeucker) { - new PathSimplification(pointList, partitions, douglasPeucker).simplify(); + public static void simplify(PointList pointList, List partitions, RamerDouglasPeucker ramerDouglasPeucker) { + new PathSimplification(pointList, partitions, ramerDouglasPeucker).simplify(); } - private PathSimplification(PointList pointList, List partitions, DouglasPeucker douglasPeucker) { + private PathSimplification(PointList pointList, List partitions, RamerDouglasPeucker ramerDouglasPeucker) { this.pointList = pointList; this.partitions = partitions; - this.douglasPeucker = douglasPeucker; + this.ramerDouglasPeucker = ramerDouglasPeucker; numPartitions = this.partitions.size(); currIntervalIndex = new int[numPartitions]; currIntervalStart = new int[numPartitions]; @@ -155,19 +187,19 @@ private void simplify() { // no partitions -> no constraints, just simplify the entire point list if (partitions.isEmpty()) { - douglasPeucker.simplify(pointList, 0, pointList.size() - 1); + ramerDouglasPeucker.simplify(pointList, 0, pointList.size() - 1); pointList.makeImmutable(); return; } - // Douglas-Peucker never removes the first/last point of a given interval, so as long as we only run it + // Ramer-Douglas-Peucker never removes the first/last point of a given interval, so as long as we only run it // on each interval we can be sure that the interval boundaries will remain in the point list. // Whenever we remove points from an interval we have to update the interval indices of all partitions. // For example if an interval goes from point 4 to 9 and we remove points 5 and 7 we have to update the interval // to [4,7]. // The basic idea to do this is as follows: We iterate through the point list and whenever we hit an interval - // end (q) in one of the partitions we run Douglas-Peucker for the interval [p,q], where p is the point where the - // last interval ended. We keep track of the number of removed points in the current and previous intervals + // end (q) in one of the partitions we run Ramer-Douglas-Peucker for the interval [p,q], where p is the point where + // the last interval ended. We keep track of the number of removed points in the current and previous intervals // to be able to calculate the updated indices. // prepare for the first interval in each partition @@ -179,7 +211,7 @@ private void simplify() { // iterate the point list and simplify and update the intervals on the go for (int p = 0; p < pointList.size(); p++) { int removed = 0; - // first we check if we hit the end of an interval for one of the partitions and run Douglas-Peucker if we do + // first we check if we hit the end of an interval for one of the partitions and run Ramer-Douglas-Peucker if we do for (int s = 0; s < numPartitions; s++) { if (partitionFinished[s]) { continue; @@ -190,7 +222,7 @@ private void simplify() { // see #1764. Note that since the point list does not get compressed here yet we have to keep track // of the total number of removed points to calculate the new interval boundaries later final boolean compress = false; - removed = douglasPeucker.simplify(pointList, intervalStart, currIntervalEnd[s], compress); + removed = ramerDouglasPeucker.simplify(pointList, intervalStart, currIntervalEnd[s], compress); intervalStart = p; break; } @@ -218,7 +250,7 @@ private void simplify() { // now we finally have to compress the pointList (actually remove the deleted points). note only after this // call the (now shifted) indices in path details and instructions are correct - DouglasPeucker.removeNaN(pointList); + RamerDouglasPeucker.removeNaN(pointList); // Make sure that the instruction references are not broken pointList.makeImmutable(); @@ -316,4 +348,18 @@ interface Partition { void setInterval(int index, int start, int end); } + public static class Interval { + public int start; + public int end; + + public Interval(int start, int end) { + this.start = start; + this.end = end; + } + + @Override + public String toString() { + return "[" + start + ", " + end + "]"; + } + } } diff --git a/core/src/main/java/com/graphhopper/util/DouglasPeucker.java b/core/src/main/java/com/graphhopper/util/RamerDouglasPeucker.java similarity index 93% rename from core/src/main/java/com/graphhopper/util/DouglasPeucker.java rename to core/src/main/java/com/graphhopper/util/RamerDouglasPeucker.java index b82624f8ca0..779848e6f51 100644 --- a/core/src/main/java/com/graphhopper/util/DouglasPeucker.java +++ b/core/src/main/java/com/graphhopper/util/RamerDouglasPeucker.java @@ -26,14 +26,14 @@ * * @author Peter Karich */ -public class DouglasPeucker { +public class RamerDouglasPeucker { private double normedMaxDist; private double elevationMaxDistance; private double maxDistance; private DistanceCalc calc; private boolean approx; - public DouglasPeucker() { + public RamerDouglasPeucker() { setApproximation(true); // 1m setMaxDistance(1); @@ -52,7 +52,7 @@ public void setApproximation(boolean a) { /** * maximum distance of discrepancy (from the normal way) in meter */ - public DouglasPeucker setMaxDistance(double dist) { + public RamerDouglasPeucker setMaxDistance(double dist) { this.normedMaxDist = calc.calcNormalizedDist(dist); this.maxDistance = dist; return this; @@ -61,7 +61,7 @@ public DouglasPeucker setMaxDistance(double dist) { /** * maximum elevation distance of discrepancy (from the normal way) in meters */ - public DouglasPeucker setElevationMaxDistance(double dist) { + public RamerDouglasPeucker setElevationMaxDistance(double dist) { this.elevationMaxDistance = dist; return this; } @@ -69,7 +69,7 @@ public DouglasPeucker setElevationMaxDistance(double dist) { /** * Simplifies the points, from index 0 to size-1. *

- * It is a wrapper method for {@link DouglasPeucker#simplify(PointList, int, int)}. + * It is a wrapper method for {@link RamerDouglasPeucker#simplify(PointList, int, int)}. * * @return The number removed points */ @@ -88,7 +88,7 @@ public int simplify(PointList points, int fromIndex, int lastIndex) { * @param points The PointList to simplify * @param fromIndex Start index to simplify, should be <= lastIndex * @param lastIndex Simplify up to this index - * @param compress Whether or not the points shall be compressed or not, if set to false no points + * @param compress Whether the points shall be compressed or not, if set to false no points * are actually removed, but instead their lat/lon/ele is only set to NaN * @return The number of removed points */ diff --git a/core/src/main/java/com/graphhopper/util/StopWatch.java b/core/src/main/java/com/graphhopper/util/StopWatch.java index e4baa7bdb7b..23a6cd99bd7 100644 --- a/core/src/main/java/com/graphhopper/util/StopWatch.java +++ b/core/src/main/java/com/graphhopper/util/StopWatch.java @@ -81,6 +81,13 @@ public long getMillis() { return elapsedNanos / 1_000_000; } + /** + * returns the elapsed time in ms but includes the fraction as well to get a precise value + */ + public double getMillisDouble() { + return elapsedNanos / 1_000_000.0; + } + public long getNanos() { return elapsedNanos; } diff --git a/core/src/main/java/com/graphhopper/util/TranslationMap.java b/core/src/main/java/com/graphhopper/util/TranslationMap.java index 8fb172603d6..44272da22e3 100644 --- a/core/src/main/java/com/graphhopper/util/TranslationMap.java +++ b/core/src/main/java/com/graphhopper/util/TranslationMap.java @@ -34,7 +34,7 @@ public class TranslationMap { // ISO codes (639-1), use 'en_US' as reference private static final List LOCALES = Arrays.asList("ar", "ast", "bg", "bn_BN", "ca", "cs_CZ", "da_DK", "de_DE", "el", "eo", "es", "en_US", "fa", "fil", "fi", - "fr_FR", "fr_CH", "gl", "he", "hr_HR", "hsb", "hu_HU", "in_ID", "it", "ja", "ko", "lt_LT", "ne", + "fr_FR", "fr_CH", "gl", "he", "hr_HR", "hsb", "hu_HU", "in_ID", "it", "ja", "ko", "lt_LT", "nb_NO", "ne", "nl", "pl_PL", "pt_BR", "pt_PT", "ro", "ru", "sk", "sl_SI", "sr_RS", "sv_SE", "tr", "uk", "vi_VN", "zh_CN", "zh_HK", "zh_TW"); private final Map translations = new HashMap<>(); diff --git a/core/src/main/java/com/graphhopper/util/details/AbstractPathDetailsBuilder.java b/core/src/main/java/com/graphhopper/util/details/AbstractPathDetailsBuilder.java index 1912eadf88d..ad8f97ee70a 100644 --- a/core/src/main/java/com/graphhopper/util/details/AbstractPathDetailsBuilder.java +++ b/core/src/main/java/com/graphhopper/util/details/AbstractPathDetailsBuilder.java @@ -33,7 +33,7 @@ public abstract class AbstractPathDetailsBuilder implements PathDetailsBuilder { private final String name; private boolean isOpen = false; private PathDetail currentDetail; - private List pathDetails = new ArrayList<>(); + private final List pathDetails = new ArrayList<>(); public AbstractPathDetailsBuilder(String name) { this.name = name; diff --git a/core/src/main/java/com/graphhopper/util/details/AverageSpeedDetails.java b/core/src/main/java/com/graphhopper/util/details/AverageSpeedDetails.java index 4abf1b837f9..db5c418a4b3 100644 --- a/core/src/main/java/com/graphhopper/util/details/AverageSpeedDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/AverageSpeedDetails.java @@ -1,6 +1,24 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.graphhopper.util.details; import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; @@ -12,7 +30,7 @@ public class AverageSpeedDetails extends AbstractPathDetailsBuilder { private final double precision; private Double decimalValue; // will include the turn time penalty - private int prevEdgeId = -1; + private int prevEdgeId = EdgeIterator.NO_EDGE; public AverageSpeedDetails(Weighting weighting) { this(weighting, 0.1); @@ -35,22 +53,21 @@ protected Object getCurrentValue() { @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { - // for very short edges we might not be able to calculate a proper value for speed. dividing by calcMillis can - // even lead to speed=Infinity -> just ignore these cases here, see #1848 + // For very short edges we might not be able to calculate a proper value for speed. dividing by calcMillis can + // even lead to an infinity speed. So, just ignore these edges, see #1848 and #2620 and #2636. final double distance = edge.getDistance(); - if (distance < 0.1) { - if (decimalValue != null) - return false; - - // in case this is the first edge we have to return some value - decimalValue = null; + long time = GHUtility.calcMillisWithTurnMillis(weighting, edge, false, prevEdgeId); + if (distance < 0.01 || time < 1) { + prevEdgeId = edge.getEdge(); + if (decimalValue != null) return false; + // in case this is the first edge we return decimalValue=null return true; } - double tmpVal = distance / GHUtility.calcMillisWithTurnMillis(weighting, edge, false, prevEdgeId) * 3600; + double speed = distance / time * 3600; prevEdgeId = edge.getEdge(); - if (decimalValue == null || Math.abs(tmpVal - decimalValue) >= precision) { - this.decimalValue = Math.round(tmpVal / precision) * precision; + if (decimalValue == null || Math.abs(speed - decimalValue) >= precision) { + this.decimalValue = Math.round(speed / precision) * precision; return true; } return false; diff --git a/core/src/main/java/com/graphhopper/util/details/ConstantDetailsBuilder.java b/core/src/main/java/com/graphhopper/util/details/ConstantDetailsBuilder.java new file mode 100644 index 00000000000..d308e22acb7 --- /dev/null +++ b/core/src/main/java/com/graphhopper/util/details/ConstantDetailsBuilder.java @@ -0,0 +1,48 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.util.details; + +import com.graphhopper.util.EdgeIteratorState; + +/** + * Simply returns the same value everywhere, useful to represent values that are the same between two (via-)points + */ +public class ConstantDetailsBuilder extends AbstractPathDetailsBuilder { + private final Object value; + private boolean firstEdge = true; + + public ConstantDetailsBuilder(String name, Object value) { + super(name); + this.value = value; + } + + @Override + protected Object getCurrentValue() { + return value; + } + + @Override + public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { + if (firstEdge) { + firstEdge = false; + return true; + } else + return false; + } +} diff --git a/core/src/main/java/com/graphhopper/util/details/DecimalDetails.java b/core/src/main/java/com/graphhopper/util/details/DecimalDetails.java index 76ce84e8724..155bf7f7ffd 100644 --- a/core/src/main/java/com/graphhopper/util/details/DecimalDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/DecimalDetails.java @@ -23,7 +23,7 @@ public class DecimalDetails extends AbstractPathDetailsBuilder { private final DecimalEncodedValue ev; - private double decimalValue = -1; + private Double decimalValue; private final String infinityJsonValue; private final double precision; @@ -55,7 +55,7 @@ protected Object getCurrentValue() { @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { double tmpVal = edge.get(ev); - if (Math.abs(tmpVal - decimalValue) >= precision) { + if (decimalValue == null || Math.abs(tmpVal - decimalValue) >= precision) { this.decimalValue = Double.isInfinite(tmpVal) ? tmpVal : Math.round(tmpVal / precision) * precision; return true; } diff --git a/core/src/main/java/com/graphhopper/util/details/DistanceDetails.java b/core/src/main/java/com/graphhopper/util/details/DistanceDetails.java index c36e1cf4585..4380bc498d3 100644 --- a/core/src/main/java/com/graphhopper/util/details/DistanceDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/DistanceDetails.java @@ -23,7 +23,6 @@ public class DistanceDetails extends AbstractPathDetailsBuilder { - private int edgeId = -1; private double distance = 0; public DistanceDetails() { @@ -32,12 +31,8 @@ public DistanceDetails() { @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { - if (edge.getEdge() != edgeId) { - edgeId = edge.getEdge(); - distance = edge.getDistance(); - return true; - } - return false; + distance = edge.getDistance(); + return true; } @Override diff --git a/core/src/main/java/com/graphhopper/util/details/EdgeIdDetails.java b/core/src/main/java/com/graphhopper/util/details/EdgeIdDetails.java index f093c82e70e..220535ba848 100644 --- a/core/src/main/java/com/graphhopper/util/details/EdgeIdDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/EdgeIdDetails.java @@ -18,6 +18,7 @@ package com.graphhopper.util.details; import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState; +import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; @@ -30,7 +31,7 @@ */ public class EdgeIdDetails extends AbstractPathDetailsBuilder { - private int edgeId = -1; + private int edgeId = EdgeIterator.NO_EDGE; public EdgeIdDetails() { super(EDGE_ID); @@ -38,12 +39,8 @@ public EdgeIdDetails() { @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { - int thisEdgeId = edgeId(edge); - if (thisEdgeId != edgeId) { - edgeId = thisEdgeId; - return true; - } - return false; + edgeId = edgeId(edge); + return true; } private int edgeId(EdgeIteratorState edge) { diff --git a/core/src/main/java/com/graphhopper/util/details/EdgeKeyDetails.java b/core/src/main/java/com/graphhopper/util/details/EdgeKeyDetails.java index 83cfc3a8a18..0c08f4ea854 100644 --- a/core/src/main/java/com/graphhopper/util/details/EdgeKeyDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/EdgeKeyDetails.java @@ -19,24 +19,23 @@ import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState; import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.GHUtility; import static com.graphhopper.util.Parameters.Details.EDGE_KEY; public class EdgeKeyDetails extends AbstractPathDetailsBuilder { - private int edgeKey; + private int edgeKey = -1; public EdgeKeyDetails() { super(EDGE_KEY); - edgeKey = -1; } @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { - int newEdgeKey = getEdgeKey(edge); - if (newEdgeKey != edgeKey) { - edgeKey = newEdgeKey; + int newKey = edge instanceof VirtualEdgeIteratorState + ? ((VirtualEdgeIteratorState) edge).getOriginalEdgeKey() : edge.getEdgeKey(); + if (newKey != edgeKey) { // do not duplicate path detail if going over via point (two virtual edges) + edgeKey = newKey; return true; } return false; @@ -46,13 +45,4 @@ public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { public Object getCurrentValue() { return this.edgeKey; } - - static int getEdgeKey(EdgeIteratorState edge) { - if (edge instanceof VirtualEdgeIteratorState) { - return ((VirtualEdgeIteratorState) edge).getOriginalEdgeKey(); - } else { - return edge.getEdge() * 2 + (edge.get(EdgeIteratorState.REVERSE_STATE) ? 1 : 0); - } - } - } diff --git a/core/src/main/java/com/graphhopper/util/details/EnumDetails.java b/core/src/main/java/com/graphhopper/util/details/EnumDetails.java index 985efb2e9d4..5b9bc6f6572 100644 --- a/core/src/main/java/com/graphhopper/util/details/EnumDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/EnumDetails.java @@ -23,7 +23,7 @@ public class EnumDetails extends AbstractPathDetailsBuilder { private final EnumEncodedValue ev; - private Enum objVal = null; + private E objVal; public EnumDetails(String name, EnumEncodedValue ev) { super(name); diff --git a/core/src/main/java/com/graphhopper/util/details/IntDetails.java b/core/src/main/java/com/graphhopper/util/details/IntDetails.java index 97665831f3a..dfa29fdb674 100644 --- a/core/src/main/java/com/graphhopper/util/details/IntDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/IntDetails.java @@ -23,7 +23,7 @@ public class IntDetails extends AbstractPathDetailsBuilder { private final IntEncodedValue ev; - private int intVal = -1; + private Integer intVal; public IntDetails(String name, IntEncodedValue ev) { super(name); @@ -38,7 +38,7 @@ protected Object getCurrentValue() { @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { int val = edge.get(ev); - if (val != intVal) { + if (intVal == null || val != intVal) { this.intVal = val; return true; } diff --git a/core/src/main/java/com/graphhopper/util/details/IntersectionDetails.java b/core/src/main/java/com/graphhopper/util/details/IntersectionDetails.java new file mode 100644 index 00000000000..71b9310e9e3 --- /dev/null +++ b/core/src/main/java/com/graphhopper/util/details/IntersectionDetails.java @@ -0,0 +1,154 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.util.details; + +import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState; +import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.storage.Graph; +import com.graphhopper.storage.NodeAccess; +import com.graphhopper.util.*; +import com.graphhopper.util.shapes.GHPoint; + +import java.util.*; + +import static com.graphhopper.util.Parameters.Details.INTERSECTION; + +/** + * Calculate the intersections for a route. Every change of the edge id is considered an intersection. + *

+ * The format is inspired by the format that is consumed by Maplibre Navigation SDK. + *

+ * Explanation of the format: + * - entries contain an array of the edges at that intersection. They are sorted by bearing, starting from 0 (which is 0° north) to 359. Every edge that we can turn onto is marked with “true” in the array. + * - bearings contain an array of the edges at that intersection. They are sorted by bearing, starting from 0 (which is 0° north) to 359. The array contains the bearings of each edge at that intersection. + * - in marks the index in the “bearings” edge we are coming from. + * - out the index we are going to. + * + * @author Robin Boldt + */ +public class IntersectionDetails extends AbstractPathDetailsBuilder { + + private int fromEdge = -1; + + private Map intersectionMap = new HashMap<>(); + + private final EdgeExplorer crossingExplorer; + private final NodeAccess nodeAccess; + private final Weighting weighting; + + public IntersectionDetails(Graph graph, Weighting weighting) { + super(INTERSECTION); + + crossingExplorer = graph.createEdgeExplorer(); + nodeAccess = graph.getNodeAccess(); + this.weighting = weighting; + } + + @Override + public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { + int toEdge = edgeId(edge); + if (toEdge != fromEdge) { + // Important to create a new map and not to clean the old map! + intersectionMap = new HashMap<>(); + + List intersectingEdges = new ArrayList<>(); + + int baseNode = edge.getBaseNode(); + EdgeIteratorState tmpEdge; + + double startLat = nodeAccess.getLat(baseNode); + double startLon = nodeAccess.getLon(baseNode); + + + EdgeIterator edgeIter = crossingExplorer.setBaseNode(baseNode); + while (edgeIter.next()) { + // We need to call detach to get the edgeId, as we need to check for VirtualEdgeIteratorState in #edgeId(), see discussion in #2590 + tmpEdge = edgeIter.detach(false); + + IntersectionValues intersectionValues = new IntersectionValues(); + intersectionValues.bearing = calculateBearing(startLat, startLon, tmpEdge); + intersectionValues.in = edgeId(tmpEdge) == fromEdge; + intersectionValues.out = edgeId(tmpEdge) == edgeId(edge); + // The in edge is always false, this means that u-turns are not considered as possible turning option + intersectionValues.entry = !intersectionValues.in && Double.isFinite(weighting.calcEdgeWeightWithAccess(tmpEdge, false)); + + intersectingEdges.add(intersectionValues); + } + + intersectingEdges.sort(null); + + List bearings = new ArrayList<>(intersectingEdges.size()); + List entries = new ArrayList<>(intersectingEdges.size()); + + for (int i = 0; i < intersectingEdges.size(); i++) { + IntersectionValues intersectionValues = intersectingEdges.get(i); + bearings.add(intersectionValues.bearing); + entries.add(intersectionValues.entry); + if (intersectionValues.in) { + intersectionMap.put("in", i); + } + if (intersectionValues.out) { + intersectionMap.put("out", i); + } + } + + intersectionMap.put("bearings", bearings); + intersectionMap.put("entries", entries); + + fromEdge = toEdge; + return true; + } + return false; + } + + private int calculateBearing(double startLat, double startLon, EdgeIteratorState tmpEdge) { + PointList wayGeo = tmpEdge.fetchWayGeometry(FetchMode.PILLAR_AND_ADJ); + final double latitude = wayGeo.getLat(0); + final double longitude = wayGeo.getLon(0); + return (int) Math.round(AngleCalc.ANGLE_CALC.calcAzimuth(startLat, startLon, latitude, longitude)); + } + + private int edgeId(EdgeIteratorState edge) { + if (edge instanceof VirtualEdgeIteratorState) { + return GHUtility.getEdgeFromEdgeKey(((VirtualEdgeIteratorState) edge).getOriginalEdgeKey()); + } else { + return edge.getEdge(); + } + } + + @Override + public Object getCurrentValue() { + return this.intersectionMap; + } + + private class IntersectionValues implements Comparable { + + public int bearing; + public boolean entry; + public boolean in; + public boolean out; + + @Override + public int compareTo(Object o) { + if (o instanceof IntersectionValues) { + return Integer.compare(this.bearing, ((IntersectionValues) o).bearing); + } + return 0; + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/graphhopper/util/details/StreetNameDetails.java b/core/src/main/java/com/graphhopper/util/details/KVStringDetails.java similarity index 70% rename from core/src/main/java/com/graphhopper/util/details/StreetNameDetails.java rename to core/src/main/java/com/graphhopper/util/details/KVStringDetails.java index ca80323f1a3..a599df1fde4 100644 --- a/core/src/main/java/com/graphhopper/util/details/StreetNameDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/KVStringDetails.java @@ -19,25 +19,28 @@ import com.graphhopper.util.EdgeIteratorState; -import static com.graphhopper.util.Parameters.Details.STREET_NAME; - /** - * Calculate the speed name segments of a Path + * Return a String value from the key-values * * @author Robin Boldt */ -public class StreetNameDetails extends AbstractPathDetailsBuilder { +public class KVStringDetails extends AbstractPathDetailsBuilder { - private String curStreetName = null; + private String curString; - public StreetNameDetails() { - super(STREET_NAME); + public KVStringDetails(String name) { + super(name); } @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { - if (curStreetName == null || !curStreetName.equals(edge.getName())) { - curStreetName = edge.getName(); + if (curString == null) { + curString = (String) edge.getValue(getName()); + return true; + } + String val = (String) edge.getValue(getName()); + if (!curString.equals(val)) { + curString = val; return true; } return false; @@ -45,6 +48,6 @@ public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { @Override public Object getCurrentValue() { - return this.curStreetName; + return this.curString; } } diff --git a/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java b/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java index 910d99ca0e0..6e456a72876 100644 --- a/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java +++ b/core/src/main/java/com/graphhopper/util/details/PathDetailsBuilderFactory.java @@ -17,14 +17,14 @@ */ package com.graphhopper.util.details; +import com.graphhopper.routing.Path; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.storage.Graph; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import static com.graphhopper.routing.util.EncodingManager.getKey; import static com.graphhopper.util.Parameters.Details.*; /** @@ -34,14 +34,25 @@ */ public class PathDetailsBuilderFactory { - public List createPathDetailsBuilders(List requestedPathDetails, EncodedValueLookup evl, Weighting weighting) { + public List createPathDetailsBuilders(List requestedPathDetails, Path path, EncodedValueLookup evl, Weighting weighting, Graph graph) { List builders = new ArrayList<>(); - if (requestedPathDetails.contains(AVERAGE_SPEED)) - builders.add(new AverageSpeedDetails(weighting)); + if (requestedPathDetails.contains(LEG_TIME)) + builders.add(new ConstantDetailsBuilder(LEG_TIME, path.getTime())); + if (requestedPathDetails.contains(LEG_DISTANCE)) + builders.add(new ConstantDetailsBuilder(LEG_DISTANCE, path.getDistance())); + if (requestedPathDetails.contains(LEG_WEIGHT)) + builders.add(new ConstantDetailsBuilder(LEG_WEIGHT, path.getWeight())); if (requestedPathDetails.contains(STREET_NAME)) - builders.add(new StreetNameDetails()); + builders.add(new KVStringDetails(STREET_NAME)); + if (requestedPathDetails.contains(STREET_REF)) + builders.add(new KVStringDetails(STREET_REF)); + if (requestedPathDetails.contains(STREET_DESTINATION)) + builders.add(new KVStringDetails(STREET_DESTINATION)); + + if (requestedPathDetails.contains(AVERAGE_SPEED)) + builders.add(new AverageSpeedDetails(weighting)); if (requestedPathDetails.contains(EDGE_ID)) builders.add(new EdgeIdDetails()); @@ -58,37 +69,32 @@ public List createPathDetailsBuilders(List requested if (requestedPathDetails.contains(DISTANCE)) builders.add(new DistanceDetails()); - for (String checkSuffix : requestedPathDetails) { - if (checkSuffix.endsWith(getKey("", "priority")) && evl.hasEncodedValue(checkSuffix)) - builders.add(new DecimalDetails(checkSuffix, evl.getDecimalEncodedValue(checkSuffix))); - } - - for (String key : Arrays.asList(MaxSpeed.KEY, MaxWidth.KEY, MaxHeight.KEY, MaxWeight.KEY, - MaxAxleLoad.KEY, MaxLength.KEY)) { - if (requestedPathDetails.contains(key) && evl.hasEncodedValue(key)) - builders.add(new DecimalDetails(key, evl.getDecimalEncodedValue(key))); + if (requestedPathDetails.contains(INTERSECTION)) + builders.add(new IntersectionDetails(graph, weighting)); + + for (String pathDetail : requestedPathDetails) { + if (!evl.hasEncodedValue(pathDetail)) continue; // path details like "time" won't be found + + EncodedValue ev = evl.getEncodedValue(pathDetail, EncodedValue.class); + if (ev instanceof DecimalEncodedValue) + builders.add(new DecimalDetails(pathDetail, (DecimalEncodedValue) ev)); + else if (ev instanceof BooleanEncodedValue) + builders.add(new BooleanDetails(pathDetail, (BooleanEncodedValue) ev)); + else if (ev instanceof EnumEncodedValue) + builders.add(new EnumDetails<>(pathDetail, (EnumEncodedValue) ev)); + else if (ev instanceof StringEncodedValue) + builders.add(new StringDetails(pathDetail, (StringEncodedValue) ev)); + else if (ev instanceof IntEncodedValue) + builders.add(new IntDetails(pathDetail, (IntEncodedValue) ev)); + else throw new IllegalArgumentException("unknown EncodedValue class " + ev.getClass().getName()); } - for (String key : Arrays.asList(Roundabout.KEY, RoadClassLink.KEY, GetOffBike.KEY, "car_access", "bike_access")) { - if (requestedPathDetails.contains(key) && evl.hasEncodedValue(key)) - builders.add(new BooleanDetails(key, evl.getBooleanEncodedValue(key))); - } - - for (String key : Arrays.asList(RoadClass.KEY, RoadEnvironment.KEY, Surface.KEY, Smoothness.KEY, RoadAccess.KEY, - BikeNetwork.KEY, FootNetwork.KEY, Toll.KEY, TrackType.KEY, Hazmat.KEY, HazmatTunnel.KEY, - HazmatWater.KEY, Country.KEY)) { - if (requestedPathDetails.contains(key) && evl.hasEncodedValue(key)) - builders.add(new EnumDetails<>(key, evl.getEnumEncodedValue(key, Enum.class))); - } - - for (String key : Arrays.asList(MtbRating.KEY, HikeRating.KEY, HorseRating.KEY, Lanes.KEY)) { - if (requestedPathDetails.contains(key) && evl.hasEncodedValue(key)) - builders.add(new IntDetails(key, evl.getIntEncodedValue(key))); - } - - if (requestedPathDetails.size() != builders.size()) { - throw new IllegalArgumentException("You requested the details " + requestedPathDetails + " but we could only find " + builders); - } + if (requestedPathDetails.size() > builders.size()) { + ArrayList clonedArr = new ArrayList<>(requestedPathDetails); // avoid changing request parameter + for (PathDetailsBuilder pdb : builders) clonedArr.remove(pdb.getName()); + throw new IllegalArgumentException("Cannot find the path details: " + clonedArr); + } else if (requestedPathDetails.size() < builders.size()) + throw new IllegalStateException("It should not happen that there are more path details added " + builders + " than requested " + requestedPathDetails); return builders; } diff --git a/core/src/main/java/com/graphhopper/util/details/PathDetailsFromEdges.java b/core/src/main/java/com/graphhopper/util/details/PathDetailsFromEdges.java index 2e8328dff27..39da194f606 100644 --- a/core/src/main/java/com/graphhopper/util/details/PathDetailsFromEdges.java +++ b/core/src/main/java/com/graphhopper/util/details/PathDetailsFromEdges.java @@ -20,6 +20,7 @@ import com.graphhopper.routing.Path; import com.graphhopper.routing.ev.EncodedValueLookup; import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.storage.Graph; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.FetchMode; @@ -51,16 +52,15 @@ public PathDetailsFromEdges(List calculators, int previousIn /** * Calculates the PathDetails for a Path. This method will return fast, if there are no calculators. * - * @param path - * @param weighting * @param pathBuilderFactory Generates the relevant PathBuilders * @return List of PathDetails for this Path */ public static Map> calcDetails(Path path, EncodedValueLookup evLookup, Weighting weighting, - List requestedPathDetails, PathDetailsBuilderFactory pathBuilderFactory, int previousIndex) { + List requestedPathDetails, PathDetailsBuilderFactory pathBuilderFactory, + int previousIndex, Graph graph) { if (!path.isFound() || requestedPathDetails.isEmpty()) return Collections.emptyMap(); - List pathBuilders = pathBuilderFactory.createPathDetailsBuilders(requestedPathDetails, evLookup, weighting); + List pathBuilders = pathBuilderFactory.createPathDetailsBuilders(requestedPathDetails, path, evLookup, weighting, graph); if (pathBuilders.isEmpty()) return Collections.emptyMap(); diff --git a/core/src/main/java/com/graphhopper/util/details/StringDetails.java b/core/src/main/java/com/graphhopper/util/details/StringDetails.java index bdeabceacb9..025f40bb4d9 100644 --- a/core/src/main/java/com/graphhopper/util/details/StringDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/StringDetails.java @@ -23,7 +23,7 @@ public class StringDetails extends AbstractPathDetailsBuilder { private final StringEncodedValue ev; - private String currentVal = null; + private String currentVal; public StringDetails(String name, StringEncodedValue ev) { super(name); @@ -39,7 +39,7 @@ protected Object getCurrentValue() { public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { String val = edge.get(ev); // we can use the reference equality here - if (val != currentVal) { + if (!val.equals(currentVal)) { this.currentVal = val; return true; } diff --git a/core/src/main/java/com/graphhopper/util/details/TimeDetails.java b/core/src/main/java/com/graphhopper/util/details/TimeDetails.java index 1a1a486193d..59099e4dcc3 100644 --- a/core/src/main/java/com/graphhopper/util/details/TimeDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/TimeDetails.java @@ -18,6 +18,7 @@ package com.graphhopper.util.details; import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; @@ -31,7 +32,7 @@ public class TimeDetails extends AbstractPathDetailsBuilder { private final Weighting weighting; - private int prevEdgeId = -1; + private int prevEdgeId = EdgeIterator.NO_EDGE; // will include the turn time penalty private long time = 0; @@ -42,12 +43,9 @@ public TimeDetails(Weighting weighting) { @Override public boolean isEdgeDifferentToLastEdge(EdgeIteratorState edge) { - if (edge.getEdge() != prevEdgeId) { - time = GHUtility.calcMillisWithTurnMillis(weighting, edge, false, prevEdgeId); - prevEdgeId = edge.getEdge(); - return true; - } - return false; + time = GHUtility.calcMillisWithTurnMillis(weighting, edge, false, prevEdgeId); + prevEdgeId = edge.getEdge(); + return true; } @Override diff --git a/core/src/main/java/com/graphhopper/util/details/WeightDetails.java b/core/src/main/java/com/graphhopper/util/details/WeightDetails.java index 437afada5ca..1a4980ff950 100644 --- a/core/src/main/java/com/graphhopper/util/details/WeightDetails.java +++ b/core/src/main/java/com/graphhopper/util/details/WeightDetails.java @@ -33,7 +33,7 @@ public class WeightDetails extends AbstractPathDetailsBuilder { private final Weighting weighting; private int edgeId = EdgeIterator.NO_EDGE; - private double weight = Double.NaN; + private Double weight; public WeightDetails(Weighting weighting) { super(WEIGHT); diff --git a/core/src/main/java/com/graphhopper/util/shapes/Polygon.java b/core/src/main/java/com/graphhopper/util/shapes/Polygon.java index 7aa9a744745..e58e1137db7 100644 --- a/core/src/main/java/com/graphhopper/util/shapes/Polygon.java +++ b/core/src/main/java/com/graphhopper/util/shapes/Polygon.java @@ -35,15 +35,17 @@ */ public class Polygon implements Shape { - private final GeometryFactory factory = new GeometryFactory(); + private static final GeometryFactory factory = new GeometryFactory(); public final PreparedGeometry prepPolygon; public final boolean rectangle; public final Envelope envelope; + public final BBox bbox; public Polygon(PreparedPolygon prepPolygon) { this.prepPolygon = prepPolygon; this.rectangle = prepPolygon.getGeometry().isRectangle(); this.envelope = prepPolygon.getGeometry().getEnvelopeInternal(); + this.bbox = BBox.fromEnvelope(envelope); } public Polygon(double[] lats, double[] lons) { @@ -61,6 +63,7 @@ public Polygon(double[] lats, double[] lons) { this.prepPolygon = new PreparedPolygon(factory.createPolygon(new PackedCoordinateSequence.Double(coordinates, 2))); this.rectangle = prepPolygon.getGeometry().isRectangle(); this.envelope = prepPolygon.getGeometry().getEnvelopeInternal(); + this.bbox = BBox.fromEnvelope(envelope); } public static Polygon create(org.locationtech.jts.geom.Polygon polygon) { @@ -84,7 +87,7 @@ public boolean contains(double lat, double lon) { @Override public BBox getBounds() { - return new BBox(envelope.getMinX(), envelope.getMaxX(), envelope.getMinY(), envelope.getMaxY()); + return bbox; } public double getMinLat() { @@ -102,7 +105,7 @@ public double getMaxLat() { public double getMaxLon() { return envelope.getMaxX(); } - + public boolean isRectangle() { return rectangle; } @@ -111,20 +114,4 @@ public boolean isRectangle() { public String toString() { return "polygon (" + prepPolygon.getGeometry().getNumPoints() + " points," + prepPolygon.getGeometry().getNumGeometries() + " geometries)"; } - - public static Polygon parsePoints(String pointsStr) { - String[] arr = pointsStr.split(","); - if (arr.length % 2 == 1) - throw new IllegalArgumentException("incorrect polygon specified: " + Arrays.asList(arr)); - - Coordinate[] coordinates = new Coordinate[arr.length / 2 + 1]; - for (int i = 0; i < coordinates.length - 1; i++) { - int arrIndex = i * 2; - coordinates[i] = new Coordinate(Double.parseDouble(arr[arrIndex + 1]), Double.parseDouble(arr[arrIndex])); - } - coordinates[coordinates.length - 1] = coordinates[0]; - - // store more efficient - return new Polygon(new PreparedPolygon(new GeometryFactory().createPolygon(new PackedCoordinateSequence.Double(coordinates, 2)))); - } } diff --git a/core/src/main/resources/com/graphhopper/countries/countries.geojson b/core/src/main/resources/com/graphhopper/countries/countries.geojson index 99dea8455aa..bd2cc5fbfba 100644 --- a/core/src/main/resources/com/graphhopper/countries/countries.geojson +++ b/core/src/main/resources/com/graphhopper/countries/countries.geojson @@ -1,218 +1 @@ -{"type":"FeatureCollection", "features": [ -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-179.3865,-23.786],[-179.3735,-23.879],[-179.341,-23.948],[-179.292,-24.008],[-179.2285,-24.0555],[-179.154,-24.0875],[-179.074,-24.1025],[-178.992,-24.0995],[-178.913,-24.0785],[-178.842,-24.0415],[-178.7825,-23.9895],[-178.739,-23.926],[-178.713,-23.855],[-178.706,-23.78],[-178.7195,-23.706],[-178.7515,-23.6365],[-178.8005,-23.5765],[-178.8645,-23.529],[-178.958,-23.4915],[-179.06,-23.481],[-179.141,-23.493],[-179.234,-23.5325],[-179.2965,-23.581],[-179.3445,-23.642],[-179.375,-23.7115],[-179.3865,-23.786]]],[[[-176.434,-22.3355],[-176.4045,-22.4355],[-176.3605,-22.49],[-176.3025,-22.5285],[-176.235,-22.5475],[-176.1675,-22.546],[-176.0975,-22.523],[-176.0205,-22.4565],[-175.9865,-22.3815],[-175.9925,-22.2765],[-176.033,-22.209],[-176.1075,-22.1535],[-176.1805,-22.1335],[-176.2565,-22.1365],[-176.3265,-22.1605],[-176.4055,-22.2345],[-176.434,-22.3355]]],[[[-175.8855,-15.6275],[-175.87,-15.6885],[-175.8125,-15.7685],[-175.7545,-15.8125],[-175.6695,-15.839],[-175.616,-15.84],[-175.521,-15.8105],[-175.431,-15.7265],[-175.395,-15.621],[-175.3965,-15.5575],[-175.4205,-15.4885],[-175.4795,-15.4205],[-175.5785,-15.3715],[-175.6595,-15.367],[-175.769,-15.4045],[-175.822,-15.4455],[-175.8725,-15.529],[-175.8855,-15.6275]]],[[[-175.621,-20.5565],[-175.588,-20.6615],[-175.541,-20.7115],[-175.486,-20.744],[-175.4145,-20.7675],[-175.293,-20.7535],[-175.2305,-20.715],[-175.182,-20.6525],[-175.1625,-20.5805],[-175.163,-20.5155],[-175.187,-20.443],[-175.238,-20.3845],[-175.3565,-20.3345],[-175.4295,-20.337],[-175.511,-20.367],[-175.555,-20.3995],[-175.6,-20.461],[-175.621,-20.5565]]],[[[-175.5705,-21.0995],[-175.5515,-21.192],[-175.5225,-21.2585],[-175.47,-21.3135],[-175.2755,-21.4325],[-175.1715,-21.47],[-175.1395,-21.5765],[-175.091,-21.629],[-175.026,-21.663],[-174.9525,-21.674],[-174.8985,-21.6675],[-174.805,-21.6345],[-174.751,-21.596],[-174.6975,-21.5135],[-174.6855,-21.443],[-174.6945,-21.308],[-174.708,-21.228],[-174.763,-21.1415],[-174.763,-21.087],[-174.8015,-20.957],[-174.8345,-20.899],[-174.889,-20.852],[-174.9595,-20.8245],[-175.037,-20.8225],[-175.0925,-20.8385],[-175.227,-20.813],[-175.303,-20.827],[-175.413,-20.8725],[-175.479,-20.92],[-175.532,-20.9775],[-175.5625,-21.0395],[-175.5705,-21.0995]]],[[[-175.3185,-19.7385],[-175.3005,-19.8385],[-175.272,-19.883],[-175.2015,-19.949],[-175.0945,-19.987],[-175.01,-19.9845],[-174.9935,-20.041],[-174.9955,-20.153],[-175.0305,-20.2715],[-175.0165,-20.34],[-174.981,-20.401],[-174.9995,-20.484],[-174.9855,-20.5525],[-174.9475,-20.6125],[-174.8905,-20.6565],[-174.778,-20.7025],[-174.7015,-20.704],[-174.6305,-20.682],[-174.572,-20.638],[-174.545,-20.6],[-174.4885,-20.596],[-174.4085,-20.5635],[-174.3535,-20.514],[-174.3135,-20.421],[-174.306,-20.35],[-174.317,-20.252],[-174.348,-20.195],[-174.301,-20.16],[-174.261,-20.1015],[-174.246,-20.049],[-174.152,-19.93],[-174.097,-19.848],[-174.054,-19.714],[-174.0625,-19.628],[-174.0895,-19.569],[-174.144,-19.5015],[-174.2095,-19.461],[-174.3075,-19.4465],[-174.34,-19.4225],[-174.4435,-19.3915],[-174.516,-19.4005],[-174.588,-19.437],[-174.6675,-19.5215],[-174.6925,-19.5715],[-174.8065,-19.5835],[-174.874,-19.4935],[-174.94,-19.458],[-175.0325,-19.446],[-175.108,-19.4645],[-175.1725,-19.508],[-175.2475,-19.576],[-175.2965,-19.652],[-175.3185,-19.7385]]],[[[-174.881,-18.8085],[-174.865,-18.8795],[-174.8315,-18.9355],[-174.782,-18.984],[-174.6935,-19.0225],[-174.627,-19.027],[-174.5465,-19.0075],[-174.4835,-18.966],[-174.4185,-18.862],[-174.409,-18.7915],[-174.4225,-18.73],[-174.4595,-18.67],[-174.556,-18.6005],[-174.636,-18.5815],[-174.7475,-18.602],[-174.836,-18.672],[-174.8695,-18.736],[-174.881,-18.8085]]],[[[-174.5375,-18.028],[-174.5085,-18.1305],[-174.4285,-18.2055],[-174.3835,-18.224],[-174.3315,-18.3065],[-174.269,-18.348],[-174.192,-18.3655],[-174.114,-18.356],[-174.0505,-18.325],[-174.0035,-18.279],[-173.9675,-18.19],[-173.9695,-18.122],[-173.9975,-18.0535],[-174.047,-18.0005],[-174.102,-17.972],[-174.119,-17.927],[-174.1645,-17.869],[-174.219,-17.832],[-174.2835,-17.813],[-174.372,-17.818],[-174.442,-17.849],[-174.5015,-17.9075],[-174.527,-17.961],[-174.5375,-18.028]]],[[[-174.382,-18.8075],[-174.343,-18.907],[-174.2885,-18.957],[-174.0295,-19.057],[-173.964,-19.0575],[-173.894,-19.035],[-173.8365,-18.9915],[-173.7875,-18.9185],[-173.722,-18.761],[-173.71,-18.648],[-173.725,-18.5635],[-173.7825,-18.454],[-173.8665,-18.388],[-173.9355,-18.3705],[-174.007,-18.376],[-174.1065,-18.433],[-174.1765,-18.458],[-174.271,-18.532],[-174.325,-18.599],[-174.375,-18.7365],[-174.382,-18.8075]]],[[[-174.016,-15.9565],[-174.0015,-16.026],[-173.944,-16.1155],[-173.8555,-16.1705],[-173.7925,-16.183],[-173.726,-16.1755],[-173.6665,-16.1495],[-173.582,-16.067],[-173.549,-16.003],[-173.5295,-15.8555],[-173.5355,-15.804],[-173.5765,-15.724],[-173.63,-15.675],[-173.711,-15.6415],[-173.7775,-15.6395],[-173.843,-15.658],[-173.897,-15.694],[-173.937,-15.746],[-173.9585,-15.8135],[-173.9915,-15.857],[-174.016,-15.9565]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9a/Flag_of_Tonga.svg","name:en":"Tonga","wikidata":"Q678","ISO3166-1:alpha2":"TO","ISO3166-1:alpha3":"TON","ISO3166-1:numeric":"776"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-42.3545,-53.5505],[-42.338,-53.612],[-42.2835,-53.6725],[-42.2085,-53.7145],[-42.0535,-53.748],[-41.9675,-53.799],[-41.895,-53.822],[-41.7705,-53.8345],[-41.6015,-53.8055],[-41.5325,-53.7725],[-41.447,-53.675],[-41.4495,-53.59],[-41.492,-53.529],[-41.547,-53.4895],[-41.641,-53.4525],[-41.744,-53.4365],[-41.8415,-53.381],[-41.954,-53.354],[-42.0435,-53.3505],[-42.1625,-53.369],[-42.2425,-53.401],[-42.3355,-53.483],[-42.3545,-53.5505]]],[[[-38.642,-53.991],[-38.62,-54.063],[-38.538,-54.148],[-38.4455,-54.195],[-38.358,-54.217],[-38.2535,-54.224],[-38.182,-54.249],[-38.026,-54.27],[-37.957,-54.307],[-37.8025,-54.365],[-37.702,-54.385],[-37.585,-54.447],[-37.4755,-54.472],[-37.4635,-54.554],[-37.407,-54.618],[-37.31,-54.668],[-37.214,-54.698],[-37.084,-54.715],[-37.014,-54.772],[-36.8875,-54.818],[-36.774,-54.83],[-36.6345,-54.815],[-36.552,-54.907],[-36.432,-54.97],[-36.342,-55.039],[-36.181,-55.088],[-36.0275,-55.089],[-35.8585,-55.06],[-35.7145,-55.01],[-35.533,-54.96],[-35.455,-54.906],[-35.409,-54.826],[-35.4095,-54.77],[-35.4515,-54.7],[-35.555,-54.615],[-35.553,-54.509],[-35.5855,-54.456],[-35.666,-54.395],[-35.8315,-54.31],[-35.952,-54.187],[-36.028,-54.135],[-36.206,-54.074],[-36.2985,-54.018],[-36.531,-53.931],[-36.6875,-53.908],[-36.874,-53.868],[-37.0115,-53.856],[-37.247,-53.805],[-37.455,-53.77],[-37.621,-53.784],[-37.7565,-53.776],[-37.881,-53.793],[-38.085,-53.802],[-38.3,-53.791],[-38.478,-53.821],[-38.593,-53.889],[-38.642,-53.991]]],[[[-28.5075,-56.706],[-28.4685,-56.8],[-28.383,-56.861],[-28.3045,-56.89],[-28.1785,-56.91],[-28.073,-56.908],[-27.959,-56.886],[-27.8425,-56.83],[-27.7875,-56.772],[-27.77,-56.698],[-27.8005,-56.628],[-27.883,-56.563],[-28.057,-56.511],[-28.1765,-56.507],[-28.269,-56.519],[-28.4305,-56.585],[-28.4915,-56.65],[-28.5075,-56.706]]],[[[-27.971,-56.308],[-27.96,-56.356],[-27.9075,-56.42],[-27.797,-56.488],[-27.696,-56.518],[-27.57,-56.53],[-27.468,-56.523],[-27.3695,-56.499],[-27.268,-56.446],[-27.1735,-56.344],[-27.1715,-56.26],[-27.261,-56.155],[-27.426,-56.087],[-27.544,-56.075],[-27.7205,-56.096],[-27.8175,-56.13],[-27.892,-56.174],[-27.944,-56.228],[-27.971,-56.308]]],[[[-27.8125,-59.442],[-27.7445,-59.558],[-27.6535,-59.613],[-27.4575,-59.669],[-27.1585,-59.684],[-27.0595,-59.677],[-26.9235,-59.643],[-26.773,-59.562],[-26.7185,-59.516],[-26.68,-59.444],[-26.6895,-59.382],[-26.7775,-59.296],[-26.931,-59.239],[-27.0455,-59.224],[-27.2375,-59.238],[-27.4615,-59.236],[-27.6015,-59.264],[-27.7205,-59.313],[-27.78,-59.362],[-27.8125,-59.442]]],[[[-27.5665,-56.708],[-27.527,-56.816],[-27.4295,-56.884],[-27.307,-56.92],[-27.1905,-56.935],[-27.0775,-56.932],[-27.167,-57.044],[-27.176,-57.132],[-27.092,-57.237],[-26.9565,-57.292],[-26.853,-57.31],[-26.7045,-57.31],[-26.4795,-57.272],[-26.3955,-57.24],[-26.3115,-57.176],[-26.281,-57.062],[-26.3265,-56.983],[-26.373,-56.942],[-26.4745,-56.893],[-26.6075,-56.865],[-26.7025,-56.86],[-26.7675,-56.824],[-26.716,-56.743],[-26.7255,-56.669],[-26.7605,-56.619],[-26.8815,-56.54],[-27.0465,-56.495],[-27.224,-56.49],[-27.418,-56.541],[-27.538,-56.63],[-27.5665,-56.708]]],[[[-27.128,-59.045],[-27.1175,-59.091],[-27.0495,-59.165],[-26.942,-59.216],[-26.741,-59.264],[-26.5715,-59.28],[-26.443,-59.268],[-26.2745,-59.215],[-26.1645,-59.144],[-26.125,-59.084],[-26.1195,-59.02],[-26.16,-58.948],[-26.224,-58.901],[-26.375,-58.836],[-26.553,-58.804],[-26.7535,-58.821],[-26.9395,-58.871],[-27.064,-58.933],[-27.1125,-58.987],[-27.128,-59.045]]],[[[-26.887,-57.774],[-26.8465,-57.874],[-26.778,-57.932],[-26.5935,-58.0],[-26.44,-58.016],[-26.324,-58.009],[-26.1715,-57.975],[-26.0815,-57.935],[-25.9945,-57.857],[-25.9765,-57.795],[-25.9915,-57.737],[-26.0625,-57.665],[-26.177,-57.605],[-26.334,-57.555],[-26.457,-57.543],[-26.563,-57.55],[-26.665,-57.574],[-26.8105,-57.652],[-26.8625,-57.702],[-26.887,-57.774]]],[[[-26.8335,-58.435],[-26.7755,-58.545],[-26.6425,-58.623],[-26.503,-58.668],[-26.304,-58.701],[-26.11,-58.693],[-26.0245,-58.671],[-25.922,-58.619],[-25.869,-58.562],[-25.8485,-58.5],[-25.8715,-58.354],[-25.918,-58.3],[-26.028,-58.238],[-26.122,-58.209],[-26.264,-58.185],[-26.46,-58.186],[-26.55,-58.202],[-26.7035,-58.256],[-26.7815,-58.317],[-26.8335,-58.435]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/ed/Flag_of_South_Georgia_and_the_South_Sandwich_Islands.svg","name:en":"South Georgia and the South Sandwich Islands","wikidata":"Q35086","ISO3166-1:alpha2":"GS","ISO3166-1:alpha3":"SGS","ISO3166-1:numeric":"239"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-57.649,-30.194],[-57.615,-30.256],[-57.6435,-30.3425],[-57.7675,-30.4185],[-57.8885,-30.509],[-57.883,-30.593],[-57.848,-30.6185],[-57.844,-30.6585],[-57.809,-30.6955],[-57.7995,-30.853],[-57.822,-30.908],[-57.915,-30.921],[-57.8615,-31.0325],[-57.8715,-31.0705],[-57.9165,-31.1225],[-57.913,-31.212],[-57.9385,-31.2755],[-57.997,-31.36],[-57.98,-31.3895],[-58.0815,-31.4545],[-58.075,-31.492],[-58.004,-31.532],[-57.981,-31.5905],[-58.0385,-31.7595],[-58.0845,-31.82],[-58.191,-31.853],[-58.207,-31.871],[-58.1385,-32.002],[-58.142,-32.0565],[-58.168,-32.0935],[-58.18,-32.1655],[-58.109,-32.24],[-58.103,-32.3095],[-58.182,-32.3745],[-58.204,-32.465],[-58.1605,-32.5725],[-58.1455,-32.667],[-58.15,-32.727],[-58.1205,-32.8115],[-58.1145,-32.924],[-58.0845,-33.001],[-58.176,-33.077],[-58.257,-33.1025],[-58.3605,-33.12],[-58.4045,-33.1805],[-58.41,-33.231],[-58.3905,-33.277],[-58.432,-33.366],[-58.4435,-33.5375],[-58.494,-33.5815],[-58.474,-33.6525],[-58.4345,-33.7205],[-58.4235,-33.9165],[-58.377,-34.0],[-58.3445,-34.007],[-58.295,-34.178],[-58.1805,-34.233],[-58.0,-34.3765],[-57.9345,-34.452],[-57.9345,-34.685],[-57.508,-34.781],[-57.328,-34.863],[-57.0,-35.189],[-56.705,-35.173],[-55.869,-35.632],[-55.7035,-35.7825],[-55.2495,-35.4505],[-54.9375,-35.222],[-54.837,-35.2285],[-54.7785,-35.2155],[-54.6885,-35.1645],[-54.6495,-35.117],[-54.625,-35.0415],[-54.497,-35.0145],[-54.4365,-34.9705],[-54.182,-34.8655],[-54.1275,-34.8675],[-54.0335,-34.844],[-53.9765,-34.812],[-53.6185,-34.575],[-53.5295,-34.5005],[-53.4995,-34.429],[-53.3145,-34.154],[-53.288,-34.0755],[-53.234,-33.9845],[-53.231,-33.912],[-53.181,-33.869],[-53.3715,-33.743],[-53.4305,-33.739],[-53.4395,-33.6935],[-53.5325,-33.689],[-53.5255,-33.5265],[-53.511,-33.4595],[-53.508,-33.353],[-53.5215,-33.265],[-53.518,-33.1535],[-53.4445,-33.053],[-53.2445,-32.9345],[-53.2945,-32.8995],[-53.185,-32.8505],[-53.1385,-32.7895],[-53.086,-32.7885],[-53.0755,-32.741],[-53.1935,-32.6415],[-53.2795,-32.6195],[-53.3715,-32.5695],[-53.433,-32.5575],[-53.471,-32.4805],[-53.5565,-32.4725],[-53.644,-32.385],[-53.6335,-32.3505],[-53.685,-32.2195],[-53.711,-32.1995],[-53.746,-32.0785],[-53.8065,-32.07],[-53.8495,-32.001],[-53.9725,-31.9395],[-54.0315,-31.896],[-54.081,-31.9295],[-54.4565,-31.6515],[-54.4745,-31.5695],[-54.5155,-31.5105],[-54.5865,-31.4565],[-54.6705,-31.454],[-54.755,-31.4245],[-54.8365,-31.442],[-54.858,-31.408],[-54.9385,-31.354],[-55.0025,-31.2695],[-55.0745,-31.332],[-55.1835,-31.2655],[-55.2465,-31.2525],[-55.25,-31.206],[-55.3055,-31.141],[-55.3715,-31.0235],[-55.4375,-31.0045],[-55.4415,-30.9595],[-55.4805,-30.9525],[-55.51,-30.902],[-55.5455,-30.8905],[-55.582,-30.834],[-55.661,-30.8695],[-55.6485,-30.923],[-55.7805,-31.017],[-55.8135,-31.0155],[-55.8715,-31.071],[-55.933,-31.087],[-56.008,-31.0705],[-56.019,-31.0435],[-56.008,-30.8945],[-55.9895,-30.8585],[-56.023,-30.7855],[-56.062,-30.7745],[-56.146,-30.706],[-56.1885,-30.6045],[-56.384,-30.4975],[-56.3995,-30.447],[-56.49,-30.3985],[-56.5375,-30.3355],[-56.6055,-30.2925],[-56.6625,-30.1975],[-56.7845,-30.1505],[-56.781,-30.126],[-56.8505,-30.089],[-56.906,-30.1075],[-56.994,-30.086],[-57.115,-30.1135],[-57.168,-30.198],[-57.167,-30.248],[-57.2035,-30.2855],[-57.291,-30.289],[-57.31,-30.259],[-57.3975,-30.3045],[-57.4185,-30.2775],[-57.5495,-30.2735],[-57.5665,-30.2075],[-57.649,-30.194]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fe/Flag_of_Uruguay.svg","name:en":"Uruguay","wikidata":"Q77","ISO3166-1:alpha2":"UY","ISO3166-1:alpha3":"URY","ISO3166-1:numeric":"858"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-57.015,6.225],[-57.032,6.1695],[-57.0785,6.0845],[-57.1625,6.0515],[-57.1435,5.989],[-57.135,5.8935],[-57.1675,5.8095],[-57.183,5.74],[-57.1605,5.6995],[-57.1735,5.5995],[-57.1985,5.553],[-57.2705,5.464],[-57.2695,5.4035],[-57.327,5.351],[-57.2915,5.3215],[-57.2925,5.2405],[-57.2515,5.2765],[-57.227,5.262],[-57.183,5.168],[-57.23,5.1365],[-57.251,5.1855],[-57.299,5.1735],[-57.3005,5.1385],[-57.3515,5.0155],[-57.4105,4.993],[-57.476,4.991],[-57.5495,5.004],[-57.608,4.992],[-57.688,5.0105],[-57.746,4.9675],[-57.776,4.921],[-57.844,4.9245],[-57.9245,4.8165],[-57.8905,4.772],[-57.844,4.682],[-57.8445,4.6335],[-57.877,4.574],[-57.928,4.441],[-57.9585,4.4035],[-57.9535,4.288],[-58.034,4.217],[-58.071,4.1595],[-58.049,4.079],[-58.0435,3.998],[-57.957,3.9205],[-57.876,3.8095],[-57.849,3.741],[-57.8455,3.6895],[-57.821,3.653],[-57.755,3.623],[-57.7065,3.556],[-57.6565,3.5235],[-57.637,3.4555],[-57.6655,3.401],[-57.6465,3.361],[-57.5975,3.3415],[-57.5205,3.3645],[-57.4735,3.3385],[-57.4275,3.361],[-57.358,3.358],[-57.3,3.384],[-57.279,3.3245],[-57.2945,3.2785],[-57.2905,3.1815],[-57.2465,3.088],[-57.221,3.076],[-57.2045,3.0255],[-57.226,2.956],[-57.2085,2.8525],[-57.1465,2.7825],[-57.089,2.766],[-57.103,2.7345],[-57.0605,2.6715],[-57.018,2.6345],[-57.023,2.583],[-56.997,2.5555],[-56.9895,2.5025],[-56.93,2.436],[-56.9345,2.397],[-56.8855,2.3575],[-56.886,2.302],[-56.8265,2.2655],[-56.821,2.211],[-56.7265,2.0865],[-56.691,2.026],[-56.586,2.028],[-56.537,2.0085],[-56.469,1.949],[-56.412,1.9285],[-56.335,1.937],[-56.244,1.878],[-56.2345,1.8985],[-56.1525,1.8905],[-56.0985,1.8465],[-56.0365,1.85],[-55.999,1.8315],[-55.9565,1.845],[-55.904,1.888],[-55.9365,1.9865],[-55.909,2.0475],[-55.967,2.0885],[-56.003,2.1675],[-56.0565,2.1895],[-56.043,2.228],[-56.1205,2.2505],[-56.1255,2.278],[-56.094,2.321],[-56.0505,2.335],[-56.0365,2.374],[-55.9895,2.427],[-55.978,2.5275],[-55.9345,2.5335],[-55.83,2.459],[-55.7665,2.455],[-55.747,2.4105],[-55.6985,2.4215],[-55.5705,2.4235],[-55.4995,2.443],[-55.4295,2.439],[-55.3855,2.4185],[-55.3455,2.448],[-55.356,2.4755],[-55.32,2.5155],[-55.2345,2.5035],[-55.173,2.5595],[-55.1235,2.5675],[-55.103,2.5255],[-55.0035,2.591],[-54.954,2.5835],[-54.9125,2.485],[-54.87,2.468],[-54.87,2.431],[-54.828,2.4215],[-54.7605,2.4635],[-54.6965,2.4625],[-54.7,2.406],[-54.642,2.322],[-54.601,2.3375],[-54.572,2.3545],[-54.521,2.337],[-54.5045,2.3925],[-54.4735,2.4365],[-54.42,2.436],[-54.349,2.5215],[-54.3175,2.6285],[-54.2805,2.6625],[-54.263,2.7225],[-54.206,2.7765],[-54.2075,2.829],[-54.1715,2.9415],[-54.1945,3.0655],[-54.1935,3.1975],[-54.174,3.2075],[-54.13,3.2845],[-54.064,3.3135],[-54.048,3.3875],[-54.023,3.4105],[-54.009,3.466],[-54.004,3.563],[-53.98,3.6055],[-54.003,3.645],[-54.051,3.635],[-54.0835,3.6755],[-54.0785,3.708],[-54.127,3.798],[-54.183,3.7995],[-54.1995,3.8525],[-54.2345,3.858],[-54.2485,3.9055],[-54.2955,3.9475],[-54.3185,4.015],[-54.358,4.0515],[-54.324,4.149],[-54.396,4.2025],[-54.382,4.269],[-54.386,4.349],[-54.4405,4.4195],[-54.452,4.509],[-54.414,4.597],[-54.4335,4.634],[-54.433,4.7035],[-54.4775,4.748],[-54.4735,4.857],[-54.4865,4.8985],[-54.44,4.949],[-54.4375,5.017],[-54.4055,5.084],[-54.374,5.109],[-54.2965,5.2405],[-54.193,5.315],[-54.128,5.4065],[-54.0485,5.4915],[-54.008,5.551],[-54.0205,5.6585],[-53.967,5.761],[-53.8435,5.978],[-53.8765,6.004],[-53.949,6.032],[-54.0715,6.049],[-54.144,6.078],[-54.4035,6.132],[-54.6795,6.178],[-54.818,6.189],[-55.0215,6.195],[-55.1345,6.183],[-55.247,6.141],[-55.3435,6.17],[-55.5185,6.18],[-55.602,6.172],[-55.765,6.168],[-55.8565,6.148],[-56.003,6.085],[-56.0585,6.033],[-56.105,6.048],[-56.3675,6.104],[-56.4855,6.114],[-56.553,6.155],[-56.683,6.182],[-56.814,6.172],[-56.9045,6.197],[-56.999,6.197],[-57.015,6.225]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/60/Flag_of_Suriname.svg","name:en":"Suriname","wikidata":"Q730","ISO3166-1:alpha2":"SR","ISO3166-1:alpha3":"SUR","ISO3166-1:numeric":"740"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-18.3935,27.7165],[-18.387,27.664],[-18.336,27.5755],[-18.283,27.5335],[-18.087,27.453],[-18.0095,27.4345],[-17.893,27.4525],[-17.834,27.482],[-17.764,27.556],[-17.6785,27.7075],[-17.6575,27.767],[-17.6675,27.867],[-17.7295,27.9665],[-17.7865,28.0105],[-17.8555,28.0385],[-17.952,28.0475],[-18.031,28.0325],[-18.254,27.942],[-18.3235,27.893],[-18.3825,27.792],[-18.3935,27.7165]]],[[[-18.2375,28.76],[-18.2305,28.7075],[-18.1725,28.5785],[-18.104,28.4635],[-18.062,28.3585],[-18.013,28.3055],[-17.9065,28.2525],[-17.8035,28.247],[-17.7445,28.263],[-17.6745,28.3045],[-17.6365,28.346],[-17.547,28.486],[-17.531,28.5315],[-17.492,28.745],[-17.5185,28.833],[-17.591,28.9515],[-17.6655,29.0165],[-17.7575,29.0445],[-17.934,29.0585],[-18.0175,29.0395],[-18.126,28.9715],[-18.194,28.8985],[-18.2305,28.826],[-18.2375,28.76]]],[[[-17.578,28.1005],[-17.557,28.01],[-17.5055,27.9365],[-17.4545,27.89],[-17.379,27.8425],[-17.265,27.813],[-17.176,27.8175],[-17.074,27.846],[-17.022,27.875],[-16.9355,27.948],[-16.884,27.8745],[-16.8045,27.8175],[-16.6795,27.791],[-16.4965,27.8225],[-16.4015,27.864],[-16.2535,28.0085],[-16.2015,28.0985],[-16.177,28.179],[-15.954,28.3935],[-15.921,28.434],[-15.893,28.5365],[-15.9135,28.6495],[-15.9605,28.7175],[-16.007,28.7515],[-16.132,28.7825],[-16.321,28.781],[-16.3985,28.768],[-16.5245,28.6995],[-16.9025,28.585],[-17.025,28.5375],[-17.118,28.4585],[-17.147,28.3955],[-17.2235,28.416],[-17.344,28.41],[-17.4255,28.3805],[-17.4855,28.3395],[-17.5385,28.267],[-17.5725,28.178],[-17.578,28.1005]]],[[[-16.064,27.9105],[-16.0475,27.836],[-15.99,27.7355],[-15.9265,27.6665],[-15.82,27.5855],[-15.715,27.543],[-15.592,27.529],[-15.4665,27.5545],[-15.3165,27.625],[-15.239,27.6845],[-15.179,27.7615],[-15.137,27.872],[-15.1275,27.9335],[-15.137,28.0265],[-15.1715,28.199],[-15.1935,28.257],[-15.2385,28.31],[-15.3075,28.357],[-15.4275,28.382],[-15.7195,28.3695],[-15.8005,28.35],[-15.8985,28.2765],[-16.011,28.127],[-16.0425,28.07],[-16.0625,27.989],[-16.064,27.9105]]],[[[-14.735,28.059],[-14.703,27.958],[-14.6655,27.916],[-14.6045,27.8785],[-14.533,27.86],[-14.3125,27.8405],[-14.2215,27.864],[-13.8105,28.0595],[-13.7545,28.1045],[-13.7025,28.17],[-13.638,28.3235],[-13.6225,28.443],[-13.597,28.5285],[-13.4385,28.7885],[-13.334,28.8495],[-13.252,28.9355],[-13.1215,29.192],[-13.1015,29.262],[-13.106,29.315],[-13.1495,29.399],[-13.2615,29.497],[-13.291,29.7305],[-13.338,29.833],[-13.4065,29.885],[-13.463,29.905],[-13.539,29.9115],[-13.64,29.884],[-13.6885,29.8515],[-13.7405,29.7805],[-14.026,29.1165],[-14.226,28.7945],[-14.389,28.4545],[-14.6395,28.2625],[-14.6965,28.196],[-14.735,28.059]]],[[[-1.804,43.595],[-1.8685,43.591],[-2.1925,43.524],[-2.66,43.6475],[-2.7885,43.658],[-2.943,43.644],[-3.3695,43.6645],[-3.5455,43.7135],[-3.6215,43.7145],[-3.8455,43.695],[-4.3565,43.611],[-4.872,43.661],[-5.3355,43.749],[-5.7525,43.852],[-5.8135,43.8625],[-5.934,43.8605],[-6.2885,43.7965],[-6.6375,43.7775],[-6.895,43.778],[-7.5885,43.9795],[-7.7155,43.9925],[-7.893,43.981],[-8.0335,43.9465],[-8.214,43.8755],[-8.4645,43.739],[-8.979,43.54],[-9.3365,43.3515],[-9.4165,43.301],[-9.5375,43.1565],[-9.5755,43.046],[-9.573,42.9185],[-9.5375,42.8045],[-9.344,42.4985],[-9.1835,42.165],[-9.144,41.9785],[-9.1565,41.8725],[-8.871,41.864],[-8.746,41.9645],[-8.6615,42.0035],[-8.637,42.0475],[-8.547,42.054],[-8.518,42.0795],[-8.429,42.0725],[-8.332,42.084],[-8.2535,42.1365],[-8.1955,42.148],[-8.186,42.0725],[-8.1185,42.0825],[-8.086,42.0165],[-8.1305,42.0035],[-8.217,41.913],[-8.162,41.861],[-8.165,41.818],[-8.094,41.808],[-8.0125,41.8335],[-7.9885,41.8675],[-7.9215,41.882],[-7.7335,41.8925],[-7.704,41.9075],[-7.574,41.8295],[-7.4525,41.865],[-7.39,41.8425],[-7.3155,41.8425],[-7.1965,41.88],[-7.1735,41.919],[-7.1865,41.9695],[-7.1415,41.991],[-7.077,41.952],[-6.7505,41.9435],[-6.7,41.9335],[-6.5995,41.9485],[-6.546,41.931],[-6.571,41.8835],[-6.5175,41.875],[-6.5645,41.7515],[-6.5485,41.6855],[-6.5125,41.661],[-6.4525,41.681],[-6.3545,41.6765],[-6.2545,41.633],[-6.189,41.575],[-6.3055,41.4495],[-6.3155,41.39],[-6.4155,41.348],[-6.469,41.3015],[-6.5805,41.239],[-6.6485,41.2475],[-6.7015,41.1805],[-6.768,41.135],[-6.754,41.104],[-6.809,41.0365],[-6.9315,41.017],[-6.86,40.951],[-6.8035,40.846],[-6.8305,40.743],[-6.799,40.6555],[-6.8455,40.566],[-6.801,40.5505],[-6.8495,40.4515],[-6.8385,40.4145],[-6.781,40.364],[-6.7905,40.334],[-6.864,40.296],[-6.868,40.2645],[-6.9515,40.2575],[-7.009,40.2275],[-7.019,40.1425],[-6.954,40.1155],[-6.881,40.0415],[-6.8855,39.94],[-6.9035,39.8705],[-6.987,39.81],[-6.977,39.772],[-7.0155,39.6705],[-7.1515,39.653],[-7.2515,39.667],[-7.331,39.641],[-7.534,39.667],[-7.5,39.591],[-7.377,39.489],[-7.2945,39.457],[-7.323,39.382],[-7.3105,39.341],[-7.2315,39.2785],[-7.248,39.2535],[-7.2215,39.1935],[-7.132,39.1645],[-7.144,39.1085],[-7.028,39.1155],[-6.981,39.088],[-6.951,39.0235],[-7.052,38.907],[-7.033,38.879],[-7.0965,38.816],[-7.1255,38.8155],[-7.203,38.752],[-7.26,38.723],[-7.2725,38.6385],[-7.264,38.588],[-7.3035,38.543],[-7.318,38.44],[-7.143,38.2615],[-7.097,38.179],[-7.0285,38.1825],[-6.969,38.1605],[-7.007,38.021],[-7.0445,38.014],[-7.1085,38.041],[-7.1285,38.0015],[-7.2455,37.992],[-7.255,37.9235],[-7.3215,37.8185],[-7.419,37.7435],[-7.445,37.664],[-7.5225,37.553],[-7.4905,37.5185],[-7.4405,37.391],[-7.441,37.3075],[-7.422,37.275],[-7.397,37.1575],[-7.3865,36.9745],[-7.3065,36.968],[-7.0065,36.9685],[-6.9115,36.9375],[-6.6875,36.624],[-6.5325,36.428],[-6.4185,36.2575],[-6.233,36.0605],[-6.1865,36.0225],[-6.0565,35.9595],[-5.8855,35.965],[-5.6665,35.9355],[-5.6215,35.9405],[-5.3,36.033],[-5.213,36.0475],[-5.112,36.047],[-5.075,36.136],[-4.897,36.266],[-4.6805,36.287],[-4.5245,36.327],[-4.0425,36.524],[-3.965,36.5245],[-3.407,36.4885],[-3.3375,36.4985],[-3.1915,36.537],[-2.8165,36.4815],[-2.678,36.4815],[-2.1725,36.517],[-2.1105,36.524],[-1.997,36.558],[-1.879,36.6365],[-1.801,36.716],[-1.764,36.77],[-1.714,36.8065],[-1.6635,36.874],[-1.647,36.9325],[-1.606,37.0055],[-1.5825,37.078],[-1.3545,37.2465],[-1.0455,37.337],[-0.845,37.3575],[-0.7315,37.382],[-0.6045,37.4225],[-0.466,37.514],[-0.418,37.568],[-0.3925,37.655],[-0.389,37.9215],[-0.2715,38.027],[-0.217,38.1085],[-0.1775,38.2275],[0.0585,38.3575],[0.242,38.469],[0.442,38.6065],[0.487,38.6775],[0.496,38.73],[0.473,38.8355],[0.417,38.911],[0.3085,38.992],[0.047,39.254],[0.0545,39.58],[0.36,39.927],[0.353,39.831],[0.378,39.7685],[0.4225,39.713],[0.4835,39.6675],[0.5575,39.6355],[0.682,39.6165],[0.766,39.6245],[0.845,39.648],[0.914,39.6865],[0.968,39.7365],[1.0045,39.7955],[1.02,39.8595],[1.0145,39.9245],[0.988,39.9865],[0.942,40.041],[0.88,40.085],[0.805,40.116],[0.723,40.1315],[0.638,40.1305],[0.4955,40.088],[0.614,40.2275],[1.075,40.5755],[1.111,40.6095],[1.3775,40.9225],[1.509,40.9485],[1.567,40.976],[1.81,41.011],[2.0015,41.06],[2.0945,41.069],[2.22,41.102],[2.309,41.1475],[2.3565,41.1875],[2.7165,41.4085],[2.9105,41.4715],[2.9755,41.5085],[3.1065,41.5575],[3.207,41.615],[3.363,41.718],[3.4605,41.8205],[3.4995,41.9085],[3.5995,42.2775],[3.5985,42.3625],[3.5585,42.4355],[3.448,42.4355],[3.11,42.4355],[2.9455,42.48],[2.9185,42.456],[2.84,42.459],[2.8005,42.4195],[2.6795,42.407],[2.661,42.367],[2.5795,42.3585],[2.542,42.334],[2.481,42.341],[2.4145,42.392],[2.257,42.4385],[2.2,42.4165],[2.129,42.4125],[2.083,42.3635],[2.012,42.353],[1.9645,42.3825],[1.9585,42.424],[1.8045,42.4905],[1.726,42.5025],[1.597,42.4675],[1.5525,42.4335],[1.4445,42.4415],[1.425,42.558],[1.442,42.6035],[1.3575,42.7195],[1.166,42.709],[1.109,42.7715],[0.9605,42.806],[0.9265,42.7895],[0.8585,42.8255],[0.7085,42.8615],[0.646,42.7825],[0.675,42.6915],[0.527,42.7025],[0.4225,42.6905],[0.3585,42.7195],[0.2955,42.674],[0.2595,42.7165],[0.1845,42.7355],[0.0015,42.686],[-0.062,42.695],[-0.156,42.785],[-0.189,42.787],[-0.308,42.8425],[-0.394,42.799],[-0.4435,42.796],[-0.5055,42.8275],[-0.5675,42.781],[-0.602,42.8315],[-0.718,42.8865],[-0.7515,42.967],[-0.8105,42.9515],[-0.9455,42.9535],[-1.0065,42.989],[-1.111,43.0205],[-1.2475,43.0425],[-1.346,43.0905],[-1.354,43.0285],[-1.441,43.0465],[-1.471,43.079],[-1.4145,43.128],[-1.383,43.19],[-1.379,43.25],[-1.533,43.2935],[-1.609,43.252],[-1.6695,43.315],[-1.73,43.296],[-1.79,43.3535],[-1.772,43.388],[-1.772,43.488],[-1.804,43.595]],[[-5.4005,36.1485],[-5.277,36.1495],[-5.2905,36.0855],[-5.364,36.061],[-5.4005,36.1485]]],[[[-3.2715,35.944],[-3.2635,35.8845],[-3.233,35.8305],[-3.1825,35.7865],[-3.1175,35.758],[-3.045,35.7475],[-2.938,35.768],[-2.878,35.803],[-2.8355,35.852],[-2.814,35.909],[-2.816,35.969],[-2.841,36.025],[-2.887,36.0715],[-2.949,36.104],[-3.02,36.119],[-3.094,36.1155],[-3.162,36.0925],[-3.218,36.0535],[-3.2555,36.0025],[-3.2715,35.944]]],[[[0.898,38.9805],[0.9315,38.824],[0.974,38.746],[1.173,38.5235],[1.2525,38.4655],[1.3145,38.4445],[1.4,38.437],[1.584,38.4455],[1.6945,38.4705],[1.805,38.557],[1.8395,38.632],[1.91,39.002],[1.9115,39.0545],[1.8885,39.118],[1.7785,39.24],[1.71,39.278],[1.6035,39.313],[1.519,39.3205],[1.3895,39.298],[1.2255,39.2475],[1.023,39.152],[0.9645,39.114],[0.9075,39.033],[0.898,38.9805]]],[[[2.04,39.5765],[2.053,39.5075],[2.126,39.4185],[2.741,38.977],[2.7965,38.9465],[2.8935,38.9235],[3.019,38.928],[3.0825,38.9465],[3.1545,38.9905],[3.4245,39.225],[3.4545,39.2565],[3.6415,39.4945],[3.6925,39.571],[3.7375,39.682],[3.741,39.728],[4.198,39.6065],[4.2715,39.5955],[4.369,39.604],[4.432,39.625],[4.518,39.689],[4.5875,39.8385],[4.589,39.9045],[4.5175,40.066],[4.4425,40.149],[4.2845,40.244],[4.172,40.282],[4.021,40.291],[3.77,40.2505],[3.6725,40.218],[3.593,40.149],[3.5365,40.046],[3.5295,39.994],[3.4105,40.097],[3.3385,40.1405],[3.2055,40.1645],[3.1065,40.1565],[2.858,40.109],[2.7425,40.059],[2.625,40.0205],[2.5025,39.95],[2.417,39.885],[2.296,39.816],[2.181,39.763],[2.1145,39.7195],[2.0515,39.6325],[2.04,39.5765]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/32/Flag_of_Spain_%28Civil%29.svg","name:en":"Spain","wikidata":"Q29","ISO3166-1:alpha2":"ES","ISO3166-1:alpha3":"ESP","ISO3166-1:numeric":"724"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-61.7295,10.948],[-61.731,10.904],[-61.803,10.7145],[-61.803,10.589],[-61.8625,10.5885],[-62.083,10.046],[-61.9735,10.008],[-61.855,9.9865],[-61.6305,9.9865],[-61.5,9.97],[-61.2235,9.876],[-61.2075,9.8745],[-61.092,9.8855],[-60.9135,9.9495],[-60.833,10.0025],[-60.795,10.061],[-60.305,11.218],[-60.2895,11.286],[-60.2985,11.3555],[-60.3315,11.451],[-60.373,11.5055],[-60.431,11.5435],[-60.5085,11.563],[-60.5735,11.558],[-60.7445,11.5085],[-61.7295,10.948]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/64/Flag_of_Trinidad_and_Tobago.svg","name:en":"Trinidad and Tobago","wikidata":"Q754","ISO3166-1:alpha2":"TT","ISO3166-1:alpha3":"TTO","ISO3166-1:numeric":"780"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[51.586,24.664],[51.5845,24.599],[51.5475,24.531],[51.4665,24.4625],[51.416,24.393],[51.456,24.364],[51.5295,24.3365],[51.59,24.266],[51.5905,24.127],[51.92,23.7375],[52.1635,23.4425],[52.581,22.939],[53.3315,22.8535],[53.8075,22.797],[54.38,22.727],[55.1375,22.6315],[55.2115,22.7055],[55.2275,22.7925],[55.2135,22.9355],[55.2325,23.1105],[55.2805,23.1775],[55.4015,23.3925],[55.437,23.412],[55.447,23.457],[55.5725,23.6295],[55.569,23.7205],[55.533,23.756],[55.5335,23.8495],[55.485,23.94],[55.495,23.9535],[55.633,24.02],[55.731,24.058],[55.782,24.056],[55.833,24.0145],[55.9025,24.047],[56.0175,24.0665],[55.9605,24.1705],[55.9545,24.2225],[55.8335,24.201],[55.777,24.2345],[55.791,24.2795],[55.834,24.3275],[55.834,24.4095],[55.765,24.5295],[55.7675,24.5725],[55.816,24.6155],[55.8365,24.6715],[55.8315,24.78],[55.8145,24.7995],[55.8125,24.911],[55.8515,24.966],[55.911,24.9655],[55.961,25.006],[56.007,24.9945],[56.059,24.9495],[56.042,24.8865],[55.9785,24.8775],[56.036,24.811],[56.0675,24.7415],[56.1195,24.7345],[56.201,24.785],[56.2055,24.8505],[56.2595,24.86],[56.3255,24.8985],[56.349,24.933],[56.3235,24.973],[56.594,25.009],[56.5855,25.0875],[56.5975,25.1855],[56.6025,25.3235],[56.59,25.414],[56.5915,25.549],[56.549,25.6925],[56.4185,25.6825],[56.202,25.612],[56.1585,25.6615],[56.1675,25.725],[56.14,25.827],[56.18,25.91],[56.163,25.942],[56.1955,25.9795],[56.153,26.0695],[56.0865,26.0505],[55.8885,26.1515],[55.8265,26.007],[55.768,25.926],[55.666,25.898],[55.5545,25.811],[55.4975,25.7965],[55.432,25.76],[55.378,25.702],[55.2795,25.5665],[55.2385,25.5705],[55.1315,25.5445],[55.068,25.5005],[55.0105,25.4145],[54.969,25.38],[54.9295,25.318],[54.9045,25.226],[54.8565,25.206],[54.7925,25.154],[54.7155,25.0695],[54.5925,25.033],[54.525,24.9985],[54.447,24.9055],[54.4265,24.86],[54.014,24.555],[53.8205,24.5235],[53.575,24.5935],[53.552,24.691],[53.5035,24.75],[53.4345,24.7895],[53.3025,24.8135],[53.312,24.877],[53.299,24.945],[53.263,25.0075],[53.2235,25.046],[53.1485,25.0865],[53.098,25.0975],[53.104,25.1895],[53.079,25.2555],[52.9885,25.336],[52.909,25.3615],[52.804,25.3545],[52.647,25.143],[52.67,25.062],[52.6315,24.992],[52.5555,25.003],[52.45,24.983],[52.3965,25.0195],[52.346,24.9975],[52.334,24.967],[52.3785,24.9005],[52.3475,24.874],[52.3295,24.802],[52.337,24.7415],[52.2235,24.729],[52.1515,24.686],[52.092,24.6105],[52.0255,24.7585],[51.829,24.716],[51.586,24.664]],[[56.208,25.256],[56.2455,25.2755],[56.2705,25.328],[56.344,25.264],[56.2735,25.232],[56.208,25.256]]],[[[53.9775,25.224],[54.017,25.1145],[54.0535,25.0745],[54.131,25.03],[54.23,25.012],[54.34,25.033],[54.3995,25.073],[54.4405,25.127],[54.46,25.1895],[54.455,25.272],[54.428,25.339],[54.357,25.4125],[54.239,25.4525],[54.1825,25.4505],[54.082,25.414],[54.0255,25.366],[53.9885,25.2985],[53.9775,25.224]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/cb/Flag_of_the_United_Arab_Emirates.svg","name:en":"United Arab Emirates","wikidata":"Q878","ISO3166-1:alpha2":"AE","ISO3166-1:alpha3":"ARE","ISO3166-1:numeric":"784"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-172.7215,-8.53],[-172.715,-8.5865],[-172.6795,-8.688],[-172.635,-8.739],[-172.5325,-8.784],[-172.4625,-8.781],[-172.388,-8.76],[-172.31,-8.702],[-172.2765,-8.6445],[-172.264,-8.5655],[-172.274,-8.4985],[-172.306,-8.4365],[-172.382,-8.3675],[-172.44,-8.341],[-172.527,-8.333],[-172.624,-8.3625],[-172.673,-8.404],[-172.709,-8.4645],[-172.7215,-8.53]]],[[[-172.0695,-9.178],[-172.0635,-9.228],[-172.0155,-9.319],[-171.947,-9.3845],[-171.8265,-9.4285],[-171.7555,-9.4285],[-171.6745,-9.3995],[-171.6215,-9.355],[-171.588,-9.307],[-171.566,-9.2375],[-171.581,-9.073],[-171.6235,-8.9775],[-171.6815,-8.9285],[-171.793,-8.903],[-171.9215,-8.925],[-171.986,-8.9595],[-172.032,-9.0095],[-172.058,-9.072],[-172.0695,-9.178]]],[[[-171.4715,-9.384],[-171.456,-9.455],[-171.4155,-9.516],[-171.336,-9.5975],[-171.238,-9.6415],[-171.1265,-9.6285],[-171.057,-9.5805],[-171.023,-9.536],[-170.997,-9.4715],[-170.9815,-9.3865],[-170.9955,-9.2795],[-171.072,-9.1775],[-171.161,-9.131],[-171.232,-9.126],[-171.3305,-9.1625],[-171.401,-9.222],[-171.447,-9.2805],[-171.4715,-9.384]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/8e/Flag_of_Tokelau.svg","name:en":"Tokelau","wikidata":"Q36823","ISO3166-1:alpha2":"TK","ISO3166-1:alpha3":"TKL","ISO3166-1:numeric":"772"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-72.55,21.9485],[-72.6095,21.858],[-72.63,21.804],[-72.6725,21.6405],[-72.6775,21.54],[-72.6465,21.493],[-72.5895,21.4485],[-72.347,21.324],[-71.937,21.0165],[-71.837,20.975],[-71.7075,20.9555],[-71.5955,20.9785],[-71.2495,20.97],[-71.18,20.9795],[-71.0835,21.038],[-70.979,21.149],[-70.893,21.2525],[-70.866,21.3575],[-70.8685,21.422],[-70.938,21.6],[-71.0125,21.68],[-71.364,21.898],[-71.459,21.925],[-71.5455,21.9945],[-71.618,22.027],[-71.7025,22.0375],[-71.7505,22.086],[-71.807,22.1205],[-71.893,22.149],[-71.992,22.163],[-72.117,22.14],[-72.2085,22.084],[-72.2425,22.0475],[-72.312,22.0655],[-72.3865,22.06],[-72.4545,22.0315],[-72.55,21.9485]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/a/a0/Flag_of_the_Turks_and_Caicos_Islands.svg","name:en":"Turks and Caicos Islands","wikidata":"Q18221","ISO3166-1:alpha2":"TC","ISO3166-1:alpha3":"TCA","ISO3166-1:numeric":"796"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-14.0155,57.6025],[-13.991,57.53],[-13.903,57.4645],[-13.718,57.4215],[-13.5555,57.4365],[-13.4375,57.484],[-13.379,57.5385],[-13.3615,57.6075],[-13.4155,57.694],[-13.4855,57.735],[-13.5995,57.7665],[-13.76,57.769],[-13.875,57.7415],[-13.956,57.6975],[-14.0155,57.6025]]],[[[-9.0235,57.829],[-9.0075,57.772],[-8.939,57.703],[-8.857,57.66],[-8.6765,57.605],[-8.5035,57.593],[-8.32,57.62],[-8.2,57.679],[-8.154,57.726],[-8.103,57.855],[-8.1435,57.962],[-8.267,58.043],[-8.3875,58.075],[-8.533,58.082],[-8.6475,58.066],[-8.867,57.996],[-8.966,57.938],[-9.0235,57.829]]],[[[-6.7445,55.415],[-6.7195,55.28],[-6.8215,55.2165],[-6.942,55.2045],[-7.218,55.0795],[-7.345,55.051],[-7.3915,55.0225],[-7.405,54.942],[-7.4455,54.9365],[-7.442,54.8755],[-7.484,54.824],[-7.549,54.7895],[-7.5785,54.742],[-7.6365,54.7515],[-7.746,54.7045],[-7.855,54.727],[-7.9135,54.6765],[-7.7715,54.6215],[-7.8505,54.533],[-8.006,54.546],[-8.042,54.506],[-8.1605,54.4405],[-8.056,54.3655],[-7.9865,54.344],[-7.9465,54.3045],[-7.8885,54.3005],[-7.862,54.2185],[-7.702,54.208],[-7.566,54.1265],[-7.4905,54.123],[-7.442,54.154],[-7.347,54.1175],[-7.31,54.1675],[-7.248,54.2045],[-7.147,54.2255],[-7.1735,54.286],[-7.1525,54.3355],[-7.104,54.3555],[-7.0535,54.412],[-6.982,54.4095],[-6.8775,54.347],[-6.8025,54.215],[-6.746,54.189],[-6.664,54.1925],[-6.6315,54.147],[-6.6615,54.1225],[-6.661,54.0625],[-6.623,54.039],[-6.48,54.067],[-6.3675,54.07],[-6.368,54.1115],[-6.2805,54.111],[-6.1135,54.033],[-5.951,53.9315],[-5.826,53.87],[-5.634,53.977],[-5.5795,54.031],[-5.493,54.051],[-5.3825,54.099],[-5.2705,54.172],[-5.202,54.232],[-5.123,54.346],[-5.0875,54.456],[-4.8685,54.434],[-4.6725,54.4995],[-4.4005,54.5465],[-4.1535,54.5505],[-4.0345,54.479],[-3.779,54.288],[-3.7275,54.196],[-3.606,54.079],[-3.544,53.984],[-3.3915,53.8695],[-3.397,53.774],[-3.3685,53.696],[-3.4265,53.616],[-3.44,53.547],[-3.631,53.508],[-3.7705,53.535],[-3.904,53.541],[-4.0055,53.526],[-4.1125,53.588],[-4.212,53.612],[-4.442,53.635],[-4.6815,53.619],[-4.7565,53.603],[-4.9005,53.523],[-4.9445,53.447],[-5.0135,53.377],[-5.0345,53.309],[-5.014,53.239],[-4.9405,53.155],[-4.812,53.085],[-4.994,52.971],[-5.094,52.853],[-5.1275,52.783],[-5.1175,52.687],[-5.062,52.623],[-4.919,52.559],[-4.835,52.547],[-4.7265,52.551],[-4.609,52.583],[-4.454,52.582],[-4.398,52.495],[-4.4425,52.413],[-4.539,52.389],[-4.648,52.339],[-4.786,52.325],[-4.9185,52.281],[-5.004,52.228],[-5.1855,52.218],[-5.278,52.186],[-5.3665,52.121],[-5.528,52.054],[-5.6395,51.963],[-5.6735,51.894],[-5.7675,51.827],[-5.805,51.756],[-5.777,51.645],[-5.7,51.581],[-5.622,51.549],[-5.47,51.529],[-5.3805,51.5025],[-5.3725,51.229],[-5.356,50.619],[-5.378,50.543],[-5.4905,50.434],[-5.717,50.384],[-5.8825,50.314],[-6.01,50.2],[-6.048,50.114],[-6.12,50.148],[-6.241,50.179],[-6.4,50.174],[-6.492,50.15],[-6.5975,50.101],[-6.687,50.014],[-6.7045,49.949],[-6.6735,49.828],[-6.603,49.741],[-6.5035,49.693],[-6.425,49.677],[-6.3165,49.677],[-6.1695,49.716],[-6.068,49.767],[-5.992,49.829],[-5.9395,49.909],[-5.8115,49.854],[-5.733,49.838],[-5.6185,49.838],[-5.4975,49.858],[-5.363,49.783],[-5.247,49.76],[-5.145,49.762],[-5.017,49.795],[-4.89,49.858],[-4.7795,49.961],[-4.744,50.022],[-4.652,50.043],[-4.5235,50.124],[-4.3815,50.139],[-4.279,50.115],[-4.0845,50.095],[-3.9895,50.048],[-3.8525,50.014],[-3.754,50.003],[-3.622,50.012],[-3.5115,50.039],[-3.389,50.103],[-3.3355,50.178],[-3.2595,50.233],[-3.183,50.338],[-3.1555,50.453],[-3.0855,50.485],[-2.865,50.517],[-2.769,50.477],[-2.745,50.431],[-2.6705,50.366],[-2.5275,50.318],[-2.4245,50.314],[-2.313,50.335],[-2.1955,50.397],[-2.1205,50.381],[-1.9835,50.382],[-1.788,50.423],[-1.699,50.4745],[-1.567,50.458],[-1.4775,50.409],[-1.3325,50.376],[-1.185,50.385],[-1.031,50.423],[-0.9435,50.468],[-0.867,50.528],[-0.7825,50.522],[-0.6665,50.537],[-0.545,50.593],[-0.348,50.608],[-0.2345,50.626],[0.177,50.538],[0.2445,50.534],[0.416,50.564],[0.55,50.642],[0.789,50.687],[0.867,50.717],[1.0415,50.716],[1.152,50.745],[1.2635,50.8245],[1.2815,50.8955],[1.3555,50.949],[1.5465,51.038],[1.724,51.0985],[1.8875,51.1995],[1.7325,51.265],[1.7715,51.379],[1.717,51.493],[1.6185,51.556],[1.4605,51.592],[1.291,51.602],[1.435,51.661],[1.5415,51.726],[1.61,51.83],[1.7075,51.9],[1.8085,51.944],[1.8935,52.03],[1.944,52.151],[1.9515,52.207],[2.0425,52.342],[2.0915,52.473],[2.068,52.568],[2.069,52.668],[1.987,52.82],[1.889,52.902],[1.6445,53.037],[1.422,53.12],[1.196,53.148],[1.0195,53.179],[0.8105,53.18],[0.689,53.189],[0.6575,53.299],[0.6245,53.355],[0.487,53.5315],[0.47,53.668],[0.38,53.79],[0.166,53.972],[0.2305,54.026],[0.262,54.143],[0.2235,54.212],[0.173,54.255],[0.042,54.312],[-0.089,54.39],[-0.1535,54.476],[-0.244,54.565],[-0.3275,54.622],[-0.587,54.724],[-0.8635,54.784],[-0.9755,54.862],[-1.004,54.921],[-1.0325,55.04],[-1.15,55.172],[-1.19,55.288],[-1.183,55.346],[-1.2235,55.429],[-1.2695,55.581],[-1.252,55.634],[-1.2645,55.698],[-1.313,55.757],[-1.455,55.826],[-1.6845,55.879],[-1.7735,55.978],[-1.863,56.045],[-1.9815,56.0965],[-2.164,56.276],[-2.0625,56.345],[-2.0265,56.4175],[-2.0575,56.5155],[-2.129,56.573],[-2.0845,56.644],[-1.9125,56.767],[-1.8455,56.853],[-1.8235,56.926],[-1.769,56.974],[-1.691,57.081],[-1.671,57.204],[-1.5835,57.257],[-1.4365,57.385],[-1.4055,57.438],[-1.3955,57.526],[-1.5005,57.713],[-1.6925,57.834],[-1.9305,57.896],[-2.1575,57.9],[-2.3745,57.891],[-2.4635,57.875],[-2.8145,57.905],[-3.0515,57.885],[-3.158,57.9135],[-3.254,58.0235],[-3.135,58.108],[-2.928,58.176],[-2.788,58.264],[-2.69,58.387],[-2.6665,58.452],[-2.6725,58.516],[-2.583,58.5625],[-2.5235,58.6215],[-2.509,58.713],[-2.4265,58.741],[-2.304,58.827],[-2.268,58.899],[-2.1945,58.963],[-2.139,59.07],[-2.1385,59.117],[-2.048,59.166],[-1.984,59.254],[-1.978,59.397],[-1.889,59.349],[-1.738,59.313],[-1.52,59.319],[-1.4225,59.342],[-1.308,59.394],[-1.237,59.454],[-1.2015,59.537],[-1.2635,59.654],[-1.0545,59.687],[-0.9325,59.751],[-0.863,59.865],[-0.769,59.972],[-0.645,60.052],[-0.6095,60.109],[-0.6085,60.1795],[-0.404,60.3035],[-0.346,60.3565],[-0.3235,60.448],[-0.3825,60.5315],[-0.358,60.616],[-0.392,60.687],[-0.349,60.824],[-0.4145,60.927],[-0.521,60.985],[-0.705,61.044],[-0.866,61.061],[-1.0785,61.034],[-1.215,60.977],[-1.281,60.923],[-1.454,60.8525],[-1.643,60.8015],[-1.782,60.728],[-1.889,60.69],[-1.974,60.6295],[-2.0165,60.551],[-2.1335,60.5015],[-2.206,60.4395],[-2.23,60.345],[-2.422,60.273],[-2.51,60.182],[-2.508,60.094],[-2.457,60.03],[-2.287,59.944],[-2.1885,59.92],[-2.0225,59.911],[-1.784,59.954],[-1.7865,59.88],[-1.7525,59.814],[-1.6735,59.753],[-1.84,59.725],[-1.9185,59.695],[-2.0085,59.625],[-2.053,59.504],[-2.192,59.568],[-2.269,59.584],[-2.424,59.591],[-2.655,59.551],[-2.8095,59.583],[-2.97,59.581],[-3.0585,59.565],[-3.3045,59.493],[-3.409,59.433],[-3.462,59.328],[-3.615,59.279],[-3.7005,59.213],[-3.7385,59.143],[-3.7545,59.007],[-3.814,58.919],[-3.7995,58.808],[-3.873,58.787],[-3.983,58.801],[-4.148,58.791],[-4.2605,58.759],[-4.585,58.781],[-4.7945,58.814],[-5.0635,58.8245],[-6.328,58.716],[-6.453,58.691],[-6.975,58.4865],[-7.15,58.4455],[-7.248,58.407],[-7.3475,58.455],[-7.444,58.479],[-7.63,58.489],[-7.835,58.46],[-7.9655,58.397],[-8.0135,58.347],[-8.0215,58.232],[-7.9575,58.162],[-7.783,58.094],[-7.64,58.075],[-8.02,57.808],[-8.095,57.695],[-8.1045,57.5465],[-8.065,57.4505],[-7.8445,57.2105],[-7.886,57.061],[-8.001,56.892],[-8.0305,56.807],[-8.0065,56.724],[-7.9645,56.676],[-7.5315,56.3095],[-7.513,56.2245],[-7.4035,56.1365],[-7.272,56.098],[-7.15,56.0905],[-6.968,56.02],[-6.886,55.669],[-6.8095,55.56],[-6.7625,55.528],[-6.5785,55.4435],[-6.7445,55.415]]],[[[-6.56,59.102],[-6.5335,59.03],[-6.4625,58.963],[-6.286,58.899],[-6.091,58.891],[-5.9385,58.923],[-5.7425,58.917],[-5.579,58.953],[-5.489,59.001],[-5.4345,59.061],[-5.4225,59.141],[-5.4515,59.202],[-5.509,59.255],[-5.6455,59.313],[-5.8185,59.335],[-5.936,59.327],[-6.061,59.295],[-6.21,59.301],[-6.394,59.266],[-6.4885,59.218],[-6.5525,59.142],[-6.56,59.102]]],[[[-4.8965,59.025],[-4.873,58.956],[-4.747,58.866],[-4.634,58.834],[-4.4205,58.828],[-4.2715,58.864],[-4.0815,58.973],[-4.011,59.081],[-4.0625,59.188],[-4.161,59.246],[-4.305,59.282],[-4.478,59.286],[-4.671,59.238],[-4.835,59.134],[-4.8965,59.025]]]]},"properties":{"flag":"https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg","name:en":"United Kingdom","wikidata":"Q145","ISO3166-1:alpha2":"GB","ISO3166-1:alpha3":"GBR","ISO3166-1:numeric":"826"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[29.3755,-22.1955],[29.2175,-22.1785],[29.1495,-22.216],[29.0375,-22.217],[28.964,-22.319],[28.971,-22.378],[28.922,-22.458],[28.869,-22.4475],[28.8325,-22.4875],[28.709,-22.515],[28.6425,-22.5575],[28.566,-22.5595],[28.5195,-22.5875],[28.48,-22.571],[28.345,-22.5715],[28.2685,-22.6385],[28.213,-22.6635],[28.1585,-22.7215],[28.163,-22.7555],[28.11,-22.801],[28.0395,-22.841],[28.0365,-22.914],[27.935,-22.963],[27.944,-23.031],[27.9195,-23.0685],[27.7825,-23.143],[27.784,-23.1735],[27.7145,-23.2285],[27.6045,-23.216],[27.573,-23.266],[27.564,-23.3485],[27.5285,-23.3855],[27.462,-23.384],[27.4015,-23.4115],[27.327,-23.4045],[27.3035,-23.4625],[27.1925,-23.4985],[27.148,-23.5515],[27.108,-23.5705],[27.064,-23.627],[27.071,-23.67],[26.9995,-23.653],[26.9715,-23.703],[26.9645,-23.7775],[26.9415,-23.8435],[26.955,-23.874],[26.928,-23.9225],[26.909,-24.029],[26.863,-24.0995],[26.8805,-24.197],[26.866,-24.2485],[26.8,-24.3],[26.704,-24.322],[26.665,-24.3795],[26.6015,-24.41],[26.5145,-24.485],[26.4705,-24.5905],[26.3935,-24.637],[26.1835,-24.688],[26.009,-24.7235],[25.956,-24.7475],[25.859,-24.7555],[25.8885,-24.882],[25.8615,-24.9225],[25.7315,-25.249],[25.7015,-25.289],[25.6905,-25.3745],[25.6635,-25.468],[25.5815,-25.638],[25.514,-25.684],[25.337,-25.771],[25.199,-25.76],[25.1695,-25.7665],[25.038,-25.7215],[24.912,-25.798],[24.7935,-25.8255],[24.713,-25.8055],[24.678,-25.819],[24.606,-25.799],[24.4485,-25.734],[24.379,-25.765],[24.2885,-25.7255],[24.1955,-25.622],[24.1065,-25.63],[24.066,-25.6505],[24.003,-25.648],[23.9665,-25.615],[23.929,-25.638],[23.8435,-25.5515],[23.817,-25.51],[23.7615,-25.5005],[23.759,-25.469],[23.677,-25.427],[23.5945,-25.397],[23.4415,-25.2775],[23.42,-25.3135],[23.3605,-25.286],[23.294,-25.294],[23.274,-25.2745],[23.1925,-25.2775],[23.1365,-25.317],[23.1135,-25.3035],[22.978,-25.34],[22.9675,-25.3835],[22.91,-25.4225],[22.864,-25.4795],[22.8525,-25.5355],[22.814,-25.583],[22.8535,-25.6115],[22.8,-25.723],[22.7695,-25.733],[22.723,-25.9085],[22.721,-26.0185],[22.669,-26.0145],[22.673,-26.0735],[22.5795,-26.157],[22.5495,-26.226],[22.4745,-26.2],[22.422,-26.223],[22.367,-26.319],[22.2475,-26.347],[22.1935,-26.395],[22.201,-26.4205],[22.166,-26.504],[22.0775,-26.5815],[22.0485,-26.635],[21.953,-26.6685],[21.8235,-26.6595],[21.7875,-26.676],[21.7935,-26.7705],[21.78,-26.8025],[21.6945,-26.8615],[21.627,-26.8665],[21.6045,-26.846],[21.486,-26.8415],[21.404,-26.822],[21.264,-26.837],[21.1335,-26.8735],[21.083,-26.8495],[21.0065,-26.8445],[20.978,-26.8115],[20.885,-26.7975],[20.818,-26.823],[20.763,-26.8665],[20.676,-26.861],[20.6505,-26.81],[20.6145,-26.6865],[20.635,-26.665],[20.6315,-26.5845],[20.604,-26.521],[20.6175,-26.4445],[20.646,-26.435],[20.761,-26.2855],[20.7895,-26.2715],[20.8155,-26.192],[20.862,-26.1355],[20.817,-26.005],[20.84,-25.962],[20.807,-25.926],[20.7875,-25.86],[20.7885,-25.803],[20.7615,-25.7915],[20.7135,-25.7115],[20.6765,-25.6835],[20.6975,-25.589],[20.631,-25.526],[20.643,-25.4985],[20.5985,-25.422],[20.566,-25.395],[20.4965,-25.269],[20.4465,-25.199],[20.4125,-25.0825],[20.384,-25.0345],[20.286,-24.9695],[20.2655,-24.9285],[20.2035,-24.8965],[20.1155,-24.8805],[20.0905,-24.833],[20.038,-24.8095],[19.999,-24.764],[19.999,-25.2205],[19.999,-25.7805],[19.9995,-26.1425],[19.999,-26.6185],[20.0,-26.9125],[19.999,-27.547],[19.999,-27.8885],[19.999,-28.428],[19.847,-28.46],[19.813,-28.5085],[19.73,-28.488],[19.671,-28.5295],[19.622,-28.511],[19.5485,-28.541],[19.507,-28.598],[19.4895,-28.682],[19.4055,-28.7365],[19.27,-28.742],[19.24,-28.806],[19.285,-28.9085],[19.23,-28.9125],[19.1965,-28.947],[19.1185,-28.9695],[19.006,-28.9245],[18.9835,-28.879],[18.9355,-28.857],[18.7285,-28.8315],[18.533,-28.868],[18.4235,-28.901],[18.351,-28.8805],[18.2635,-28.8795],[18.194,-28.9155],[18.079,-28.87],[18.046,-28.8745],[17.972,-28.7915],[17.9255,-28.769],[17.736,-28.7565],[17.619,-28.765],[17.599,-28.6945],[17.4935,-28.693],[17.43,-28.7215],[17.4295,-28.57],[17.389,-28.571],[17.3655,-28.509],[17.333,-28.475],[17.3555,-28.437],[17.3965,-28.4275],[17.414,-28.376],[17.376,-28.321],[17.3825,-28.296],[17.334,-28.226],[17.215,-28.248],[17.184,-28.209],[17.1965,-28.128],[17.1445,-28.0955],[17.0825,-28.033],[16.8845,-28.09],[16.888,-28.173],[16.857,-28.207],[16.803,-28.222],[16.755,-28.302],[16.808,-28.361],[16.7695,-28.396],[16.777,-28.44],[16.734,-28.4945],[16.6885,-28.4705],[16.621,-28.5],[16.5925,-28.5355],[16.48,-28.5655],[16.453,-28.633],[16.3335,-28.8005],[16.3895,-28.9405],[16.4305,-29.004],[16.5,-29.0525],[16.5265,-29.1085],[16.6095,-29.1985],[16.6225,-29.273],[16.6715,-29.3755],[16.7245,-29.4495],[16.7515,-29.5105],[16.7855,-29.642],[16.8275,-29.7295],[16.85,-29.85],[16.898,-29.9865],[16.9355,-30.0515],[16.972,-30.185],[17.0415,-30.3325],[17.057,-30.4045],[17.193,-30.624],[17.2155,-30.689],[17.28,-30.7955],[17.311,-30.8285],[17.397,-31.0055],[17.4695,-31.092],[17.5455,-31.24],[17.6525,-31.361],[17.7145,-31.4715],[17.897,-31.667],[17.9975,-31.8175],[18.0325,-31.924],[18.038,-31.985],[18.068,-32.073],[18.066,-32.1205],[18.0875,-32.2335],[17.6955,-32.605],[17.6625,-32.644],[17.6155,-32.7755],[17.61,-32.8515],[17.637,-33.0245],[17.6955,-33.1475],[17.762,-33.236],[17.856,-33.495],[18.088,-34.202],[18.195,-34.412],[18.269,-34.4805],[18.3655,-34.5365],[18.4485,-34.5565],[18.746,-34.5815],[19.2985,-34.87],[19.532,-34.9615],[19.6255,-34.985],[19.9645,-35.032],[20.0355,-35.0325],[20.125,-35.009],[20.9165,-34.6685],[21.3275,-34.637],[21.759,-34.5945],[21.994,-34.545],[22.065,-34.5115],[22.2605,-34.378],[22.99,-34.2945],[23.39,-34.3115],[23.9365,-34.3505],[24.496,-34.3905],[24.818,-34.4135],[24.8955,-34.408],[25.47,-34.287],[25.7875,-34.217],[26.373,-34.0295],[26.723,-33.9025],[26.8505,-33.8355],[26.941,-33.8165],[27.0415,-33.768],[27.1215,-33.747],[27.2455,-33.691],[27.303,-33.6455],[27.6085,-33.4675],[27.7385,-33.387],[27.878,-33.286],[27.959,-33.2515],[28.097,-33.167],[28.1355,-33.125],[28.2385,-33.062],[28.3215,-32.9575],[28.4005,-32.9255],[28.53,-32.8555],[28.581,-32.8005],[28.6455,-32.768],[28.7325,-32.7015],[28.7705,-32.6555],[28.8345,-32.6095],[28.9515,-32.492],[29.0665,-32.4075],[29.198,-32.264],[29.2605,-32.217],[29.3365,-32.114],[29.3905,-32.076],[29.468,-31.974],[29.528,-31.9315],[29.5915,-31.8515],[29.686,-31.802],[29.8315,-31.694],[29.8905,-31.6365],[29.9995,-31.589],[30.1295,-31.48],[30.2405,-31.3725],[30.37,-31.219],[30.4135,-31.178],[30.546,-30.999],[30.578,-30.9655],[30.663,-30.837],[30.747,-30.7275],[30.933,-30.442],[31.079,-30.1675],[31.154,-30.1],[31.2705,-29.9575],[31.29,-29.897],[31.288,-29.826],[31.3335,-29.743],[31.5285,-29.499],[31.6305,-29.3975],[31.7495,-29.292],[31.901,-29.132],[32.1015,-29.0625],[32.193,-29.0035],[32.3885,-28.825],[32.4515,-28.78],[32.5425,-28.696],[32.5855,-28.64],[32.634,-28.528],[32.652,-28.4265],[32.722,-28.3095],[32.7755,-28.1865],[32.7835,-28.1105],[32.8185,-27.944],[32.816,-27.8975],[32.8395,-27.7815],[32.869,-27.691],[32.9015,-27.538],[32.9595,-27.4095],[33.0515,-27.176],[33.0855,-27.052],[33.1025,-26.921],[32.902,-26.858],[32.692,-26.868],[32.33,-26.8635],[32.2205,-26.833],[32.1865,-26.863],[32.1345,-26.8405],[32.0925,-26.805],[32.016,-26.82],[31.996,-26.924],[31.9745,-27.1165],[31.978,-27.3175],[31.4935,-27.315],[31.1505,-27.202],[31.0615,-27.0965],[30.985,-27.037],[30.9585,-26.992],[30.9735,-26.908],[30.9075,-26.86],[30.8875,-26.8035],[30.813,-26.8445],[30.791,-26.7125],[30.792,-26.569],[30.805,-26.4665],[30.892,-26.321],[30.9615,-26.2615],[31.071,-26.0675],[31.113,-25.9825],[31.1265,-25.921],[31.2635,-25.8095],[31.316,-25.743],[31.4165,-25.719],[31.868,-25.9995],[31.975,-25.9525],[31.9305,-25.841],[31.9785,-25.6935],[32.006,-25.645],[31.996,-25.5265],[31.9795,-25.457],[32.016,-25.3775],[32.0335,-25.1325],[31.998,-24.6895],[32.009,-24.5755],[32.009,-24.4555],[31.987,-24.302],[31.908,-24.182],[31.8805,-23.9525],[31.768,-23.885],[31.6975,-23.722],[31.69,-23.6255],[31.6625,-23.586],[31.561,-23.482],[31.5505,-23.4105],[31.5635,-23.198],[31.4755,-22.9255],[31.3075,-22.424],[31.2735,-22.375],[31.221,-22.37],[31.162,-22.3285],[31.094,-22.3485],[30.9995,-22.3165],[30.839,-22.292],[30.704,-22.317],[30.667,-22.3105],[30.626,-22.3365],[30.492,-22.3165],[30.379,-22.3515],[30.283,-22.354],[30.2275,-22.295],[30.1215,-22.3085],[30.0165,-22.229],[29.972,-22.224],[29.923,-22.189],[29.805,-22.1655],[29.7735,-22.1415],[29.677,-22.139],[29.6545,-22.125],[29.6055,-22.1555],[29.541,-22.1525],[29.528,-22.178],[29.472,-22.1655],[29.3755,-22.1955]],[[27.0115,-29.649],[27.0595,-29.635],[27.12,-29.5775],[27.206,-29.559],[27.363,-29.476],[27.392,-29.4155],[27.4195,-29.4105],[27.4365,-29.334],[27.466,-29.294],[27.5115,-29.283],[27.548,-29.2475],[27.515,-29.224],[27.617,-29.153],[27.652,-29.1005],[27.6825,-29.024],[27.7155,-29.007],[27.7295,-28.947],[27.8325,-28.906],[27.896,-28.9085],[27.948,-28.8525],[27.9905,-28.883],[28.073,-28.8155],[28.166,-28.713],[28.233,-28.695],[28.2795,-28.714],[28.357,-28.6925],[28.414,-28.6275],[28.4715,-28.6055],[28.507,-28.618],[28.613,-28.5965],[28.648,-28.5715],[28.7035,-28.619],[28.7045,-28.6715],[28.795,-28.6995],[28.808,-28.771],[28.905,-28.759],[28.944,-28.7825],[28.939,-28.86],[28.975,-28.893],[29.075,-28.919],[29.1515,-28.9875],[29.2155,-29.0265],[29.259,-29.0905],[29.308,-29.0765],[29.3555,-29.127],[29.3565,-29.2015],[29.408,-29.212],[29.437,-29.2645],[29.4555,-29.341],[29.4035,-29.444],[29.3295,-29.47],[29.297,-29.5225],[29.3195,-29.5815],[29.285,-29.5865],[29.279,-29.6445],[29.236,-29.64],[29.1835,-29.668],[29.145,-29.7165],[29.1315,-29.8255],[29.1025,-29.85],[29.16,-29.883],[29.1695,-29.9165],[29.109,-29.937],[29.006,-30.0045],[28.9855,-30.0315],[28.879,-30.07],[28.859,-30.088],[28.69,-30.1245],[28.6675,-30.1405],[28.5685,-30.123],[28.452,-30.158],[28.4,-30.1435],[28.365,-30.1685],[28.3405,-30.2345],[28.3055,-30.255],[28.2365,-30.26],[28.2155,-30.307],[28.253,-30.3235],[28.271,-30.371],[28.229,-30.43],[28.1795,-30.439],[28.1555,-30.472],[28.1775,-30.5225],[28.1505,-30.5845],[28.114,-30.599],[28.123,-30.6635],[28.004,-30.659],[27.94,-30.644],[27.918,-30.6025],[27.7815,-30.6175],[27.7415,-30.604],[27.693,-30.545],[27.6215,-30.5045],[27.6015,-30.4465],[27.534,-30.3845],[27.475,-30.3565],[27.4645,-30.314],[27.3765,-30.3255],[27.3655,-30.219],[27.394,-30.1425],[27.327,-30.148],[27.2965,-30.056],[27.223,-29.997],[27.0965,-29.73],[27.0115,-29.649]]],[[[37.3005,-46.9355],[37.319,-47.0125],[37.393,-47.097],[37.491,-47.1475],[37.646,-47.1765],[37.8925,-47.176],[37.9845,-47.1535],[38.0645,-47.1125],[38.136,-47.049],[38.201,-46.935],[38.197,-46.86],[38.175,-46.8175],[38.234,-46.7745],[38.2795,-46.7065],[38.29,-46.63],[38.2375,-46.5265],[38.1585,-46.466],[38.052,-46.43],[37.938,-46.411],[37.8225,-46.418],[37.7385,-46.444],[37.6445,-46.502],[37.5915,-46.5795],[37.5835,-46.643],[37.4595,-46.704],[37.3575,-46.798],[37.3005,-46.9355]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/a/af/Flag_of_South_Africa.svg","name:en":"South Africa","wikidata":"Q258","ISO3166-1:alpha2":"ZA","ISO3166-1:alpha3":"ZAF","ISO3166-1:numeric":"710"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[30.796,-8.2765],[30.567,-8.2975],[30.537,-8.2795],[30.4985,-8.2965],[30.429,-8.275],[29.702,-8.3695],[29.6255,-8.415],[29.56,-8.381],[28.991,-8.462],[28.9365,-8.545],[28.962,-8.6105],[28.956,-8.686],[28.8865,-8.8135],[28.734,-9.0],[28.6695,-9.058],[28.512,-9.1705],[28.3995,-9.226],[28.3735,-9.2555],[28.379,-9.2935],[28.431,-9.324],[28.518,-9.3545],[28.5115,-9.44],[28.574,-9.493],[28.57,-9.545],[28.6095,-9.589],[28.6055,-9.619],[28.636,-9.747],[28.6685,-9.751],[28.6755,-9.812],[28.6595,-9.883],[28.626,-9.9475],[28.6215,-9.9935],[28.644,-10.02],[28.629,-10.066],[28.627,-10.161],[28.577,-10.221],[28.636,-10.3105],[28.6185,-10.3645],[28.644,-10.425],[28.63,-10.5185],[28.675,-10.5685],[28.6585,-10.6265],[28.695,-10.6765],[28.6255,-10.7155],[28.562,-10.8215],[28.544,-10.869],[28.5405,-10.9475],[28.5105,-10.97],[28.5125,-11.0205],[28.4765,-11.1035],[28.5,-11.147],[28.4825,-11.1975],[28.49,-11.238],[28.457,-11.2925],[28.456,-11.3715],[28.396,-11.4755],[28.386,-11.5875],[28.434,-11.6515],[28.4305,-11.73],[28.4555,-11.775],[28.4355,-11.816],[28.493,-11.845],[28.5135,-11.877],[28.5755,-11.911],[28.61,-11.9065],[28.634,-11.9415],[28.719,-11.9905],[28.765,-11.9935],[28.7775,-12.0475],[28.807,-12.067],[28.8545,-12.135],[28.9175,-12.1725],[29.0095,-12.2935],[29.0405,-12.3835],[29.1205,-12.395],[29.177,-12.3725],[29.2395,-12.379],[29.2695,-12.3605],[29.3465,-12.421],[29.3825,-12.415],[29.4795,-12.46],[29.53,-12.422],[29.4815,-12.388],[29.459,-12.3095],[29.479,-12.254],[29.562,-12.2025],[29.5985,-12.1955],[29.6475,-12.2345],[29.721,-12.2185],[29.7455,-12.1675],[29.815,-12.15],[29.815,-12.4565],[29.815,-13.097],[29.815,-13.4445],[29.7645,-13.4355],[29.7265,-13.459],[29.613,-13.4035],[29.6515,-13.347],[29.6465,-13.2945],[29.678,-13.262],[29.576,-13.2055],[29.53,-13.229],[29.4345,-13.315],[29.3615,-13.3255],[29.183,-13.4415],[29.1435,-13.379],[29.0895,-13.3795],[29.0055,-13.423],[28.9625,-13.3765],[28.959,-13.334],[28.922,-13.2345],[28.918,-13.1905],[28.8905,-13.154],[28.8475,-13.1555],[28.8475,-13.111],[28.8235,-13.08],[28.8255,-13.005],[28.781,-12.9375],[28.754,-12.9415],[28.7125,-12.866],[28.654,-12.8145],[28.609,-12.8335],[28.5805,-12.899],[28.543,-12.868],[28.543,-12.8235],[28.4515,-12.722],[28.467,-12.69],[28.5085,-12.6895],[28.534,-12.635],[28.4925,-12.5885],[28.4455,-12.563],[28.4385,-12.514],[28.399,-12.5005],[28.3395,-12.4315],[28.194,-12.4045],[28.1695,-12.378],[28.122,-12.438],[28.083,-12.3515],[28.043,-12.3555],[27.961,-12.3145],[27.932,-12.2425],[27.823,-12.2275],[27.7685,-12.2745],[27.65,-12.286],[27.615,-12.241],[27.5245,-12.1615],[27.496,-12.0565],[27.4665,-12.044],[27.4765,-11.9545],[27.3955,-11.8725],[27.287,-11.8035],[27.231,-11.787],[27.219,-11.746],[27.2355,-11.685],[27.2075,-11.5685],[27.1605,-11.601],[27.0535,-11.593],[27.016,-11.618],[27.0475,-11.707],[27.0065,-11.745],[26.996,-11.8285],[26.961,-11.916],[26.9155,-11.9375],[26.891,-11.9775],[26.8195,-11.987],[26.776,-11.956],[26.709,-12.0065],[26.6905,-11.993],[26.576,-11.9855],[26.5,-11.952],[26.479,-11.9195],[26.4235,-11.9115],[26.3125,-11.9635],[26.303,-11.9475],[26.2065,-11.921],[26.1855,-11.938],[26.048,-11.9365],[25.968,-11.8825],[25.927,-11.886],[25.891,-11.852],[25.887,-11.8085],[25.8305,-11.8295],[25.721,-11.817],[25.671,-11.7755],[25.656,-11.733],[25.5055,-11.7915],[25.4955,-11.7145],[25.332,-11.627],[25.3065,-11.565],[25.3135,-11.518],[25.306,-11.4335],[25.2835,-11.397],[25.2945,-11.301],[25.354,-11.201],[25.318,-11.1885],[25.205,-11.236],[25.1455,-11.2375],[25.06,-11.259],[25.028,-11.245],[24.873,-11.2655],[24.8635,-11.2915],[24.7945,-11.31],[24.7505,-11.303],[24.672,-11.3525],[24.6415,-11.401],[24.5915,-11.42],[24.5275,-11.465],[24.4385,-11.4665],[24.42,-11.427],[24.3645,-11.4005],[24.3415,-11.367],[24.4015,-11.2645],[24.4015,-11.1995],[24.3795,-11.085],[24.302,-11.0415],[24.256,-11.045],[24.186,-11.0205],[24.144,-11.031],[24.1325,-10.9245],[24.065,-10.8885],[24.0,-10.89],[24.018,-10.942],[24.0105,-11.0185],[24.032,-11.134],[24.0315,-11.2745],[24.056,-11.3085],[24.0875,-11.4035],[24.024,-11.446],[24.038,-11.531],[23.9755,-11.6505],[23.993,-11.743],[24.023,-11.83],[23.998,-11.856],[23.9805,-12.0775],[23.9825,-12.1715],[24.0,-12.208],[24.0595,-12.2635],[24.078,-12.362],[24.064,-12.426],[24.0405,-12.4345],[23.947,-12.5595],[23.96,-12.592],[23.9205,-12.6815],[23.933,-12.698],[23.898,-12.7695],[23.8935,-12.8415],[23.945,-12.8955],[23.9835,-12.8985],[24.025,-12.9705],[24.058,-13.001],[23.6725,-13.001],[23.229,-13.0005],[22.6525,-12.9995],[22.0035,-12.9995],[22.001,-13.3095],[22.0005,-13.73],[22.0005,-14.146],[22.0005,-14.7385],[22.0005,-15.4065],[22.0005,-15.8865],[22.0,-16.2075],[22.03,-16.2185],[22.1205,-16.348],[22.1395,-16.459],[22.1355,-16.49],[22.185,-16.546],[22.264,-16.602],[22.42,-16.7455],[22.492,-16.835],[22.599,-16.9185],[22.6285,-16.961],[22.716,-17.0425],[22.729,-17.075],[22.7915,-17.12],[22.802,-17.1655],[22.8965,-17.201],[22.9095,-17.2225],[22.9955,-17.274],[23.087,-17.4135],[23.136,-17.4665],[23.1775,-17.475],[23.2215,-17.5325],[23.3415,-17.554],[23.391,-17.5765],[23.434,-17.638],[23.927,-17.539],[24.2485,-17.474],[24.327,-17.492],[24.3815,-17.4715],[24.453,-17.48],[24.559,-17.539],[24.6275,-17.511],[24.7085,-17.497],[24.8025,-17.5365],[24.829,-17.52],[24.8835,-17.529],[24.9075,-17.5545],[24.975,-17.5535],[24.998,-17.592],[25.032,-17.5865],[25.0895,-17.663],[25.194,-17.7695],[25.262,-17.7905],[25.263,-17.7915],[25.3315,-17.8365],[25.379,-17.8525],[25.453,-17.8435],[25.5155,-17.8655],[25.5465,-17.8395],[25.6185,-17.841],[25.682,-17.809],[25.705,-17.8385],[25.771,-17.8515],[25.856,-17.918],[25.863,-17.975],[25.9705,-18.003],[26.0015,-17.977],[26.092,-17.981],[26.0885,-17.9385],[26.2075,-17.8885],[26.245,-17.918],[26.3085,-17.937],[26.411,-17.9385],[26.51,-17.9905],[26.5635,-17.988],[26.602,-18.051],[26.698,-18.062],[26.752,-18.021],[26.803,-18.024],[26.8365,-17.991],[26.895,-17.9895],[26.9675,-17.965],[27.041,-17.959],[27.0845,-17.901],[27.153,-17.837],[27.144,-17.8065],[27.226,-17.6905],[27.2985,-17.6125],[27.424,-17.51],[27.559,-17.4105],[27.6245,-17.3325],[27.63,-17.2465],[27.801,-17.0055],[27.8335,-16.969],[27.9185,-16.9225],[28.1355,-16.8245],[28.269,-16.718],[28.642,-16.5715],[28.7355,-16.5545],[28.8225,-16.4705],[28.8165,-16.4455],[28.851,-16.3745],[28.821,-16.303],[28.857,-16.2495],[28.836,-16.2125],[28.8435,-16.1535],[28.87,-16.1035],[28.8495,-16.0405],[28.9335,-15.9435],[29.0305,-15.932],[29.0465,-15.8985],[29.222,-15.7685],[29.288,-15.7545],[29.4155,-15.6865],[29.487,-15.6925],[29.571,-15.6445],[29.63,-15.6645],[29.8295,-15.6095],[29.9325,-15.6235],[29.9705,-15.6435],[30.176,-15.6235],[30.2195,-15.667],[30.2745,-15.6445],[30.354,-15.6555],[30.421,-15.621],[30.3665,-15.5405],[30.4015,-15.4765],[30.377,-15.437],[30.383,-15.387],[30.361,-15.3295],[30.283,-15.2645],[30.257,-15.164],[30.213,-15.0835],[30.234,-15.0355],[30.2165,-14.9975],[30.365,-14.9595],[30.5175,-14.888],[30.799,-14.7835],[31.079,-14.7175],[31.19,-14.6795],[31.491,-14.612],[31.6835,-14.5065],[31.8265,-14.473],[31.9295,-14.42],[32.0565,-14.3885],[32.2415,-14.3235],[32.4515,-14.2825],[32.6105,-14.2145],[33.2165,-13.9995],[33.186,-13.959],[33.145,-13.939],[33.0865,-13.9705],[33.041,-14.0495],[32.994,-14.007],[32.998,-13.943],[32.9685,-13.9375],[32.9545,-13.8885],[32.907,-13.8255],[32.844,-13.7935],[32.8075,-13.8005],[32.7725,-13.7695],[32.843,-13.722],[32.7835,-13.6495],[32.7185,-13.651],[32.6785,-13.6255],[32.6705,-13.5745],[32.6975,-13.559],[32.7575,-13.5735],[32.8475,-13.526],[32.86,-13.469],[32.8985,-13.4495],[32.932,-13.3735],[32.9305,-13.325],[33.008,-13.1935],[32.984,-13.14],[32.99,-13.037],[33.017,-12.954],[33.0175,-12.8925],[32.976,-12.886],[32.946,-12.8465],[32.9525,-12.7615],[33.0575,-12.5945],[33.1815,-12.6135],[33.232,-12.595],[33.272,-12.5485],[33.3225,-12.533],[33.3755,-12.554],[33.399,-12.516],[33.5485,-12.364],[33.531,-12.34],[33.4755,-12.3255],[33.376,-12.348],[33.3115,-12.2485],[33.3135,-12.202],[33.257,-12.14],[33.2735,-12.066],[33.3325,-11.9875],[33.3485,-11.93],[33.3285,-11.8955],[33.3405,-11.823],[33.327,-11.6975],[33.331,-11.612],[33.301,-11.592],[33.2455,-11.5915],[33.2605,-11.539],[33.256,-11.475],[33.232,-11.444],[33.3335,-11.311],[33.3545,-11.255],[33.3805,-11.234],[33.4045,-11.156],[33.328,-11.0705],[33.305,-10.939],[33.259,-10.889],[33.277,-10.8495],[33.3165,-10.8615],[33.336,-10.821],[33.418,-10.794],[33.4535,-10.808],[33.5355,-10.7525],[33.598,-10.658],[33.6835,-10.6085],[33.6725,-10.5375],[33.633,-10.4985],[33.6335,-10.458],[33.5725,-10.401],[33.5445,-10.342],[33.561,-10.239],[33.5555,-10.212],[33.467,-10.158],[33.433,-10.1165],[33.3175,-10.0555],[33.355,-9.934],[33.391,-9.912],[33.35,-9.815],[33.3065,-9.804],[33.2425,-9.7055],[33.2275,-9.6295],[33.201,-9.601],[33.1135,-9.5865],[33.1065,-9.659],[33.0605,-9.616],[32.9965,-9.626],[32.994,-9.5625],[33.0145,-9.5025],[32.9475,-9.487],[32.9535,-9.4015],[32.8925,-9.366],[32.8675,-9.374],[32.7655,-9.323],[32.7555,-9.285],[32.6485,-9.276],[32.5955,-9.2485],[32.541,-9.2545],[32.4865,-9.1725],[32.4315,-9.1185],[32.3265,-9.132],[32.243,-9.125],[32.164,-9.063],[32.061,-9.0515],[31.996,-9.076],[31.9365,-9.018],[31.947,-8.937],[31.8295,-8.892],[31.7795,-8.8905],[31.735,-8.9235],[31.68,-8.9115],[31.6585,-8.88],[31.585,-8.839],[31.57,-8.809],[31.582,-8.7455],[31.5705,-8.7095],[31.5265,-8.681],[31.4895,-8.684],[31.4585,-8.6405],[31.3105,-8.608],[31.2855,-8.631],[31.211,-8.5835],[31.16,-8.626],[31.078,-8.622],[31.028,-8.592],[30.978,-8.5385],[30.8785,-8.4085],[30.796,-8.2765]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/06/Flag_of_Zambia.svg","name:en":"Zambia","wikidata":"Q953","ISO3166-1:alpha2":"ZM","ISO3166-1:alpha3":"ZMB","ISO3166-1:numeric":"894"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[32.9535,-9.4015],[32.9925,-9.3685],[33.022,-9.4],[33.1435,-9.4925],[33.2135,-9.506],[33.326,-9.486],[33.3905,-9.5335],[33.4435,-9.6145],[33.508,-9.626],[33.554,-9.5875],[33.699,-9.6145],[33.7315,-9.5845],[33.7735,-9.5865],[33.8125,-9.63],[33.871,-9.664],[33.9165,-9.713],[33.9555,-9.654],[33.9455,-9.5725],[33.9785,-9.522],[34.038,-9.4935],[34.209,-9.6455],[34.3265,-9.7575],[34.345,-9.8],[34.416,-9.885],[34.54,-10.0665],[34.5195,-10.119],[34.5695,-10.2065],[34.565,-10.258],[34.5845,-10.3035],[34.56,-10.4325],[34.5935,-10.49],[34.568,-10.5285],[34.584,-10.5725],[34.625,-10.597],[34.681,-10.7515],[34.6705,-10.8945],[34.6545,-10.9535],[34.612,-11.029],[34.6375,-11.053],[34.638,-11.118],[34.7565,-11.2085],[34.8005,-11.281],[34.7895,-11.3135],[34.928,-11.3985],[34.9305,-11.4625],[34.956,-11.4915],[34.959,-11.5735],[35.2315,-11.5685],[35.4155,-11.572],[35.561,-11.566],[35.6505,-11.5505],[35.708,-11.509],[35.7195,-11.475],[35.766,-11.471],[35.8115,-11.4195],[35.8465,-11.412],[35.9135,-11.438],[35.95,-11.4315],[36.0085,-11.487],[36.042,-11.4945],[36.063,-11.5405],[36.128,-11.518],[36.132,-11.5555],[36.1885,-11.598],[36.183,-11.671],[36.221,-11.708],[36.27,-11.6905],[36.2875,-11.7205],[36.3545,-11.7135],[36.3835,-11.6805],[36.494,-11.6815],[36.517,-11.6975],[36.5195,-11.7615],[36.5735,-11.6985],[36.6225,-11.737],[36.767,-11.649],[36.794,-11.6085],[36.8365,-11.584],[36.9105,-11.6055],[36.9865,-11.58],[37.044,-11.593],[37.08,-11.625],[37.1575,-11.6315],[37.2255,-11.6545],[37.301,-11.641],[37.333,-11.678],[37.415,-11.6855],[37.4455,-11.6365],[37.502,-11.614],[37.581,-11.626],[37.6555,-11.5795],[37.753,-11.547],[37.7855,-11.505],[37.7775,-11.4575],[37.8275,-11.3725],[37.8305,-11.311],[37.944,-11.2585],[37.9705,-11.2725],[38.064,-11.2775],[38.1115,-11.2605],[38.265,-11.2875],[38.2825,-11.3275],[38.316,-11.3265],[38.3705,-11.3745],[38.489,-11.4165],[38.5365,-11.3655],[38.5775,-11.3475],[38.611,-11.307],[38.6635,-11.273],[38.7495,-11.274],[38.7675,-11.25],[38.8495,-11.2085],[38.883,-11.172],[39.0785,-11.159],[39.123,-11.1435],[39.2365,-11.173],[39.274,-11.1575],[39.356,-11.083],[39.438,-11.0485],[39.522,-10.9815],[39.595,-10.9515],[39.7595,-10.9155],[39.876,-10.846],[39.999,-10.807],[40.1345,-10.69],[40.176,-10.668],[40.2155,-10.6105],[40.298,-10.566],[40.371,-10.5505],[40.419,-10.483],[40.493,-10.4145],[40.647,-10.325],[40.644,-10.2745],[40.574,-10.1725],[40.5365,-10.133],[39.9945,-9.7],[39.8295,-9.034],[39.793,-8.6],[40.0765,-7.8755],[40.1145,-7.705],[40.1295,-6.908],[40.0985,-6.797],[39.7985,-6.3195],[40.0505,-5.2535],[40.074,-5.101],[40.0805,-4.8905],[40.055,-4.8095],[39.987,-4.727],[39.91,-4.691],[39.7955,-4.6985],[39.7645,-4.681],[39.647,-4.681],[39.605,-4.681],[39.478,-4.892],[39.424,-4.898],[39.3645,-4.8635],[39.347,-4.832],[39.2195,-4.68],[38.8255,-4.405],[38.367,-4.086],[37.99,-3.8155],[37.784,-3.6715],[37.7495,-3.5445],[37.6655,-3.505],[37.617,-3.52],[37.6175,-3.466],[37.5845,-3.4405],[37.712,-3.3105],[37.673,-3.061],[37.512,-2.9545],[37.381,-2.884],[37.004,-2.6715],[36.6315,-2.463],[36.1455,-2.189],[36.001,-2.1055],[35.484,-1.8135],[35.141,-1.6225],[34.776,-1.4145],[34.3865,-1.194],[34.0805,-1.022],[34.046,-1.046],[34.019,-1.0],[33.9335,-1.0],[33.5195,-1.0],[32.7505,-1.0],[31.999,-1.0],[31.832,-0.9985],[31.27,-1.0],[30.7995,-1.0],[30.763,-0.986],[30.6985,-1.015],[30.6585,-1.0625],[30.583,-1.061],[30.5405,-1.0745],[30.473,-1.0575],[30.453,-1.081],[30.471,-1.157],[30.5095,-1.172],[30.5515,-1.252],[30.5685,-1.337],[30.6125,-1.35],[30.6295,-1.385],[30.6825,-1.397],[30.739,-1.454],[30.745,-1.5235],[30.84,-1.651],[30.8215,-1.693],[30.832,-1.8145],[30.808,-1.938],[30.899,-2.0775],[30.8435,-2.2055],[30.8525,-2.321],[30.7765,-2.389],[30.717,-2.3555],[30.657,-2.4135],[30.61,-2.3995],[30.5435,-2.413],[30.485,-2.556],[30.42,-2.6625],[30.4595,-2.691],[30.4405,-2.745],[30.439,-2.802],[30.415,-2.843],[30.4495,-2.913],[30.5065,-2.9535],[30.5765,-2.8945],[30.647,-2.954],[30.668,-2.9935],[30.704,-2.9715],[30.7475,-2.9975],[30.8375,-2.976],[30.816,-3.0395],[30.836,-3.094],[30.846,-3.206],[30.8135,-3.2235],[30.826,-3.264],[30.6665,-3.3205],[30.635,-3.3595],[30.6775,-3.398],[30.618,-3.4665],[30.501,-3.517],[30.451,-3.5605],[30.4555,-3.607],[30.4215,-3.6415],[30.388,-3.713],[30.41,-3.7665],[30.331,-3.7845],[30.308,-3.8455],[30.256,-3.884],[30.2135,-3.9905],[30.215,-4.0465],[30.1575,-4.115],[30.078,-4.168],[30.04,-4.2765],[30.006,-4.28],[29.9615,-4.3255],[29.8935,-4.3535],[29.8665,-4.381],[29.8155,-4.364],[29.765,-4.4275],[29.7685,-4.4565],[29.7075,-4.462],[29.6595,-4.448],[29.436,-4.449],[29.3715,-4.5695],[29.336,-4.652],[29.3285,-4.775],[29.3515,-4.9515],[29.3825,-5.052],[29.4245,-5.1425],[29.4585,-5.1845],[29.485,-5.289],[29.5345,-5.448],[29.5975,-5.579],[29.626,-5.6855],[29.632,-5.7485],[29.614,-5.799],[29.5355,-5.894],[29.5045,-5.946],[29.4985,-6.0565],[29.533,-6.167],[29.537,-6.239],[29.571,-6.338],[29.656,-6.4465],[29.704,-6.5865],[29.746,-6.6415],[29.8915,-6.7555],[30.0375,-6.822],[30.1885,-6.9625],[30.3115,-7.137],[30.382,-7.284],[30.426,-7.428],[30.4355,-7.5095],[30.456,-7.5805],[30.5095,-7.6795],[30.6495,-7.878],[30.697,-7.9705],[30.7595,-8.145],[30.796,-8.2765],[30.8785,-8.4085],[30.978,-8.5385],[31.028,-8.592],[31.078,-8.622],[31.16,-8.626],[31.211,-8.5835],[31.2855,-8.631],[31.3105,-8.608],[31.4585,-8.6405],[31.4895,-8.684],[31.5265,-8.681],[31.5705,-8.7095],[31.582,-8.7455],[31.57,-8.809],[31.585,-8.839],[31.6585,-8.88],[31.68,-8.9115],[31.735,-8.9235],[31.7795,-8.8905],[31.8295,-8.892],[31.947,-8.937],[31.9365,-9.018],[31.996,-9.076],[32.061,-9.0515],[32.164,-9.063],[32.243,-9.125],[32.3265,-9.132],[32.4315,-9.1185],[32.4865,-9.1725],[32.541,-9.2545],[32.5955,-9.2485],[32.6485,-9.276],[32.7555,-9.285],[32.7655,-9.323],[32.8675,-9.374],[32.8925,-9.366],[32.9535,-9.4015]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/38/Flag_of_Tanzania.svg","name:en":"Tanzania","wikidata":"Q924","ISO3166-1:alpha2":"TZ","ISO3166-1:alpha3":"TZA","ISO3166-1:numeric":"834"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[166.3355,-14.861],[166.443,-15.4325],[166.4605,-15.484],[166.5855,-15.738],[166.7295,-15.9395],[167.2875,-16.699],[167.449,-16.9615],[167.977,-17.815],[168.414,-18.428],[168.803,-18.969],[169.0265,-19.542],[169.074,-19.6365],[169.5825,-20.3705],[169.6435,-20.428],[169.729,-20.46],[169.7865,-20.462],[169.9165,-20.4455],[169.97,-20.4255],[170.029,-20.384],[170.0915,-20.2985],[170.4275,-19.6255],[170.45,-19.516],[170.4285,-19.4215],[170.3795,-19.3595],[169.4655,-18.565],[169.0815,-18.0215],[168.8095,-17.635],[168.857,-17.0215],[168.847,-16.942],[168.72,-16.568],[168.4855,-15.877],[168.3,-14.596],[168.264,-14.376],[167.8395,-13.191],[167.792,-13.116],[167.735,-13.075],[167.678,-13.057],[166.583,-12.874],[166.4785,-12.883],[166.413,-12.92],[166.365,-12.9785],[166.3445,-13.0335],[166.3375,-13.1345],[166.3415,-14.425],[166.3425,-14.7745],[166.3355,-14.861]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/bc/Flag_of_Vanuatu.svg","name:en":"Vanuatu","wikidata":"Q686","ISO3166-1:alpha2":"VU","ISO3166-1:alpha3":"VUT","ISO3166-1:numeric":"548"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-0.136,11.1395],[-0.1265,11.106],[-0.0545,11.086],[-0.005,11.108],[0.024,11.0625],[0.032,10.9805],[-0.005,10.9645],[-0.0255,10.8805],[-0.022,10.8185],[-0.074,10.7575],[-0.0785,10.6905],[-0.059,10.6325],[0.0415,10.6015],[0.061,10.561],[0.1425,10.5265],[0.1735,10.426],[0.2645,10.409],[0.334,10.3365],[0.3615,10.258],[0.351,10.1925],[0.3565,10.091],[0.4175,10.0575],[0.4095,10.0205],[0.3605,10.0275],[0.3575,9.8465],[0.325,9.772],[0.3245,9.7225],[0.351,9.675],[0.3175,9.652],[0.264,9.6635],[0.303,9.5965],[0.3565,9.625],[0.366,9.576],[0.239,9.5715],[0.2475,9.5215],[0.309,9.502],[0.23,9.4625],[0.2655,9.4305],[0.337,9.452],[0.3595,9.499],[0.4485,9.4995],[0.489,9.487],[0.4955,9.446],[0.567,9.406],[0.5385,9.306],[0.506,9.255],[0.5335,9.2095],[0.457,9.0355],[0.494,8.954],[0.5165,8.9375],[0.5275,8.878],[0.5005,8.8145],[0.441,8.7895],[0.3795,8.792],[0.3745,8.754],[0.4715,8.5995],[0.511,8.564],[0.647,8.491],[0.654,8.42],[0.6865,8.4115],[0.729,8.343],[0.731,8.2875],[0.6385,8.2605],[0.5845,8.093],[0.6025,8.022],[0.5965,7.987],[0.6275,7.8675],[0.6115,7.753],[0.5905,7.703],[0.586,7.622],[0.524,7.594],[0.52,7.514],[0.536,7.4275],[0.5685,7.392],[0.644,7.401],[0.6585,7.3175],[0.634,7.207],[0.595,7.12],[0.6115,7.1055],[0.59,6.999],[0.5435,6.9885],[0.5265,6.941],[0.5655,6.923],[0.535,6.8755],[0.532,6.829],[0.5675,6.822],[0.5815,6.7615],[0.6485,6.74],[0.6355,6.6415],[0.657,6.6075],[0.7475,6.563],[0.7125,6.529],[0.7495,6.444],[0.789,6.4085],[0.857,6.384],[0.893,6.3335],[1.0035,6.3355],[1.008,6.294],[1.054,6.2345],[1.0835,6.169],[1.1995,6.169],[1.1995,6.1125],[1.2735,5.9265],[1.3375,5.9385],[1.417,5.9785],[1.673,6.04],[1.63,6.2355],[1.7765,6.2855],[1.7965,6.3205],[1.745,6.4735],[1.703,6.532],[1.61,6.6135],[1.6075,6.665],[1.58,6.6915],[1.6235,6.7375],[1.5945,6.7995],[1.6065,6.9045],[1.584,6.91],[1.559,6.997],[1.6425,6.9955],[1.6445,7.443],[1.656,7.5325],[1.6425,7.62],[1.638,7.8465],[1.6345,8.358],[1.613,8.369],[1.631,8.452],[1.661,8.497],[1.6245,8.548],[1.628,8.881],[1.617,8.9595],[1.6265,9.0075],[1.6045,9.1045],[1.57,9.166],[1.5105,9.211],[1.4315,9.3015],[1.407,9.3445],[1.393,9.479],[1.3705,9.4815],[1.3395,9.546],[1.3685,9.5965],[1.3635,9.826],[1.355,9.9955],[1.153,10.121],[1.098,10.15],[0.7765,10.3765],[0.7855,10.5245],[0.8075,10.607],[0.8005,10.681],[0.8095,10.728],[0.8795,10.7995],[0.8885,10.92],[0.912,10.996],[0.6565,10.9975],[0.4945,10.931],[0.5005,11.009],[0.039,11.101],[-0.136,11.1395]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/68/Flag_of_Togo.svg","name:en":"Togo","wikidata":"Q945","ISO3166-1:alpha2":"TG","ISO3166-1:alpha3":"TGO","ISO3166-1:numeric":"768"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[6.2605,0.2495],[6.2665,0.154],[6.318,-0.0595],[6.339,-0.1095],[6.399,-0.175],[6.4735,-0.2085],[6.514,-0.2135],[6.6045,-0.196],[6.714,-0.1415],[6.783,-0.0845],[6.935,0.1425],[7.2925,0.8185],[7.61,1.418],[7.659,1.588],[7.6695,1.666],[7.6585,1.754],[7.627,1.81],[7.58,1.8525],[7.461,1.9085],[7.358,1.9245],[7.272,1.8945],[7.211,1.8325],[6.7675,1.129],[6.323,0.424],[6.2835,0.3415],[6.2605,0.2495]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/4f/Flag_of_Sao_Tome_and_Principe.svg","name:en":"São Tomé and Príncipe","wikidata":"Q1039","ISO3166-1:alpha2":"ST","ISO3166-1:alpha3":"STP","ISO3166-1:numeric":"678"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[33.9335,-1.0],[33.9335,-0.903],[33.9255,-0.5605],[33.929,-0.4575],[33.9845,-0.1335],[33.91,0.099],[34.108,0.3695],[34.0885,0.4555],[34.1195,0.4835],[34.116,0.523],[34.138,0.583],[34.2005,0.6255],[34.2795,0.646],[34.314,0.698],[34.315,0.7615],[34.3615,0.774],[34.4465,0.864],[34.48,0.94],[34.5025,1.0705],[34.5265,1.107],[34.575,1.0985],[34.579,1.1495],[34.612,1.1595],[34.6685,1.2075],[34.795,1.223],[34.831,1.269],[34.829,1.3095],[34.787,1.365],[34.794,1.4135],[34.8445,1.4575],[34.864,1.5285],[34.9435,1.5765],[34.991,1.6645],[35.0005,1.7615],[35.0,1.9625],[34.9455,2.2115],[34.9165,2.4205],[34.9525,2.461],[34.913,2.5165],[34.8975,2.588],[34.852,2.5835],[34.7755,2.698],[34.7835,2.755],[34.767,2.811],[34.7365,2.855],[34.653,2.8675],[34.6395,2.9065],[34.599,2.9245],[34.5765,3.0205],[34.5725,3.0915],[34.546,3.1365],[34.456,3.182],[34.4475,3.283],[34.43,3.3425],[34.4005,3.3705],[34.4195,3.4335],[34.396,3.488],[34.4515,3.517],[34.463,3.6695],[34.405,3.6925],[34.3825,3.7275],[34.331,3.733],[34.308,3.7115],[34.2455,3.784],[34.2155,3.881],[34.128,3.872],[34.1345,3.9615],[34.059,4.0275],[34.0915,4.0615],[34.0495,4.121],[34.0475,4.179],[33.988,4.234],[33.5145,3.755],[33.496,3.7535],[33.179,3.7785],[33.027,3.8935],[32.901,3.8155],[32.72,3.768],[32.4155,3.7455],[32.197,3.6005],[32.2185,3.5605],[32.1935,3.5095],[32.09,3.535],[32.0535,3.5895],[31.9615,3.572],[31.9605,3.648],[31.865,3.789],[31.8275,3.8185],[31.7075,3.724],[31.5755,3.6825],[31.5455,3.6565],[31.3525,3.753],[31.29,3.796],[31.182,3.7945],[31.105,3.7505],[31.0695,3.743],[31.0225,3.6935],[30.9725,3.691],[30.949,3.633],[30.8505,3.5005],[30.942,3.506],[30.937,3.399],[30.8875,3.338],[30.8415,3.243],[30.8025,3.1365],[30.791,3.0635],[30.769,3.045],[30.8565,2.962],[30.888,2.8785],[30.884,2.814],[30.8265,2.729],[30.779,2.606],[30.753,2.591],[30.762,2.51],[30.7415,2.4485],[30.834,2.427],[30.882,2.341],[30.934,2.335],[30.9595,2.395],[30.9825,2.405],[31.0745,2.3435],[31.077,2.2955],[31.131,2.262],[31.203,2.2895],[31.203,2.2215],[31.3055,2.157],[31.3055,2.1165],[31.011,1.785],[30.5495,1.266],[30.5195,1.266],[30.4955,1.206],[30.391,1.1875],[30.36,1.2015],[30.336,1.1505],[30.29,1.1725],[30.235,1.125],[30.2285,0.9885],[30.1765,0.9445],[30.169,0.905],[30.1115,0.895],[29.989,0.8455],[29.9635,0.7785],[29.977,0.745],[29.9545,0.639],[29.973,0.597],[29.979,0.5155],[29.8725,0.388],[29.82,0.1655],[29.7895,0.1695],[29.7375,0.126],[29.7185,0.075],[29.7325,-0.0805],[29.7115,-0.22],[29.6745,-0.5385],[29.6255,-0.725],[29.641,-0.831],[29.631,-0.8905],[29.589,-0.9005],[29.591,-1.0535],[29.5735,-1.186],[29.607,-1.219],[29.595,-1.2805],[29.608,-1.318],[29.591,-1.3875],[29.6765,-1.3835],[29.7115,-1.345],[29.777,-1.3665],[29.823,-1.309],[29.882,-1.357],[29.8855,-1.4235],[29.916,-1.4825],[29.997,-1.446],[30.014,-1.416],[30.17,-1.345],[30.1695,-1.2745],[30.214,-1.269],[30.2925,-1.1885],[30.307,-1.145],[30.359,-1.0605],[30.473,-1.0575],[30.5405,-1.0745],[30.583,-1.061],[30.6585,-1.0625],[30.6985,-1.015],[30.763,-0.986],[30.7995,-1.0],[31.27,-1.0],[31.832,-0.9985],[31.999,-1.0],[32.7505,-1.0],[33.5195,-1.0],[33.9335,-1.0]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/4e/Flag_of_Uganda.svg","name:en":"Uganda","wikidata":"Q1036","ISO3166-1:alpha2":"UG","ISO3166-1:alpha3":"UGA","ISO3166-1:numeric":"800"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[33.988,4.234],[34.0565,4.282],[34.3875,4.61],[34.4765,4.724],[34.5035,4.7395],[34.7125,4.798],[35.009,4.9105],[35.152,4.988],[35.1965,4.956],[35.304,5.0205],[35.3465,5.008],[35.3105,4.9075],[35.4695,4.921],[35.477,4.903],[35.421,4.771],[35.5085,4.617],[35.608,4.618],[35.6985,4.6185],[35.944,4.619],[35.949,4.631],[35.818,4.774],[35.8185,5.1005],[35.866,5.184],[35.828,5.261],[35.8635,5.301],[35.84,5.336],[35.7705,5.351],[35.7205,5.388],[35.628,5.383],[35.5675,5.412],[35.505,5.4235],[35.4115,5.3645],[35.321,5.336],[35.2895,5.378],[35.3095,5.464],[35.306,5.5035],[35.126,5.6245],[35.13,5.6875],[35.0065,5.89],[34.993,5.945],[35.007,6.021],[35.0,6.0805],[34.964,6.1365],[34.9735,6.203],[34.95,6.2465],[35.0005,6.35],[34.9915,6.3935],[35.022,6.435],[35.009,6.475],[34.9275,6.556],[34.8515,6.6095],[34.765,6.6035],[34.7185,6.653],[34.7045,6.6915],[34.6245,6.743],[34.589,6.7285],[34.53,6.755],[34.5435,6.816],[34.5305,6.852],[34.4605,6.9195],[34.372,6.913],[34.311,6.9505],[34.2705,7.0025],[34.1945,7.0405],[34.19,7.1315],[34.105,7.17],[34.074,7.218],[34.024,7.2435],[34.04,7.272],[34.0365,7.3555],[34.004,7.4185],[33.8995,7.5125],[33.866,7.559],[33.7855,7.587],[33.724,7.6565],[33.6285,7.695],[33.5815,7.689],[33.5195,7.728],[33.437,7.7525],[33.323,7.7075],[33.2505,7.778],[33.1785,7.789],[33.071,7.7915],[33.0065,7.8565],[32.998,7.9455],[33.038,7.9675],[33.0395,8.009],[33.078,8.024],[33.0845,8.0735],[33.1185,8.107],[33.193,8.137],[33.171,8.2025],[33.173,8.301],[33.2045,8.3315],[33.187,8.383],[33.214,8.4275],[33.309,8.466],[33.3975,8.4265],[33.423,8.453],[33.561,8.473],[33.6195,8.4715],[33.6755,8.446],[33.689,8.3875],[33.7675,8.3675],[33.809,8.402],[33.8765,8.412],[33.876,8.4555],[33.9005,8.4835],[33.971,8.502],[34.0215,8.4885],[34.045,8.53],[34.1005,8.556],[34.1455,8.6075],[34.1455,9.033],[34.1065,9.5],[33.8775,9.5],[33.9045,9.73],[33.916,9.7655],[33.964,9.807],[33.99,9.9185],[33.9995,10.006],[33.995,10.0635],[33.9715,10.092],[33.9705,10.146],[33.9035,10.1715],[33.8035,10.3355],[33.6685,10.442],[33.517,10.6425],[33.246,10.7775],[33.271,10.828],[33.203,11.2325],[33.1385,11.437],[33.1805,11.75],[33.2535,12.1495],[33.2945,12.219],[32.7315,12.2365],[32.739,12.1395],[32.7595,12.014],[32.7455,11.951],[32.0965,11.951],[32.391,11.706],[32.391,11.181],[32.4695,11.0435],[32.412,11.0],[32.149,10.7375],[31.9885,10.6505],[31.8395,10.379],[31.7785,10.289],[31.734,10.2535],[31.365,9.816],[31.282,9.7595],[31.18,9.75],[30.838,9.75],[30.8345,9.7115],[30.53,9.9605],[30.0,10.288],[29.941,10.288],[29.5375,10.082],[29.5375,9.75],[29.1175,9.75],[29.0465,9.7365],[29.0005,9.67],[28.8165,9.4985],[28.768,9.419],[28.768,9.352],[28.036,9.344],[27.907,9.61],[27.75,9.6065],[27.5,9.6235],[27.2505,9.6285],[27.1425,9.6265],[27.0235,9.603],[26.75,9.4965],[26.703,9.489],[26.548,9.522],[26.394,9.564],[26.346,9.594],[26.3035,9.659],[26.214,9.9165],[26.1165,10.0],[26.038,10.125],[25.988,10.162],[25.932,10.179],[25.9335,10.374],[25.835,10.4225],[25.712,10.4195],[25.6915,10.403],[25.5465,10.398],[25.4715,10.373],[25.4185,10.3915],[25.377,10.372],[25.2775,10.3515],[25.211,10.358],[25.183,10.3225],[25.0985,10.317],[25.062,10.267],[25.0585,10.209],[25.031,10.167],[25.0735,10.1275],[25.079,10.09],[25.0345,10.06],[25.048,10.022],[24.9845,9.9565],[24.9705,9.9045],[24.8375,9.81],[24.828,9.785],[24.7365,9.7175],[24.7515,9.689],[24.752,9.5905],[24.7305,9.5755],[24.697,9.4915],[24.6645,9.4595],[24.6655,9.419],[24.624,9.387],[24.612,9.328],[24.5555,9.2355],[24.5685,9.1475],[24.541,9.0985],[24.549,8.9865],[24.5775,8.941],[24.532,8.8755],[24.4485,8.8445],[24.459,8.814],[24.41,8.7985],[24.386,8.729],[24.319,8.7255],[24.256,8.687],[24.229,8.6305],[24.268,8.592],[24.1635,8.474],[24.141,8.3765],[24.163,8.3315],[24.2305,8.285],[24.3335,8.2665],[24.4705,8.2685],[24.483,8.2345],[24.539,8.2055],[24.6345,8.221],[24.7225,8.2105],[24.7625,8.1865],[24.8,8.1955],[24.8585,8.1695],[24.8805,8.0935],[24.9125,8.0315],[24.969,7.977],[25.047,7.9445],[25.0875,7.8955],[25.1775,7.9105],[25.2465,7.846],[25.282,7.7835],[25.2895,7.652],[25.2305,7.6335],[25.1725,7.5745],[25.1975,7.495],[25.287,7.47],[25.3405,7.426],[25.369,7.3445],[25.4055,7.341],[25.484,7.27],[25.536,7.278],[25.57,7.238],[25.6535,7.205],[25.8205,7.156],[25.841,7.111],[25.895,7.099],[25.9385,7.0405],[26.0395,7.003],[26.0585,6.957],[26.0785,6.85],[26.109,6.8165],[26.175,6.8125],[26.184,6.757],[26.2485,6.739],[26.2655,6.6965],[26.31,6.7105],[26.345,6.6815],[26.415,6.6535],[26.403,6.6175],[26.32,6.5125],[26.3,6.3965],[26.3635,6.352],[26.3965,6.31],[26.489,6.2795],[26.4695,6.2235],[26.5305,6.224],[26.514,6.1155],[26.569,6.028],[26.5965,6.032],[26.6615,5.997],[26.7025,6.0175],[26.7825,6.0005],[26.829,5.9015],[26.9195,5.904],[26.9335,5.865],[26.9875,5.8645],[27.0685,5.8005],[27.103,5.8105],[27.2005,5.744],[27.244,5.651],[27.2375,5.606],[27.2815,5.601],[27.287,5.54],[27.2635,5.5185],[27.2345,5.429],[27.254,5.3235],[27.3035,5.28],[27.318,5.2175],[27.4135,5.148],[27.4355,5.099],[27.466,5.0855],[27.449,5.0195],[27.512,4.9615],[27.5065,4.9395],[27.559,4.892],[27.6485,4.896],[27.707,4.817],[27.7715,4.7915],[27.7825,4.7275],[27.766,4.69],[27.7925,4.6065],[27.842,4.5895],[27.886,4.5425],[27.9225,4.5355],[27.9435,4.5735],[28.0175,4.555],[28.0415,4.531],[28.0375,4.4795],[28.089,4.4345],[28.15,4.426],[28.2065,4.352],[28.3015,4.361],[28.356,4.353],[28.3825,4.2855],[28.4645,4.295],[28.5175,4.3795],[28.584,4.384],[28.6015,4.4185],[28.668,4.4285],[28.6955,4.5025],[28.7305,4.545],[28.8025,4.5565],[28.8095,4.5005],[28.91,4.476],[29.035,4.4915],[29.08,4.4345],[29.138,4.429],[29.2215,4.341],[29.25,4.352],[29.269,4.3975],[29.347,4.3975],[29.3695,4.471],[29.404,4.473],[29.446,4.52],[29.4485,4.5665],[29.4745,4.5925],[29.473,4.6485],[29.4905,4.6945],[29.62,4.6595],[29.7495,4.5695],[29.826,4.56],[29.797,4.4375],[29.8,4.373],[29.838,4.3375],[29.903,4.3475],[29.9655,4.2955],[29.9445,4.248],[29.958,4.224],[30.028,4.2055],[30.0595,4.133],[30.1225,4.112],[30.1635,4.1155],[30.161,4.049],[30.195,4.024],[30.2115,3.94],[30.277,3.9565],[30.3465,3.924],[30.411,3.8655],[30.47,3.8315],[30.5485,3.844],[30.578,3.754],[30.574,3.671],[30.5565,3.6275],[30.6195,3.6055],[30.688,3.643],[30.7275,3.6235],[30.777,3.681],[30.798,3.6555],[30.8045,3.5955],[30.8505,3.5825],[30.864,3.5475],[30.8505,3.5005],[30.949,3.633],[30.9725,3.691],[31.0225,3.6935],[31.0695,3.743],[31.105,3.7505],[31.182,3.7945],[31.29,3.796],[31.3525,3.753],[31.5455,3.6565],[31.5755,3.6825],[31.7075,3.724],[31.8275,3.8185],[31.865,3.789],[31.9605,3.648],[31.9615,3.572],[32.0535,3.5895],[32.09,3.535],[32.1935,3.5095],[32.2185,3.5605],[32.197,3.6005],[32.4155,3.7455],[32.72,3.768],[32.901,3.8155],[33.027,3.8935],[33.179,3.7785],[33.496,3.7535],[33.5145,3.755],[33.988,4.234]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/7a/Flag_of_South_Sudan.svg","name:en":"South Sudan","wikidata":"Q958","ISO3166-1:alpha2":"SS","ISO3166-1:alpha3":"SSD","ISO3166-1:numeric":"728"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[34.1065,9.5],[34.086,9.553],[34.14,9.758],[34.207,9.905],[34.2315,10.0305],[34.323,10.117],[34.3215,10.164],[34.348,10.2465],[34.293,10.522],[34.3015,10.5715],[34.357,10.6385],[34.4395,10.7845],[34.5945,10.888],[34.674,10.8345],[34.733,10.7695],[34.798,10.7195],[34.8525,10.7265],[34.8685,10.786],[34.9755,10.8645],[34.9785,10.9155],[34.9335,10.9565],[35.0055,11.1745],[35.0075,11.1985],[34.9405,11.249],[35.0045,11.349],[35.0875,11.536],[35.096,11.583],[35.064,11.6545],[35.0585,11.74],[35.081,11.801],[35.1345,11.864],[35.227,11.8955],[35.269,11.9385],[35.271,11.9755],[35.347,12.0385],[35.3465,12.0835],[35.386,12.17],[35.437,12.21],[35.438,12.2505],[35.649,12.594],[35.6995,12.622],[35.6995,12.666],[36.0065,12.724],[36.081,12.7235],[36.1405,12.704],[36.1655,12.8755],[36.133,12.9235],[36.1695,12.984],[36.1555,13.0245],[36.2485,13.368],[36.3975,13.568],[36.4075,13.652],[36.487,13.8395],[36.4465,13.957],[36.5285,14.212],[36.5605,14.2575],[36.5415,14.278],[36.5235,14.3645],[36.4605,14.983],[36.4335,15.1675],[36.5485,15.252],[36.6105,15.437],[36.698,15.753],[36.7635,15.808],[36.9205,16.2245],[36.9655,16.256],[36.9695,16.3055],[36.9505,16.3475],[36.9575,16.419],[36.906,16.4865],[36.8985,16.537],[36.9195,16.593],[36.9165,16.6505],[36.9805,16.706],[37.009,16.7825],[37.0135,16.9135],[36.9885,16.9375],[37.026,17.077],[37.099,17.0525],[37.158,17.0155],[37.2035,17.0325],[37.252,17.025],[37.307,17.061],[37.396,17.0545],[37.502,17.197],[37.502,17.3165],[37.5835,17.3485],[37.7445,17.382],[37.791,17.4685],[37.831,17.483],[37.8955,17.4415],[37.934,17.4585],[37.9685,17.5015],[38.046,17.548],[38.087,17.5495],[38.0905,17.502],[38.137,17.504],[38.1335,17.5485],[38.19,17.559],[38.2225,17.529],[38.262,17.5355],[38.2735,17.603],[38.3445,17.6445],[38.452,17.8995],[38.5825,18.0205],[38.788,18.071],[38.749,18.1455],[38.8255,18.162],[38.888,18.2],[38.9305,18.252],[38.9555,18.3385],[39.021,18.4075],[39.041,18.457],[39.0415,18.548],[39.0575,18.6275],[39.039,18.707],[38.9895,18.777],[38.9745,18.879],[38.9285,18.956],[38.8805,19.0005],[38.828,19.026],[38.752,19.0365],[38.679,19.0865],[38.5825,19.1035],[38.495,19.0815],[38.451,19.053],[38.422,19.081],[38.394,19.1595],[38.4015,19.2455],[38.371,19.3245],[38.3285,19.371],[38.273,19.403],[38.1875,19.419],[38.123,19.4095],[38.109,19.4625],[38.0625,19.527],[37.9935,19.57],[37.9115,19.5845],[37.8305,19.569],[37.7485,19.511],[37.7125,19.45],[37.7005,19.3895],[37.6285,19.313],[37.6045,19.216],[37.5445,19.2445],[37.525,19.412],[37.496,19.544],[37.46,19.614],[37.473,19.659],[37.4815,19.848],[37.4665,19.9175],[37.4175,20.0075],[37.43,20.0775],[37.4265,20.193],[37.41,20.3375],[37.4505,20.447],[37.456,20.6165],[37.5175,20.6755],[37.5495,20.7505],[37.551,20.817],[37.5215,20.8945],[37.49,20.9335],[37.528,21.0195],[37.5315,21.0935],[37.508,21.161],[37.4555,21.238],[37.3475,21.3615],[37.3055,21.391],[37.238,21.4725],[37.189,21.547],[37.116,21.71],[37.0945,21.886],[37.0905,22.0],[36.737,21.999],[36.6195,22.0055],[36.485,22.0],[36.0995,22.0],[35.556,22.0],[35.1815,22.0],[34.475,21.9995],[34.3535,21.996],[34.3,22.0055],[34.215,21.9975],[34.08,22.0],[34.0,21.7665],[33.553,21.725],[33.1665,22.0],[32.5425,22.0],[32.0,22.0],[31.399,22.0],[31.43,22.078],[31.506,22.185],[31.47,22.225],[31.3765,22.148],[31.3385,22.081],[31.315,22.0],[31.0,22.0],[30.4155,22.0],[30.0,22.0],[29.5745,22.0],[29.024,22.0],[28.316,22.0],[27.7535,22.0],[27.242,22.0],[26.643,22.0],[26.2895,22.0],[25.527,22.0],[25.0,22.0],[25.004,21.943],[25.0035,21.6245],[25.002,21.086],[25.0015,20.479],[25.0,20.0],[24.0,20.0],[24.0,19.5],[23.999,19.066],[23.999,18.5325],[23.9995,18.033],[23.999,17.6995],[23.999,17.0995],[24.0,16.733],[23.999,16.1995],[23.9995,15.6975],[23.8115,15.7515],[23.6635,15.7745],[23.552,15.7685],[23.397,15.6975],[23.3375,15.694],[23.207,15.7185],[23.1105,15.715],[23.0755,15.6675],[22.9875,15.5835],[22.937,15.5645],[22.9185,15.4945],[22.9305,15.462],[22.988,15.415],[23.003,15.322],[22.985,15.2595],[23.004,15.2405],[22.9675,15.1825],[22.935,15.169],[22.9405,15.116],[22.882,15.0885],[22.8125,15.0345],[22.7515,14.97],[22.76,14.908],[22.7095,14.8955],[22.667,14.8635],[22.6775,14.766],[22.7025,14.6915],[22.47,14.6295],[22.4035,14.592],[22.3845,14.5195],[22.4345,14.496],[22.4475,14.384],[22.4695,14.351],[22.436,14.315],[22.444,14.272],[22.4785,14.2445],[22.5555,14.232],[22.567,14.164],[22.5525,14.121],[22.477,14.101],[22.4345,14.0515],[22.3965,14.052],[22.3445,14.0135],[22.234,13.966],[22.0845,13.779],[22.1405,13.723],[22.134,13.6695],[22.1635,13.6235],[22.2265,13.567],[22.2395,13.454],[22.2885,13.3915],[22.2695,13.321],[22.183,13.231],[22.1595,13.1905],[22.0755,13.151],[22.0295,13.143],[21.941,13.049],[21.91,12.99],[21.847,12.838],[21.8145,12.8065],[21.858,12.772],[21.8565,12.741],[21.9015,12.6775],[21.977,12.6385],[22.0745,12.64],[22.166,12.672],[22.2235,12.747],[22.335,12.6705],[22.467,12.6215],[22.409,12.487],[22.3865,12.4545],[22.433,12.4035],[22.4365,12.3545],[22.5015,12.177],[22.478,12.03],[22.5375,12.058],[22.641,12.071],[22.6145,12.0045],[22.573,11.799],[22.5535,11.6725],[22.5615,11.6215],[22.642,11.516],[22.7755,11.462],[22.791,11.4015],[22.931,11.416],[22.9485,11.316],[22.972,11.2775],[22.9755,11.216],[22.9195,11.0665],[22.878,10.9205],[22.8905,10.8825],[23.013,10.6955],[23.151,10.6005],[23.252,10.493],[23.3065,10.459],[23.6695,9.867],[23.6965,9.6715],[23.6265,9.547],[23.632,9.4495],[23.666,9.436],[23.6405,9.346],[23.65,9.2755],[23.5515,9.1805],[23.4935,9.174],[23.4715,9.1245],[23.4575,8.9905],[23.5335,8.9605],[23.583,8.9945],[23.5805,8.9015],[23.5495,8.847],[23.509,8.812],[23.4975,8.773],[23.5265,8.7085],[23.5585,8.705],[23.597,8.7345],[23.734,8.705],[23.8305,8.727],[23.8835,8.708],[23.926,8.7185],[24.127,8.6855],[24.1685,8.6985],[24.256,8.687],[24.319,8.7255],[24.386,8.729],[24.41,8.7985],[24.459,8.814],[24.4485,8.8445],[24.532,8.8755],[24.5775,8.941],[24.549,8.9865],[24.541,9.0985],[24.5685,9.1475],[24.5555,9.2355],[24.612,9.328],[24.624,9.387],[24.6655,9.419],[24.6645,9.4595],[24.697,9.4915],[24.7305,9.5755],[24.752,9.5905],[24.7515,9.689],[24.7365,9.7175],[24.828,9.785],[24.8375,9.81],[24.9705,9.9045],[24.9845,9.9565],[25.048,10.022],[25.0345,10.06],[25.079,10.09],[25.0735,10.1275],[25.031,10.167],[25.0585,10.209],[25.062,10.267],[25.0985,10.317],[25.183,10.3225],[25.211,10.358],[25.2775,10.3515],[25.377,10.372],[25.4185,10.3915],[25.4715,10.373],[25.5465,10.398],[25.6915,10.403],[25.712,10.4195],[25.835,10.4225],[25.9335,10.374],[25.932,10.179],[25.988,10.162],[26.038,10.125],[26.1165,10.0],[26.214,9.9165],[26.3035,9.659],[26.346,9.594],[26.394,9.564],[26.548,9.522],[26.703,9.489],[26.75,9.4965],[27.0235,9.603],[27.1425,9.6265],[27.2505,9.6285],[27.5,9.6235],[27.75,9.6065],[27.907,9.61],[28.036,9.344],[28.768,9.352],[28.768,9.419],[28.8165,9.4985],[29.0005,9.67],[29.0465,9.7365],[29.1175,9.75],[29.5375,9.75],[29.5375,10.082],[29.941,10.288],[30.0,10.288],[30.53,9.9605],[30.8345,9.7115],[30.838,9.75],[31.18,9.75],[31.282,9.7595],[31.365,9.816],[31.734,10.2535],[31.7785,10.289],[31.8395,10.379],[31.9885,10.6505],[32.149,10.7375],[32.412,11.0],[32.4695,11.0435],[32.391,11.181],[32.391,11.706],[32.0965,11.951],[32.7455,11.951],[32.7595,12.014],[32.739,12.1395],[32.7315,12.2365],[33.2945,12.219],[33.2535,12.1495],[33.1805,11.75],[33.1385,11.437],[33.203,11.2325],[33.271,10.828],[33.246,10.7775],[33.517,10.6425],[33.6685,10.442],[33.8035,10.3355],[33.9035,10.1715],[33.9705,10.146],[33.9715,10.092],[33.995,10.0635],[33.9995,10.006],[33.99,9.9185],[33.964,9.807],[33.916,9.7655],[33.9045,9.73],[33.8775,9.5],[34.1065,9.5]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/01/Flag_of_Sudan.svg","name:en":"Sudan","wikidata":"Q1049","ISO3166-1:alpha2":"SD","ISO3166-1:alpha3":"SDN","ISO3166-1:numeric":"729"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[8.5905,37.143],[8.642,36.941],[8.6295,36.8605],[8.6695,36.8175],[8.5725,36.773],[8.484,36.7795],[8.4595,36.7515],[8.4875,36.6975],[8.447,36.6235],[8.418,36.6225],[8.326,36.574],[8.286,36.538],[8.1615,36.4985],[8.176,36.4595],[8.4045,36.4235],[8.372,36.298],[8.3395,36.267],[8.355,36.2335],[8.3095,36.1615],[8.346,36.097],[8.2975,36.0415],[8.291,35.957],[8.2715,35.9075],[8.2645,35.7305],[8.356,35.67],[8.36,35.5255],[8.3535,35.463],[8.3145,35.3775],[8.3125,35.3125],[8.4545,35.22],[8.4,35.128],[8.3615,35.103],[8.3155,35.009],[8.3075,34.947],[8.253,34.9155],[8.2995,34.7415],[8.25,34.664],[8.244,34.6205],[8.206,34.5775],[8.1615,34.569],[8.1155,34.5125],[8.043,34.4935],[8.011,34.458],[7.959,34.4485],[7.9285,34.4185],[7.8565,34.4055],[7.845,34.329],[7.7965,34.209],[7.723,34.1705],[7.6515,34.201],[7.614,34.1135],[7.5305,34.061],[7.5605,34.0145],[7.522,33.905],[7.547,33.837],[7.5255,33.7985],[7.562,33.7775],[7.565,33.727],[7.634,33.572],[7.7365,33.4245],[7.749,33.3275],[7.786,33.2905],[7.832,33.188],[7.9015,33.1855],[8.1175,33.1],[8.116,33.05],[8.247,32.9245],[8.3225,32.8235],[8.357,32.5025],[8.8005,32.236],[9.0655,32.0875],[9.0985,31.9995],[9.187,31.676],[9.3595,31.0],[9.474,30.559],[9.559,30.229],[9.832,30.337],[9.8775,30.337],[9.944,30.3835],[10.017,30.479],[10.0695,30.5085],[10.202,30.6935],[10.272,30.8005],[10.2975,30.8865],[10.275,31.041],[10.294,31.0915],[10.1665,31.377],[10.139,31.49],[10.2125,31.566],[10.312,31.703],[10.386,31.7405],[10.4485,31.7305],[10.527,31.741],[10.635,31.8795],[10.6125,31.9165],[10.658,31.9775],[10.7225,31.9675],[10.795,31.998],[10.861,32.0855],[10.8615,32.1055],[11.029,32.1995],[11.3845,32.345],[11.549,32.4085],[11.595,32.4545],[11.6,32.519],[11.5515,32.581],[11.5015,32.612],[11.478,32.656],[11.494,32.694],[11.4875,32.7625],[11.5015,32.8305],[11.4955,32.918],[11.5275,32.986],[11.527,33.0785],[11.5615,33.167],[11.664,33.349],[11.554,33.3795],[11.461,33.435],[11.3695,33.4755],[11.336,33.6185],[11.2905,33.69],[11.3025,33.7955],[11.2935,33.8595],[11.234,33.9605],[11.16,34.0065],[11.2725,34.4445],[11.38,34.4775],[11.531,34.502],[11.6745,34.555],[11.769,34.6335],[11.865,34.7885],[11.88,34.854],[11.868,34.92],[11.8305,34.979],[11.7475,35.047],[11.7005,35.118],[11.6415,35.1765],[11.4005,35.2885],[11.3555,35.359],[11.294,35.4025],[11.319,35.4555],[11.3265,35.524],[11.294,35.609],[11.2695,35.713],[11.2915,35.8125],[11.252,35.9085],[11.1485,35.985],[11.0145,36.005],[10.911,35.979],[10.817,35.989],[10.762,36.0535],[10.7145,36.148],[10.728,36.2135],[10.8065,36.248],[10.9085,36.272],[10.98,36.312],[11.026,36.3595],[11.0935,36.4875],[11.206,36.6465],[11.3085,36.7055],[11.356,36.7685],[11.3785,36.826],[11.384,36.9],[11.3455,36.9835],[11.3085,37.123],[11.214,37.232],[11.13,37.274],[10.8325,37.342],[10.378,37.38],[10.351,37.4525],[10.2995,37.5055],[10.229,37.542],[10.147,37.558],[10.043,37.5465],[9.97,37.515],[9.9225,37.5295],[9.7305,37.5475],[9.5985,37.533],[9.482,37.496],[9.3635,37.502],[9.2815,37.4795],[9.2155,37.4405],[9.204,37.602],[9.1685,37.6685],[9.1025,37.723],[9.011,37.756],[8.921,37.7595],[8.802,37.7205],[8.668,37.6185],[8.63,37.5605],[8.6175,37.4895],[8.636,37.4215],[8.6895,37.356],[8.7495,37.3205],[8.869,37.293],[8.797,37.242],[8.7335,37.17],[8.5905,37.143]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/ce/Flag_of_Tunisia.svg","name:en":"Tunisia","wikidata":"Q948","ISO3166-1:alpha2":"TN","ISO3166-1:alpha3":"TUN","ISO3166-1:numeric":"788"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.692,36.034],[35.6175,35.9875],[35.5705,35.926],[35.556,35.8275],[35.568,35.787],[35.5,35.68],[35.4745,35.617],[35.489,35.51],[35.556,35.3985],[35.6755,35.315],[35.686,35.2205],[35.649,35.1455],[35.638,35.0465],[35.611,34.9105],[35.6195,34.8105],[35.6745,34.686],[35.754,34.662],[35.8395,34.6005],[36.0055,34.642],[36.036,34.6285],[36.1895,34.6375],[36.2975,34.6355],[36.3115,34.683],[36.4095,34.6115],[36.3805,34.508],[36.4435,34.5055],[36.4635,34.4645],[36.572,34.405],[36.5285,34.37],[36.5805,34.2775],[36.595,34.1895],[36.564,34.1345],[36.476,34.0505],[36.4405,34.0585],[36.328,33.978],[36.2835,33.918],[36.356,33.8815],[36.3965,33.8335],[36.323,33.833],[36.246,33.8595],[36.202,33.833],[36.1525,33.8555],[36.067,33.8245],[35.9685,33.715],[35.946,33.6375],[36.019,33.6135],[36.059,33.5795],[36.029,33.549],[35.952,33.534],[35.9535,33.488],[35.8765,33.426],[35.827,33.405],[35.7735,33.3355],[35.813,33.317],[35.7775,33.2765],[35.815,33.245],[35.8445,33.1675],[35.817,33.113],[35.8505,33.1025],[35.8715,32.9815],[35.895,32.945],[35.851,32.89],[35.8375,32.828],[35.7565,32.745],[35.7865,32.7475],[35.882,32.7145],[35.936,32.718],[35.967,32.6625],[36.027,32.6535],[36.0295,32.597],[36.0625,32.576],[36.0785,32.5135],[36.1225,32.5245],[36.2065,32.5195],[36.3055,32.4595],[36.407,32.377],[36.512,32.357],[36.6895,32.3345],[36.71,32.3165],[36.838,32.3115],[36.881,32.334],[36.9625,32.44],[37.065,32.4285],[37.105,32.4545],[37.2065,32.5575],[37.2555,32.572],[37.36,32.56],[37.4245,32.607],[37.444,32.643],[37.5205,32.7175],[37.741,32.762],[37.762,32.788],[37.7695,32.8785],[37.818,32.9055],[37.8885,32.894],[38.3505,33.1415],[38.7695,33.358],[38.7935,33.375],[39.3045,33.64],[39.9825,33.985],[40.64,34.315],[40.978,34.398],[41.0165,34.448],[41.1225,34.6535],[41.2285,34.7785],[41.2275,34.974],[41.218,35.132],[41.2425,35.2855],[41.275,35.385],[41.262,35.456],[41.382,35.6255],[41.3695,35.8435],[41.256,36.06],[41.294,36.365],[41.402,36.524],[41.817,36.5875],[41.9705,36.7005],[42.0,36.7385],[42.3745,37.073],[42.3515,37.1055],[42.3205,37.1855],[42.3435,37.23],[42.2345,37.283],[42.1835,37.2865],[42.035,37.182],[41.794,37.1285],[41.5975,37.1055],[41.5175,37.078],[41.2925,37.081],[41.218,37.062],[41.1635,37.0945],[41.117,37.0945],[40.9165,37.1315],[40.7625,37.1255],[40.695,37.1035],[40.528,37.028],[40.419,37.0165],[40.379,36.9795],[40.181,36.877],[40.0595,36.8545],[40.0275,36.826],[39.854,36.7765],[39.8165,36.756],[39.7105,36.75],[39.6385,36.7245],[39.491,36.7005],[39.361,36.692],[39.2095,36.6695],[39.019,36.711],[38.8955,36.701],[38.7475,36.707],[38.689,36.7365],[38.5525,36.8455],[38.3925,36.902],[38.3455,36.8995],[38.243,36.924],[38.0365,36.8605],[38.0195,36.8265],[37.9405,36.8185],[37.902,36.791],[37.7775,36.749],[37.7005,36.7575],[37.5245,36.6875],[37.471,36.635],[37.284,36.67],[37.217,36.6755],[37.1625,36.657],[37.106,36.6715],[37.0855,36.6345],[37.0205,36.666],[37.037,36.7365],[36.985,36.762],[36.7935,36.797],[36.752,36.8195],[36.685,36.8255],[36.613,36.744],[36.6315,36.7125],[36.5735,36.659],[36.5915,36.5855],[36.544,36.502],[36.5995,36.3685],[36.6575,36.3375],[36.6565,36.298],[36.695,36.245],[36.6245,36.2145],[36.534,36.2455],[36.373,36.1785],[36.371,36.1175],[36.3905,36.082],[36.376,36.0115],[36.299,35.963],[36.2085,35.9535],[36.176,35.9215],[36.1735,35.8355],[36.113,35.862],[35.9985,35.884],[36.018,35.921],[35.976,35.939],[35.9115,35.9335],[35.692,36.034]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/5/53/Flag_of_Syria.svg","name:en":"Syria","wikidata":"Q858","ISO3166-1:alpha2":"SY","ISO3166-1:alpha3":"SYR","ISO3166-1:numeric":"760"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[42.3515,37.1055],[42.464,37.142],[42.579,37.146],[42.6175,37.2185],[42.73,37.3175],[42.78,37.3755],[42.8245,37.371],[42.8975,37.3285],[42.9635,37.3155],[43.072,37.3665],[43.151,37.371],[43.2405,37.345],[43.3015,37.3035],[43.3275,37.326],[43.4745,37.253],[43.578,37.25],[43.6205,37.2215],[43.694,37.2355],[43.9165,37.2235],[44.0145,37.322],[44.1245,37.323],[44.2685,37.2445],[44.2765,37.165],[44.222,37.158],[44.185,37.098],[44.235,37.042],[44.2675,36.976],[44.3065,36.97],[44.392,37.0505],[44.5115,37.101],[44.576,37.1625],[44.627,37.19],[44.682,37.171],[44.769,37.164],[44.7845,37.144],[44.7575,37.2285],[44.815,37.2795],[44.798,37.311],[44.743,37.3345],[44.711,37.38],[44.6575,37.386],[44.619,37.437],[44.5875,37.4395],[44.6145,37.6005],[44.568,37.645],[44.6245,37.698],[44.588,37.7635],[44.5305,37.783],[44.445,37.771],[44.392,37.828],[44.403,37.852],[44.317,37.8765],[44.268,37.867],[44.2225,37.888],[44.2405,37.9555],[44.3015,38.0325],[44.351,38.135],[44.3925,38.1465],[44.402,38.251],[44.478,38.3025],[44.4975,38.3335],[44.4395,38.3835],[44.3765,38.3595],[44.3165,38.3745],[44.3055,38.441],[44.3255,38.502],[44.3095,38.531],[44.3215,38.6245],[44.2815,38.6415],[44.261,38.7055],[44.315,38.809],[44.2965,38.841],[44.2115,38.89],[44.1895,38.934],[44.181,39.0105],[44.2115,39.0205],[44.191,39.0765],[44.2135,39.1305],[44.1665,39.1775],[44.094,39.2155],[44.0755,39.332],[44.0375,39.3615],[44.051,39.403],[44.1415,39.3975],[44.233,39.4155],[44.295,39.3755],[44.432,39.443],[44.4395,39.501],[44.416,39.5565],[44.48,39.6105],[44.467,39.684],[44.6145,39.7825],[44.669,39.7145],[44.7135,39.7145],[44.8095,39.6275],[44.7635,39.7145],[44.711,39.77],[44.652,39.7955],[44.554,39.9005],[44.4805,39.967],[44.351,40.0275],[44.2745,40.0495],[44.1705,40.0275],[43.994,40.032],[43.8965,40.0215],[43.761,40.084],[43.6785,40.096],[43.6485,40.132],[43.714,40.1615],[43.6765,40.2605],[43.591,40.346],[43.62,40.398],[43.6055,40.438],[43.56,40.459],[43.558,40.4945],[43.611,40.514],[43.6865,40.593],[43.7485,40.731],[43.707,40.82],[43.6785,40.8445],[43.6715,40.9365],[43.5975,40.9835],[43.4685,41.0305],[43.447,41.099],[43.474,41.123],[43.4485,41.1765],[43.3675,41.2035],[43.247,41.177],[43.1925,41.253],[43.2125,41.2905],[43.132,41.321],[43.109,41.3495],[43.0215,41.3785],[42.967,41.4515],[42.8995,41.479],[42.8015,41.497],[42.8365,41.585],[42.7745,41.5785],[42.684,41.6],[42.601,41.585],[42.573,41.51],[42.513,41.472],[42.5195,41.4365],[42.439,41.4415],[42.2745,41.494],[42.203,41.492],[42.085,41.51],[42.043,41.494],[41.9555,41.5235],[41.8815,41.4605],[41.824,41.4315],[41.747,41.4715],[41.6355,41.4865],[41.2995,41.612],[41.231,41.554],[41.122,41.51],[40.972,41.429],[40.9195,41.391],[40.7385,41.359],[40.641,41.32],[40.553,41.255],[40.5145,41.241],[40.3385,41.222],[40.2175,41.183],[40.1515,41.14],[40.0195,41.163],[39.9525,41.162],[39.8545,41.195],[39.7065,41.211],[39.6115,41.268],[39.449,41.305],[39.3905,41.305],[39.2615,41.27],[39.136,41.278],[39.0015,41.238],[38.9065,41.242],[38.719,41.197],[38.657,41.173],[38.512,41.154],[38.4585,41.13],[38.2295,41.144],[38.0395,41.178],[37.9945,41.241],[37.874,41.305],[37.7095,41.334],[37.591,41.323],[37.5075,41.286],[37.3455,41.34],[37.2635,41.346],[37.2295,41.391],[37.154,41.446],[37.025,41.504],[36.917,41.539],[36.6845,41.583],[36.5475,41.568],[36.4325,41.508],[36.399,41.572],[36.368,41.69],[36.292,41.788],[36.218,41.846],[36.081,41.914],[35.9335,41.934],[35.8545,41.92],[35.7365,41.877],[35.5915,41.837],[35.4535,41.882],[35.436,41.907],[35.471,41.966],[35.4785,42.042],[35.4495,42.111],[35.357,42.198],[35.2975,42.224],[35.198,42.241],[35.1125,42.28],[34.9175,42.296],[34.8355,42.28],[34.718,42.206],[34.682,42.146],[34.5625,42.169],[34.4355,42.172],[34.289,42.1495],[34.165,42.173],[34.029,42.182],[33.8395,42.175],[33.632,42.188],[33.3855,42.218],[33.284,42.215],[33.2115,42.198],[33.049,42.141],[32.9105,42.107],[32.813,42.062],[32.7225,42.055],[32.4845,42.013],[32.3875,41.983],[32.3165,41.947],[32.1705,41.901],[32.0535,41.842],[31.976,41.775],[31.8475,41.721],[31.756,41.694],[31.5575,41.589],[31.388,41.535],[31.2405,41.475],[31.151,41.389],[31.127,41.297],[31.009,41.276],[30.838,41.281],[30.771,41.31],[30.5115,41.351],[30.3435,41.408],[30.2315,41.416],[30.1475,41.399],[30.047,41.339],[29.882,41.343],[29.6385,41.3805],[29.573,41.3795],[29.472,41.4045],[29.3135,41.4305],[29.201,41.4325],[29.028,41.4565],[28.7795,41.5355],[28.7195,41.5465],[28.4345,41.659],[28.3655,41.717],[28.3055,41.808],[28.3205,41.846],[28.324,41.9815],[28.0405,41.9815],[27.9075,41.975],[27.8685,41.9995],[27.778,41.966],[27.714,41.977],[27.5745,41.9375],[27.447,41.967],[27.393,42.007],[27.3765,42.046],[27.3125,42.0865],[27.2215,42.098],[27.1915,42.0605],[27.107,42.086],[27.022,42.079],[27.0145,42.045],[26.9575,41.997],[26.871,41.991],[26.833,41.969],[26.787,41.9895],[26.755,41.96],[26.641,41.972],[26.561,41.9255],[26.584,41.902],[26.5385,41.8235],[26.369,41.821],[26.3295,41.752],[26.358,41.711],[26.471,41.6695],[26.5295,41.6215],[26.595,41.61],[26.591,41.5285],[26.6355,41.3835],[26.5865,41.3225],[26.498,41.3285],[26.4605,41.282],[26.4075,41.254],[26.323,41.2535],[26.3075,41.172],[26.332,41.097],[26.3105,41.0805],[26.3665,41.018],[26.328,40.977],[26.302,40.9025],[26.248,40.8815],[26.158,40.809],[26.125,40.7425],[26.083,40.73],[25.955,40.7295],[26.05,40.3715],[25.6935,40.2335],[25.643,40.199],[25.6225,40.129],[25.6985,39.992],[25.732,39.9575],[25.8915,39.8905],[25.8925,39.8415],[25.928,39.7145],[25.9275,39.48],[25.9815,39.396],[26.143,39.4055],[26.432,39.434],[26.674,39.122],[26.7045,39.032],[26.6995,38.98],[26.6465,38.9095],[26.6185,38.809],[26.451,38.7725],[26.324,38.756],[26.2665,38.736],[26.2225,38.688],[26.216,38.6515],[26.3125,38.537],[26.3155,38.453],[26.243,38.446],[26.213,38.3835],[26.1945,38.2415],[26.2215,38.1675],[26.363,38.0955],[26.9185,37.965],[27.054,37.914],[27.101,37.846],[27.1605,37.7905],[27.165,37.724],[27.0865,37.6995],[27.0085,37.6965],[26.9585,37.649],[27.054,37.564],[27.109,37.4575],[27.1535,37.312],[27.1595,37.2425],[27.1435,37.065],[27.1875,36.981],[27.246,36.9425],[27.3935,36.905],[27.351,36.8075],[27.247,36.726],[27.298,36.6335],[27.3515,36.586],[27.462,36.543],[27.6415,36.5745],[27.775,36.6935],[27.814,36.7085],[27.8955,36.6975],[27.931,36.5375],[27.9605,36.4705],[28.002,36.4695],[28.2095,36.5615],[28.2825,36.55],[28.4435,36.561],[28.5345,36.513],[28.5895,36.501],[28.6135,36.461],[28.711,36.389],[28.853,36.368],[28.895,36.277],[29.0015,36.191],[29.0875,36.149],[29.1745,36.065],[29.236,36.03],[29.3105,36.011],[29.484,36.1855],[29.6135,36.149],[29.7005,36.086],[29.7295,35.923],[29.8065,35.93],[30.0065,35.996],[30.1175,36.047],[30.2,36.059],[30.257,36.01],[30.382,35.97],[30.51,35.983],[30.59,36.02],[30.6915,36.127],[30.727,36.21],[30.76,36.254],[30.802,36.47],[30.836,36.551],[30.832,36.647],[30.9815,36.657],[31.2095,36.622],[31.291,36.579],[31.436,36.537],[31.5145,36.486],[31.686,36.418],[31.779,36.396],[31.9205,36.335],[31.965,36.292],[32.084,36.132],[32.223,36.016],[32.3505,35.944],[32.62,35.847],[32.7775,35.818],[32.8685,35.824],[32.988,35.884],[33.1065,35.871],[33.199,35.894],[33.2565,35.928],[33.3525,35.915],[33.4665,35.927],[33.537,35.921],[33.6385,35.936],[33.7345,35.938],[33.7915,35.953],[33.938,36.033],[34.0485,36.045],[34.0925,36.062],[34.2605,36.187],[34.3075,36.252],[34.3255,36.312],[34.449,36.433],[34.5655,36.497],[34.6365,36.553],[34.7155,36.593],[34.7995,36.543],[34.91,36.523],[35.151,36.408],[35.21,36.368],[35.595,36.158],[35.692,36.034],[35.9115,35.9335],[35.976,35.939],[36.018,35.921],[35.9985,35.884],[36.113,35.862],[36.1735,35.8355],[36.176,35.9215],[36.2085,35.9535],[36.299,35.963],[36.376,36.0115],[36.3905,36.082],[36.371,36.1175],[36.373,36.1785],[36.534,36.2455],[36.6245,36.2145],[36.695,36.245],[36.6565,36.298],[36.6575,36.3375],[36.5995,36.3685],[36.544,36.502],[36.5915,36.5855],[36.5735,36.659],[36.6315,36.7125],[36.613,36.744],[36.685,36.8255],[36.752,36.8195],[36.7935,36.797],[36.985,36.762],[37.037,36.7365],[37.0205,36.666],[37.0855,36.6345],[37.106,36.6715],[37.1625,36.657],[37.217,36.6755],[37.284,36.67],[37.471,36.635],[37.5245,36.6875],[37.7005,36.7575],[37.7775,36.749],[37.902,36.791],[37.9405,36.8185],[38.0195,36.8265],[38.0365,36.8605],[38.243,36.924],[38.3455,36.8995],[38.3925,36.902],[38.5525,36.8455],[38.689,36.7365],[38.7475,36.707],[38.8955,36.701],[39.019,36.711],[39.2095,36.6695],[39.361,36.692],[39.491,36.7005],[39.6385,36.7245],[39.7105,36.75],[39.8165,36.756],[39.854,36.7765],[40.0275,36.826],[40.0595,36.8545],[40.181,36.877],[40.379,36.9795],[40.419,37.0165],[40.528,37.028],[40.695,37.1035],[40.7625,37.1255],[40.9165,37.1315],[41.117,37.0945],[41.1635,37.0945],[41.218,37.062],[41.2925,37.081],[41.5175,37.078],[41.5975,37.1055],[41.794,37.1285],[42.035,37.182],[42.1835,37.2865],[42.2345,37.283],[42.3435,37.23],[42.3205,37.1855],[42.3515,37.1055]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/b4/Flag_of_Turkey.svg","name:en":"Turkey","wikidata":"Q43","ISO3166-1:alpha2":"TR","ISO3166-1:alpha3":"TUR","ISO3166-1:numeric":"792"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[41.6085,15.5425],[41.619,15.4705],[41.668,15.3905],[41.768,15.3315],[41.8375,15.324],[41.9395,15.3525],[41.996,15.3925],[42.034,15.4425],[42.056,15.5105],[42.0585,15.562],[42.0225,15.6635],[41.9665,15.719],[41.88,15.7535],[41.7715,15.747],[41.689,15.703],[41.625,15.6195],[41.6085,15.5425]]],[[[41.7555,16.29],[41.787,16.192],[41.865,16.122],[41.9375,16.0995],[41.997,16.098],[42.067,16.118],[42.0965,16.035],[42.126,16.001],[42.083,15.918],[42.08,15.838],[42.0965,15.7885],[42.0755,15.7025],[42.0995,15.592],[42.1515,15.524],[42.1645,15.4615],[42.214,15.3705],[42.2015,15.3575],[42.1135,15.376],[42.0595,15.37],[41.973,15.326],[41.9045,15.2275],[41.892,15.161],[41.912,15.038],[41.986,14.8985],[42.032,14.8545],[42.119,14.8175],[42.1885,14.8145],[42.2835,14.849],[42.37,14.9385],[42.4005,15.0285],[42.5415,14.9745],[42.668,14.9825],[42.662,14.894],[42.69,14.783],[42.7215,14.7315],[42.694,14.652],[42.6945,14.5985],[42.7155,14.533],[42.775,14.427],[42.8035,14.3315],[42.83,14.28],[42.7595,14.287],[42.6985,14.2785],[42.6145,14.23],[42.526,14.144],[42.4775,14.061],[42.5965,13.741],[42.6275,13.6585],[42.637,13.5975],[42.7235,13.458],[42.834,13.4275],[42.9255,13.4945],[42.9985,13.602],[43.015,13.683],[43.05,13.73],[43.078,13.657],[43.055,13.543],[43.0445,13.405],[43.021,13.322],[43.0345,13.213],[43.079,13.1335],[43.145,13.068],[43.2125,12.9695],[43.249,12.8675],[43.298,12.7925],[43.273,12.687],[43.29,12.633],[43.3205,12.5955],[43.4305,12.5525],[43.5355,12.5525],[43.6365,12.518],[43.738,12.492],[43.8125,12.433],[43.8655,12.408],[43.9355,12.394],[44.0785,12.406],[44.1295,12.4205],[44.1905,12.4165],[44.329,12.4335],[44.416,12.47],[44.4705,12.4765],[44.5295,12.5015],[44.593,12.5645],[44.653,12.539],[44.7665,12.538],[44.8885,12.5255],[44.9935,12.5535],[45.05,12.5525],[45.152,12.586],[45.209,12.6305],[45.246,12.691],[45.267,12.8245],[45.4265,12.86],[45.525,12.9115],[45.6095,12.9985],[45.6995,13.1205],[45.743,13.1585],[45.8495,13.1865],[46.0325,13.2175],[46.0765,13.2135],[46.245,13.23],[46.4425,13.2025],[46.618,13.2355],[46.6705,13.226],[46.7415,13.231],[46.924,13.3045],[46.9955,13.351],[47.1225,13.375],[47.437,13.457],[47.509,13.4895],[47.584,13.543],[47.7865,13.7225],[47.9075,13.7595],[48.0185,13.8415],[48.0785,13.7945],[48.1635,13.7695],[48.25,13.7765],[48.3125,13.741],[48.4245,13.731],[48.4985,13.76],[48.6195,13.847],[48.7345,13.8505],[48.837,13.9045],[48.9715,14.0225],[49.03,14.092],[49.1475,14.1895],[49.1905,14.26],[49.2015,14.312],[49.303,14.3575],[49.351,14.4145],[49.433,14.4595],[49.6,14.52],[49.645,14.5505],[49.9425,14.6355],[50.013,14.62],[50.2495,14.6575],[50.339,14.7195],[50.4325,14.746],[50.503,14.8085],[50.5525,14.836],[50.6445,14.8585],[50.7555,14.873],[50.8655,14.8965],[51.089,14.9565],[51.1915,14.976],[51.362,15.034],[51.44,15.047],[51.5075,15.074],[51.603,15.13],[51.715,15.1445],[51.7745,15.1705],[51.8455,15.244],[51.9095,15.2525],[51.9705,15.2775],[52.037,15.345],[52.3045,15.4405],[52.3965,15.5205],[52.4295,15.6],[52.4265,15.683],[52.407,15.7455],[52.41,15.83],[52.371,15.9695],[52.41,16.0795],[52.442,16.131],[52.5555,16.2445],[52.6075,16.269],[52.8935,16.3695],[53.0375,16.4285],[53.189,16.465],[53.1105,16.647],[52.8125,17.2855],[52.746,17.2945],[52.782,17.3495],[52.5005,17.9455],[52.2835,18.404],[52.0,19.0],[51.4245,18.9],[50.7835,18.789],[50.1295,18.7215],[49.713,18.6785],[49.1165,18.6165],[48.588,18.3615],[48.1835,18.1665],[47.849,17.7565],[47.6,17.45],[47.4665,17.1165],[47.1835,16.95],[47.0,16.95],[46.75,17.2835],[46.3665,17.2335],[46.1,17.25],[45.4,17.3335],[45.2165,17.4335],[44.65,17.4335],[44.5665,17.4055],[44.4665,17.4335],[44.366,17.4335],[44.138,17.409],[44.1005,17.366],[44.0665,17.406],[44.0115,17.4025],[43.9665,17.3305],[43.832,17.339],[43.789,17.375],[43.6835,17.3665],[43.5665,17.4835],[43.4845,17.545],[43.4195,17.566],[43.355,17.5565],[43.241,17.4805],[43.228,17.3855],[43.326,17.3285],[43.2015,17.259],[43.2165,17.2105],[43.171,17.1725],[43.158,17.114],[43.227,17.0755],[43.24,17.024],[43.191,17.016],[43.178,16.9635],[43.138,16.9175],[43.18,16.849],[43.2215,16.838],[43.252,16.7795],[43.2305,16.7305],[43.2345,16.643],[43.1285,16.6715],[43.1465,16.6365],[43.1205,16.5285],[42.992,16.5215],[42.945,16.49],[42.95,16.3995],[42.8305,16.3795],[42.772,16.404],[42.15,16.404],[41.7835,16.29],[41.7555,16.29]]],[[[51.8585,12.2235],[51.8735,12.1515],[51.8985,12.1085],[51.9515,12.0585],[52.0035,12.0295],[52.1865,11.9645],[52.274,11.9495],[52.4075,11.9575],[52.4785,11.9815],[52.5525,12.051],[52.589,12.1355],[52.599,12.1975],[52.5655,12.306],[52.518,12.3555],[52.4695,12.383],[52.3405,12.396],[52.3375,12.4985],[52.3005,12.567],[52.2575,12.606],[52.195,12.6345],[52.0965,12.6385],[52.0225,12.609],[51.954,12.537],[51.9285,12.4635],[51.933,12.3915],[51.8855,12.337],[51.8585,12.2235]]],[[[52.7735,12.1585],[52.7925,12.082],[52.86,12.003],[52.984,11.9395],[53.0415,11.9235],[53.1575,11.9395],[53.2035,11.921],[53.289,11.909],[53.3665,11.918],[53.433,11.9515],[53.473,11.991],[53.5075,12.0605],[53.5115,12.143],[53.6565,12.106],[53.768,12.103],[53.963,12.1335],[54.039,12.131],[54.1065,12.1525],[54.182,12.156],[54.236,12.175],[54.3595,12.249],[54.4595,12.2725],[54.553,12.315],[54.673,12.3955],[54.713,12.445],[54.7375,12.517],[54.721,12.6295],[54.679,12.692],[54.617,12.735],[54.5145,12.7565],[54.378,12.8035],[54.282,12.857],[54.1465,12.897],[54.0515,12.8975],[53.951,12.848],[53.8635,12.8465],[53.812,12.8315],[53.7545,12.8735],[53.6955,12.897],[53.555,12.913],[53.4695,12.9095],[53.3965,12.8845],[53.266,12.8075],[53.203,12.834],[53.1035,12.831],[53.048,12.807],[52.9975,12.7625],[52.9695,12.7175],[52.9525,12.6555],[52.96,12.579],[53.0145,12.491],[53.058,12.46],[53.126,12.4375],[53.172,12.3795],[53.159,12.356],[53.056,12.3785],[52.977,12.376],[52.891,12.349],[52.8155,12.289],[52.7775,12.207],[52.7735,12.1585]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/89/Flag_of_Yemen.svg","name:en":"Yemen","wikidata":"Q805","ISO3166-1:alpha2":"YE","ISO3166-1:alpha3":"YEM","ISO3166-1:numeric":"887"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[55.999,41.32],[55.8765,41.332],[55.774,41.284],[55.7435,41.302],[55.613,41.2705],[55.5675,41.2885],[55.4995,41.2515],[55.3155,41.391],[55.2115,41.5145],[55.1155,41.611],[55.066,41.6995],[54.95,41.833],[54.952,41.9065],[54.7795,42.0525],[54.218,42.379],[53.5705,42.289],[52.985,42.121],[52.9345,42.093],[52.497,41.7975],[52.45,41.762],[52.335,41.7195],[52.472,41.626],[52.422,41.491],[52.4735,41.4445],[52.639,41.346],[52.7265,41.163],[52.725,41.048],[52.8125,40.958],[52.7345,40.7515],[52.717,40.6685],[52.63,40.579],[52.57,40.4255],[52.553,40.262],[52.619,40.161],[52.59,40.0305],[52.6335,39.9595],[52.7965,39.7465],[52.997,39.7565],[53.036,39.737],[53.0855,39.596],[52.9835,39.4805],[52.9665,39.375],[53.0445,39.248],[52.9475,39.1145],[52.916,38.9765],[52.924,38.856],[52.9725,38.749],[53.1305,38.753],[53.2855,38.902],[53.5675,39.0065],[53.7495,38.966],[53.869,38.931],[53.865,38.8865],[53.764,38.702],[53.714,38.5415],[53.755,38.3705],[53.739,38.1695],[53.7075,37.896],[53.722,37.708],[53.735,37.6595],[53.8465,37.379],[53.8965,37.3475],[54.2445,37.32],[54.285,37.352],[54.3975,37.3605],[54.4875,37.418],[54.5825,37.4585],[54.6735,37.437],[54.7315,37.488],[54.7985,37.522],[54.819,37.6005],[54.7885,37.647],[54.843,37.745],[54.942,37.79],[55.0395,37.8635],[55.127,37.9455],[55.2735,37.9905],[55.3845,38.042],[55.443,38.086],[55.7485,38.1255],[55.842,38.102],[56.0215,38.0775],[56.173,38.0945],[56.216,38.072],[56.303,38.083],[56.352,38.117],[56.3195,38.1745],[56.333,38.1975],[56.4245,38.2545],[56.5465,38.266],[56.614,38.2405],[56.664,38.2685],[56.7565,38.285],[56.7925,38.2515],[56.91,38.2145],[56.992,38.2145],[57.051,38.1935],[57.1345,38.236],[57.161,38.272],[57.2465,38.2665],[57.373,38.0755],[57.3485,37.999],[57.458,37.938],[57.512,37.9195],[57.5795,37.938],[57.6285,37.922],[57.7205,37.922],[57.796,37.8995],[57.8255,37.863],[57.8915,37.871],[58.028,37.806],[58.155,37.7935],[58.2035,37.7755],[58.229,37.7295],[58.227,37.684],[58.3595,37.661],[58.3885,37.627],[58.498,37.648],[58.55,37.706],[58.6655,37.655],[58.713,37.6485],[58.8045,37.667],[58.836,37.6995],[58.8765,37.669],[58.926,37.67],[59.0605,37.63],[59.229,37.512],[59.3535,37.5285],[59.392,37.479],[59.378,37.4075],[59.397,37.3175],[59.435,37.304],[59.538,37.1855],[59.558,37.1315],[59.6265,37.1185],[59.6535,37.142],[59.772,37.1255],[59.931,37.0425],[59.9815,37.047],[60.043,37.021],[60.085,36.978],[60.0945,36.9365],[60.1695,36.848],[60.215,36.8205],[60.267,36.757],[60.3455,36.6295],[60.6085,36.628],[60.675,36.622],[61.146,36.6465],[61.1855,36.5495],[61.157,36.4865],[61.1665,36.4135],[61.1425,36.393],[61.1615,36.297],[61.2265,36.1665],[61.2285,36.1125],[61.164,36.033],[61.1835,35.943],[61.241,35.8855],[61.262,35.8135],[61.226,35.6505],[61.274,35.605],[61.3605,35.6295],[61.3855,35.569],[61.45,35.5205],[61.588,35.437],[61.7795,35.411],[61.968,35.454],[62.0645,35.4345],[62.1535,35.341],[62.2655,35.2955],[62.291,35.256],[62.2985,35.138],[62.326,35.145],[62.4775,35.279],[62.611,35.2345],[62.713,35.2395],[62.7855,35.301],[62.824,35.3],[62.8775,35.345],[63.016,35.4125],[63.1025,35.4285],[63.13,35.544],[63.095,35.6185],[63.1115,35.6335],[63.249,35.681],[63.195,35.713],[63.109,35.827],[63.121,35.862],[63.299,35.8565],[63.5005,35.8985],[63.5365,35.948],[63.7035,35.9675],[63.989,36.0365],[64.0685,35.9975],[64.0595,36.0985],[64.17,36.163],[64.284,36.1515],[64.3155,36.208],[64.445,36.2425],[64.58,36.3515],[64.6365,36.4425],[64.6185,36.6365],[64.707,36.796],[64.7975,36.922],[64.7555,37.1125],[64.99,37.218],[65.1235,37.244],[65.1905,37.236],[65.2285,37.251],[65.2835,37.228],[65.556,37.2405],[65.6435,37.3295],[65.646,37.4385],[65.706,37.5395],[65.803,37.523],[65.8185,37.5],[65.894,37.4755],[65.974,37.467],[66.085,37.4395],[66.167,37.3695],[66.246,37.356],[66.307,37.32],[66.431,37.319],[66.5515,37.3545],[66.531,37.403],[66.5825,37.431],[66.5725,37.506],[66.5255,37.558],[66.55,37.6965],[66.548,37.792],[66.607,37.8685],[66.6895,37.918],[66.685,37.9685],[66.511,38.032],[66.4275,38.0235],[66.3235,38.0655],[66.2725,38.1075],[66.248,38.1555],[66.1665,38.157],[66.0295,38.227],[65.9565,38.2385],[65.913,38.277],[65.842,38.253],[65.564,38.2885],[65.171,38.5025],[65.0375,38.6055],[64.996,38.6115],[64.897,38.671],[64.8205,38.683],[64.717,38.752],[64.664,38.771],[64.525,38.847],[64.355,38.9765],[64.3135,38.966],[64.181,38.962],[64.0085,39.0535],[63.705,39.224],[63.694,39.2725],[63.565,39.3375],[63.2775,39.5035],[62.985,39.6635],[62.798,39.776],[62.488,39.9515],[62.4425,39.991],[62.427,40.065],[62.3965,40.0995],[62.414,40.1735],[62.3895,40.2445],[62.3915,40.318],[62.333,40.3835],[62.3495,40.4385],[62.2075,40.5085],[62.144,40.5545],[62.091,40.657],[62.0455,40.712],[62.042,40.7575],[61.9855,40.837],[62.0,40.8925],[61.971,41.019],[61.901,41.0535],[61.8855,41.107],[61.7565,41.1755],[61.713,41.215],[61.609,41.256],[61.481,41.2865],[61.4085,41.288],[61.3995,41.2015],[61.3305,41.148],[61.2405,41.1455],[61.08,41.2165],[61.065,41.2545],[60.999,41.2425],[60.816,41.259],[60.681,41.2555],[60.579,41.2235],[60.476,41.2215],[60.4055,41.253],[60.2765,41.3305],[60.23,41.337],[60.093,41.413],[60.074,41.4575],[60.104,41.542],[60.141,41.556],[60.177,41.607],[60.1455,41.6585],[60.082,41.7035],[60.0465,41.751],[60.0795,41.799],[60.1275,41.807],[60.2375,41.7665],[60.326,41.7685],[60.265,41.8155],[60.1645,41.85],[60.104,41.9035],[60.043,41.909],[60.01,41.9555],[59.97,41.9405],[59.9165,41.9915],[59.9505,42.0195],[60.0105,41.991],[60.041,42.0265],[60.0205,42.118],[59.9715,42.1575],[60.0205,42.1875],[59.955,42.2315],[59.9265,42.2775],[59.775,42.272],[59.717,42.291],[59.609,42.2975],[59.4475,42.29],[59.418,42.325],[59.305,42.361],[59.269,42.431],[59.172,42.531],[59.0765,42.519],[59.0145,42.527],[58.8715,42.6115],[58.747,42.7115],[58.6725,42.7605],[58.612,42.7035],[58.59,42.6445],[58.4675,42.6375],[58.3615,42.6745],[58.281,42.685],[58.1565,42.6215],[58.2275,42.5735],[58.3065,42.5485],[58.364,42.449],[58.426,42.38],[58.523,42.3185],[58.52,42.2735],[58.446,42.2955],[58.4005,42.3395],[58.3625,42.421],[58.3035,42.455],[58.1375,42.4805],[58.04,42.504],[57.904,42.426],[57.9475,42.3715],[57.9285,42.245],[57.847,42.2465],[57.835,42.1695],[57.7645,42.1695],[57.655,42.1375],[57.5525,42.1505],[57.356,42.1435],[57.2655,42.112],[57.214,42.0685],[57.1675,41.975],[57.13,41.9435],[57.0015,41.896],[56.9675,41.8045],[56.972,41.6725],[57.02,41.5865],[57.0315,41.446],[57.0865,41.375],[57.089,41.3345],[57.0425,41.263],[56.7875,41.2805],[56.5595,41.289],[56.4325,41.3],[55.999,41.32]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/1b/Flag_of_Turkmenistan.svg","name:en":"Turkmenistan","wikidata":"Q874","ISO3166-1:alpha2":"TM","ISO3166-1:alpha3":"TKM","ISO3166-1:numeric":"795"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[66.5515,37.3545],[66.5935,37.3685],[66.652,37.3235],[66.6925,37.357],[66.801,37.3635],[66.85,37.354],[66.95,37.3985],[67.045,37.3805],[67.1125,37.327],[67.1065,37.2915],[67.2235,37.244],[67.27,37.182],[67.453,37.2365],[67.517,37.265],[67.558,37.2225],[67.7235,37.219],[67.787,37.1855],[67.842,37.3285],[67.801,37.388],[67.8305,37.5445],[67.8525,37.6115],[67.9265,37.647],[68.0165,37.707],[68.12,37.8715],[68.143,37.933],[68.218,37.9335],[68.2705,37.915],[68.3075,38.029],[68.377,38.0845],[68.4055,38.1805],[68.356,38.2325],[68.3025,38.2555],[68.2875,38.3055],[68.157,38.374],[68.1375,38.41],[68.065,38.41],[68.1165,38.468],[68.064,38.5325],[68.0825,38.6725],[68.058,38.7075],[68.1285,38.7365],[68.095,38.814],[68.1715,38.831],[68.2015,38.867],[68.195,38.94],[68.1345,38.963],[68.0945,39.023],[68.028,39.01],[67.9215,39.0265],[67.869,39.0015],[67.768,39.016],[67.6985,39.003],[67.6685,39.0845],[67.677,39.1465],[67.584,39.1885],[67.464,39.1965],[67.399,39.226],[67.344,39.3085],[67.449,39.3125],[67.4785,39.399],[67.4325,39.53],[67.456,39.5745],[67.629,39.6135],[67.7045,39.658],[67.761,39.64],[67.793,39.657],[67.939,39.599],[67.976,39.605],[68.0385,39.5775],[68.1965,39.5545],[68.2865,39.5595],[68.3695,39.53],[68.391,39.5445],[68.524,39.532],[68.575,39.619],[68.6175,39.6375],[68.626,39.6955],[68.587,39.722],[68.6425,39.7765],[68.63,39.8545],[68.746,39.846],[68.828,39.9035],[68.8855,39.863],[68.928,39.9305],[68.8575,40.0465],[68.9685,40.065],[69.0085,40.099],[69.024,40.1455],[68.9405,40.147],[68.766,40.098],[68.633,40.0765],[68.531,40.118],[68.533,40.144],[68.7795,40.204],[68.7975,40.1725],[68.863,40.21],[69.0355,40.2285],[69.1895,40.215],[69.3055,40.195],[69.3075,40.282],[69.3365,40.349],[69.303,40.3675],[69.2715,40.488],[69.212,40.541],[69.2625,40.5765],[69.347,40.5855],[69.33,40.699],[69.3645,40.744],[69.3475,40.775],[69.4585,40.785],[69.5515,40.7695],[69.5755,40.7185],[69.6695,40.634],[69.7205,40.6285],[69.765,40.6515],[69.793,40.7015],[69.9315,40.7165],[70.068,40.755],[70.1205,40.8165],[70.2015,40.822],[70.2785,40.871],[70.372,40.898],[70.3955,41.027],[70.4695,41.042],[70.556,40.979],[70.6455,40.865],[70.6985,40.8355],[70.7275,40.769],[70.796,40.7325],[70.7375,40.664],[70.64,40.6265],[70.588,40.5665],[70.514,40.5495],[70.483,40.5015],[70.328,40.455],[70.379,40.3865],[70.4765,40.352],[70.552,40.355],[70.5775,40.336],[70.5795,40.2345],[70.6325,40.1775],[70.76,40.188],[70.894,40.2315],[70.897,40.312],[70.9815,40.3215],[71.0325,40.306],[71.1245,40.3355],[71.274,40.3435],[71.308,40.3085],[71.369,40.319],[71.428,40.284],[71.5115,40.268],[71.513,40.231],[71.6165,40.2065],[71.62,40.267],[71.6985,40.2445],[71.6895,40.1875],[71.72,40.1535],[71.8285,40.241],[71.8845,40.2625],[71.9785,40.2565],[71.9665,40.3195],[72.089,40.405],[72.087,40.451],[72.159,40.483],[72.2535,40.479],[72.278,40.434],[72.321,40.446],[72.4105,40.401],[72.441,40.4735],[72.42,40.549],[72.3855,40.604],[72.4095,40.619],[72.476,40.551],[72.5055,40.5605],[72.613,40.5175],[72.6695,40.5235],[72.6925,40.597],[72.757,40.622],[72.8015,40.6775],[72.9465,40.732],[72.9905,40.7645],[73.1395,40.7855],[73.1375,40.8275],[73.0895,40.8495],[73.014,40.845],[72.939,40.8005],[72.832,40.8565],[72.7125,40.8485],[72.5915,40.868],[72.593,40.905],[72.5515,40.962],[72.4895,40.976],[72.444,41.032],[72.3305,41.037],[72.2465,41.004],[72.1715,40.995],[72.2085,41.0595],[72.137,41.1625],[72.074,41.1215],[71.8865,41.168],[71.862,41.201],[71.925,41.294],[71.831,41.3875],[71.7595,41.4355],[71.711,41.4875],[71.6695,41.4975],[71.681,41.422],[71.6365,41.3425],[71.569,41.293],[71.4705,41.297],[71.4425,41.248],[71.4515,41.157],[71.3955,41.1075],[71.3465,41.127],[71.2795,41.102],[71.2725,41.1735],[71.2315,41.1925],[71.127,41.1435],[71.039,41.1905],[70.9625,41.166],[70.841,41.251],[70.776,41.2165],[70.7785,41.3885],[70.721,41.442],[70.666,41.47],[70.488,41.398],[70.4075,41.4705],[70.303,41.5205],[70.2405,41.4915],[70.172,41.5175],[70.213,41.611],[70.263,41.6075],[70.367,41.6475],[70.3965,41.684],[70.4965,41.7195],[70.5275,41.7985],[70.5965,41.818],[70.665,41.907],[70.854,41.9415],[70.838,41.9995],[70.865,42.054],[70.9925,42.0305],[71.029,42.0675],[71.174,42.144],[71.218,42.137],[71.269,42.1965],[71.1335,42.282],[71.025,42.2925],[70.9885,42.2525],[70.9435,42.2635],[70.8835,42.2105],[70.7985,42.2075],[70.687,42.121],[70.6345,42.0155],[70.5595,42.0235],[70.55,42.0675],[70.476,42.1095],[70.32,42.037],[70.3355,41.9745],[70.2615,41.9545],[70.1575,41.8495],[70.077,41.8085],[69.981,41.781],[69.9575,41.734],[69.8825,41.7065],[69.839,41.7205],[69.7455,41.701],[69.696,41.673],[69.6175,41.662],[69.501,41.547],[69.4455,41.5635],[69.4005,41.5035],[69.301,41.438],[69.25,41.4665],[69.1565,41.4345],[69.1665,41.395],[69.0415,41.366],[69.0395,41.255],[68.9915,41.214],[68.804,41.116],[68.714,41.032],[68.7485,40.9815],[68.6435,40.943],[68.6285,40.9975],[68.5895,41.029],[68.5265,41.0205],[68.4905,40.9715],[68.579,40.925],[68.5905,40.865],[68.5625,40.806],[68.586,40.78],[68.584,40.709],[68.644,40.694],[68.652,40.614],[68.6105,40.577],[68.502,40.5795],[68.371,40.6175],[68.218,40.6875],[68.077,40.789],[67.9775,40.831],[68.0125,40.8905],[68.0885,40.9255],[68.088,40.97],[68.0555,41.009],[68.111,41.036],[68.071,41.0805],[68.0205,41.0205],[67.9835,41.025],[67.9885,41.0855],[67.9655,41.149],[67.8665,41.1365],[67.7235,41.197],[67.3125,41.1355],[66.7095,41.1425],[66.6455,41.3475],[66.6085,41.494],[66.5355,41.8785],[65.9995,41.9395],[66.0,42.36],[66.088,42.3465],[66.095,42.566],[66.08,42.933],[65.8655,42.8675],[65.742,43.0],[65.5595,43.2895],[65.4955,43.333],[65.185,43.495],[65.002,43.7155],[64.9325,43.7355],[64.535,43.5705],[63.8575,43.5995],[63.349,43.6505],[62.2425,43.5185],[62.004,43.5055],[61.4255,43.9995],[61.138,44.226],[61.1065,44.3605],[61.068,44.3705],[60.7445,44.644],[59.9745,44.968],[59.5055,45.161],[58.9905,45.3695],[58.5885,45.5905],[57.9045,45.442],[57.0005,45.2375],[55.9985,45.0005],[55.999,44.437],[55.999,43.7535],[55.999,41.32],[56.4325,41.3],[56.5595,41.289],[56.7875,41.2805],[57.0425,41.263],[57.089,41.3345],[57.0865,41.375],[57.0315,41.446],[57.02,41.5865],[56.972,41.6725],[56.9675,41.8045],[57.0015,41.896],[57.13,41.9435],[57.1675,41.975],[57.214,42.0685],[57.2655,42.112],[57.356,42.1435],[57.5525,42.1505],[57.655,42.1375],[57.7645,42.1695],[57.835,42.1695],[57.847,42.2465],[57.9285,42.245],[57.9475,42.3715],[57.904,42.426],[58.04,42.504],[58.1375,42.4805],[58.3035,42.455],[58.3625,42.421],[58.4005,42.3395],[58.446,42.2955],[58.52,42.2735],[58.523,42.3185],[58.426,42.38],[58.364,42.449],[58.3065,42.5485],[58.2275,42.5735],[58.1565,42.6215],[58.281,42.685],[58.3615,42.6745],[58.4675,42.6375],[58.59,42.6445],[58.612,42.7035],[58.6725,42.7605],[58.747,42.7115],[58.8715,42.6115],[59.0145,42.527],[59.0765,42.519],[59.172,42.531],[59.269,42.431],[59.305,42.361],[59.418,42.325],[59.4475,42.29],[59.609,42.2975],[59.717,42.291],[59.775,42.272],[59.9265,42.2775],[59.955,42.2315],[60.0205,42.1875],[59.9715,42.1575],[60.0205,42.118],[60.041,42.0265],[60.0105,41.991],[59.9505,42.0195],[59.9165,41.9915],[59.97,41.9405],[60.01,41.9555],[60.043,41.909],[60.104,41.9035],[60.1645,41.85],[60.265,41.8155],[60.326,41.7685],[60.2375,41.7665],[60.1275,41.807],[60.0795,41.799],[60.0465,41.751],[60.082,41.7035],[60.1455,41.6585],[60.177,41.607],[60.141,41.556],[60.104,41.542],[60.074,41.4575],[60.093,41.413],[60.23,41.337],[60.2765,41.3305],[60.4055,41.253],[60.476,41.2215],[60.579,41.2235],[60.681,41.2555],[60.816,41.259],[60.999,41.2425],[61.065,41.2545],[61.08,41.2165],[61.2405,41.1455],[61.3305,41.148],[61.3995,41.2015],[61.4085,41.288],[61.481,41.2865],[61.609,41.256],[61.713,41.215],[61.7565,41.1755],[61.8855,41.107],[61.901,41.0535],[61.971,41.019],[62.0,40.8925],[61.9855,40.837],[62.042,40.7575],[62.0455,40.712],[62.091,40.657],[62.144,40.5545],[62.2075,40.5085],[62.3495,40.4385],[62.333,40.3835],[62.3915,40.318],[62.3895,40.2445],[62.414,40.1735],[62.3965,40.0995],[62.427,40.065],[62.4425,39.991],[62.488,39.9515],[62.798,39.776],[62.985,39.6635],[63.2775,39.5035],[63.565,39.3375],[63.694,39.2725],[63.705,39.224],[64.0085,39.0535],[64.181,38.962],[64.3135,38.966],[64.355,38.9765],[64.525,38.847],[64.664,38.771],[64.717,38.752],[64.8205,38.683],[64.897,38.671],[64.996,38.6115],[65.0375,38.6055],[65.171,38.5025],[65.564,38.2885],[65.842,38.253],[65.913,38.277],[65.9565,38.2385],[66.0295,38.227],[66.1665,38.157],[66.248,38.1555],[66.2725,38.1075],[66.3235,38.0655],[66.4275,38.0235],[66.511,38.032],[66.685,37.9685],[66.6895,37.918],[66.607,37.8685],[66.548,37.792],[66.55,37.6965],[66.5255,37.558],[66.5725,37.506],[66.5825,37.431],[66.531,37.403],[66.5515,37.3545]]],[[[71.036,40.1805],[71.0535,40.0925],[71.085,40.0755],[71.103,40.026],[71.1145,40.014],[71.0875,39.9885],[71.155,39.933],[71.21,39.9545],[71.115,40.0205],[71.0875,40.07],[71.091,40.1045],[71.076,40.1505],[71.036,40.1805]]],[[[71.7145,39.966],[71.8305,39.955],[71.785,39.999],[71.7145,39.966]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/84/Flag_of_Uzbekistan.svg","name:en":"Uzbekistan","wikidata":"Q265","ISO3166-1:alpha2":"UZ","ISO3166-1:alpha3":"UZB","ISO3166-1:numeric":"860"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[67.787,37.1855],[67.7905,37.086],[67.8895,37.0565],[67.9105,37.0145],[67.972,36.9805],[68.0155,36.9255],[68.0515,36.9285],[68.174,37.0145],[68.253,37.005],[68.2885,37.0315],[68.2905,37.09],[68.3155,37.1105],[68.39,37.098],[68.406,37.145],[68.543,37.167],[68.617,37.1985],[68.6715,37.2755],[68.826,37.2695],[68.8085,37.3065],[68.962,37.325],[68.9995,37.308],[69.0295,37.2565],[69.108,37.1865],[69.2535,37.0935],[69.3235,37.118],[69.37,37.166],[69.4035,37.168],[69.425,37.246],[69.393,37.36],[69.371,37.379],[69.3895,37.4495],[69.457,37.494],[69.511,37.5805],[69.6225,37.5695],[69.7135,37.5745],[69.7625,37.5915],[69.802,37.569],[69.8415,37.6035],[69.9405,37.608],[69.998,37.567],[70.1125,37.5245],[70.145,37.525],[70.266,37.6115],[70.261,37.6395],[70.3005,37.7035],[70.2795,37.816],[70.1855,37.85],[70.171,37.93],[70.262,37.9375],[70.291,37.9875],[70.3285,37.999],[70.4315,38.0995],[70.4925,38.1215],[70.5455,38.2435],[70.6055,38.283],[70.611,38.3465],[70.6845,38.364],[70.674,38.4045],[70.7565,38.428],[70.771,38.4565],[70.856,38.4415],[70.956,38.439],[70.9885,38.491],[71.0315,38.461],[71.0465,38.4105],[71.163,38.388],[71.1835,38.347],[71.252,38.3105],[71.3285,38.306],[71.3745,38.2555],[71.3215,38.0675],[71.2755,38.006],[71.261,37.9215],[71.3135,37.919],[71.3275,37.887],[71.524,37.953],[71.591,37.9235],[71.596,37.8075],[71.532,37.7615],[71.5515,37.7365],[71.526,37.6315],[71.5045,37.607],[71.4955,37.5335],[71.5265,37.48],[71.5035,37.451],[71.4865,37.336],[71.485,37.245],[71.4535,37.207],[71.432,37.0565],[71.4625,37.0235],[71.468,36.9515],[71.513,36.898],[71.563,36.764],[71.635,36.69],[71.6755,36.671],[71.8435,36.6805],[71.903,36.734],[71.958,36.756],[72.0185,36.811],[72.193,36.906],[72.209,36.9245],[72.3255,36.9815],[72.441,37.0065],[72.4735,36.9945],[72.5695,37.0075],[72.622,37.029],[72.6625,37.0175],[72.7395,37.119],[72.7755,37.195],[72.818,37.2325],[72.891,37.243],[72.9525,37.2905],[73.094,37.325],[73.1495,37.4045],[73.21,37.404],[73.3005,37.462],[73.3835,37.4465],[73.452,37.474],[73.5135,37.4745],[73.6405,37.4335],[73.7195,37.447],[73.776,37.44],[73.7705,37.344],[73.7325,37.3155],[73.671,37.31],[73.6305,37.2495],[73.843,37.234],[73.852,37.2575],[73.965,37.299],[74.0165,37.2915],[74.0635,37.3125],[74.185,37.3345],[74.276,37.402],[74.375,37.4245],[74.4015,37.392],[74.482,37.413],[74.5575,37.397],[74.566,37.3725],[74.674,37.389],[74.804,37.3555],[74.887,37.258],[74.89,37.234],[74.9105,37.275],[75.1285,37.323],[75.096,37.368],[75.1485,37.421],[75.0575,37.5245],[75.0315,37.501],[74.932,37.5685],[74.9395,37.603],[74.897,37.6655],[74.967,37.752],[74.952,37.809],[74.913,37.8565],[74.9165,38.0245],[74.8685,38.0255],[74.8205,38.0855],[74.8,38.2025],[74.698,38.228],[74.706,38.2825],[74.6655,38.3805],[74.6955,38.425],[74.6505,38.446],[74.5065,38.4715],[74.2845,38.5985],[74.187,38.6405],[74.1215,38.6415],[74.0745,38.611],[74.0395,38.544],[73.9895,38.5245],[73.9235,38.5425],[73.8985,38.581],[73.8035,38.612],[73.805,38.664],[73.744,38.732],[73.768,38.778],[73.699,38.878],[73.7575,38.9425],[73.8065,38.933],[73.854,38.9525],[73.8125,39.043],[73.751,39.0255],[73.7205,39.112],[73.662,39.165],[73.6135,39.2475],[73.569,39.244],[73.535,39.323],[73.5585,39.355],[73.526,39.3915],[73.592,39.4115],[73.6005,39.4595],[73.505,39.4735],[73.3935,39.4525],[73.353,39.3935],[73.2525,39.381],[73.1605,39.3525],[73.1065,39.3765],[72.959,39.3465],[72.7825,39.362],[72.694,39.396],[72.621,39.399],[72.589,39.3595],[72.493,39.38],[72.4625,39.35],[72.3425,39.332],[72.3,39.2925],[72.271,39.1895],[72.2195,39.1825],[72.202,39.2415],[72.1535,39.2735],[72.089,39.278],[72.0355,39.3665],[71.958,39.3315],[71.919,39.2805],[71.802,39.2735],[71.7525,39.324],[71.804,39.403],[71.761,39.4565],[71.633,39.4455],[71.5655,39.457],[71.544,39.505],[71.565,39.576],[71.5145,39.6115],[71.413,39.571],[71.3355,39.5685],[71.3075,39.5235],[71.2585,39.549],[71.1935,39.5115],[71.0895,39.499],[71.072,39.4255],[71.0165,39.3955],[70.941,39.4335],[70.9125,39.3835],[70.768,39.3995],[70.7435,39.497],[70.6925,39.52],[70.65,39.582],[70.606,39.586],[70.525,39.622],[70.4645,39.583],[70.3895,39.582],[70.3295,39.562],[70.309,39.53],[70.239,39.538],[70.2065,39.5785],[70.168,39.554],[70.127,39.5865],[69.996,39.537],[69.978,39.568],[69.9015,39.576],[69.867,39.5295],[69.8285,39.56],[69.7375,39.595],[69.678,39.5805],[69.6295,39.593],[69.574,39.553],[69.448,39.5285],[69.408,39.5495],[69.3375,39.5425],[69.3265,39.7205],[69.265,39.806],[69.327,39.877],[69.297,39.9205],[69.3945,40.0025],[69.432,39.9885],[69.4575,39.935],[69.5355,39.9525],[69.507,40.034],[69.537,40.117],[69.582,40.1045],[69.6755,40.1225],[69.784,40.1795],[69.9955,40.2345],[70.021,40.2305],[70.1755,40.143],[70.241,40.124],[70.292,40.1295],[70.358,40.081],[70.4245,40.0575],[70.498,40.054],[70.5675,40.0305],[70.6095,40.043],[70.6575,40.11],[70.7685,40.1165],[70.7995,40.1475],[70.8855,40.188],[70.894,40.2315],[70.76,40.188],[70.6325,40.1775],[70.5795,40.2345],[70.5775,40.336],[70.552,40.355],[70.4765,40.352],[70.379,40.3865],[70.328,40.455],[70.483,40.5015],[70.514,40.5495],[70.588,40.5665],[70.64,40.6265],[70.7375,40.664],[70.796,40.7325],[70.7275,40.769],[70.6985,40.8355],[70.6455,40.865],[70.556,40.979],[70.4695,41.042],[70.3955,41.027],[70.372,40.898],[70.2785,40.871],[70.2015,40.822],[70.1205,40.8165],[70.068,40.755],[69.9315,40.7165],[69.793,40.7015],[69.765,40.6515],[69.7205,40.6285],[69.6695,40.634],[69.5755,40.7185],[69.5515,40.7695],[69.4585,40.785],[69.3475,40.775],[69.3645,40.744],[69.33,40.699],[69.347,40.5855],[69.2625,40.5765],[69.212,40.541],[69.2715,40.488],[69.303,40.3675],[69.3365,40.349],[69.3075,40.282],[69.3055,40.195],[69.1895,40.215],[69.0355,40.2285],[68.863,40.21],[68.7975,40.1725],[68.7795,40.204],[68.533,40.144],[68.531,40.118],[68.633,40.0765],[68.766,40.098],[68.9405,40.147],[69.024,40.1455],[69.0085,40.099],[68.9685,40.065],[68.8575,40.0465],[68.928,39.9305],[68.8855,39.863],[68.828,39.9035],[68.746,39.846],[68.63,39.8545],[68.6425,39.7765],[68.587,39.722],[68.626,39.6955],[68.6175,39.6375],[68.575,39.619],[68.524,39.532],[68.391,39.5445],[68.3695,39.53],[68.2865,39.5595],[68.1965,39.5545],[68.0385,39.5775],[67.976,39.605],[67.939,39.599],[67.793,39.657],[67.761,39.64],[67.7045,39.658],[67.629,39.6135],[67.456,39.5745],[67.4325,39.53],[67.4785,39.399],[67.449,39.3125],[67.344,39.3085],[67.399,39.226],[67.464,39.1965],[67.584,39.1885],[67.677,39.1465],[67.6685,39.0845],[67.6985,39.003],[67.768,39.016],[67.869,39.0015],[67.9215,39.0265],[68.028,39.01],[68.0945,39.023],[68.1345,38.963],[68.195,38.94],[68.2015,38.867],[68.1715,38.831],[68.095,38.814],[68.1285,38.7365],[68.058,38.7075],[68.0825,38.6725],[68.064,38.5325],[68.1165,38.468],[68.065,38.41],[68.1375,38.41],[68.157,38.374],[68.2875,38.3055],[68.3025,38.2555],[68.356,38.2325],[68.4055,38.1805],[68.377,38.0845],[68.3075,38.029],[68.2705,37.915],[68.218,37.9335],[68.143,37.933],[68.12,37.8715],[68.0165,37.707],[67.9265,37.647],[67.8525,37.6115],[67.8305,37.5445],[67.801,37.388],[67.842,37.3285],[67.787,37.1855]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d0/Flag_of_Tajikistan.svg","name:en":"Tajikistan","wikidata":"Q863","ISO3166-1:alpha2":"TJ","ISO3166-1:alpha3":"TJK","ISO3166-1:numeric":"762"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[10.593,58.761],[10.6365,58.5175],[10.672,58.4505],[10.845,58.258],[10.962,58.0375],[11.1605,57.7005],[11.4065,57.4525],[11.636,57.152],[11.9265,56.9575],[12.022,56.838],[12.0915,56.7805],[12.227,56.707],[12.2995,56.637],[12.1775,56.4795],[12.1075,56.369],[12.088,56.304],[12.368,56.214],[12.642,56.057],[12.6525,55.9265],[12.7085,55.827],[12.896,55.6435],[12.8815,55.614],[12.7295,55.541],[12.7155,55.489],[12.6105,55.4315],[12.642,55.3375],[12.677,55.25],[12.795,55.1665],[12.876,55.135],[13.013,55.168],[13.3435,55.1335],[13.4585,55.141],[13.6105,55.181],[13.7915,55.2165],[13.848,55.215],[13.99,55.18],[14.178,55.173],[14.2895,55.1845],[14.6985,55.4675],[14.7195,55.5385],[14.7015,55.619],[14.5915,55.7555],[14.8045,55.7945],[14.959,55.808],[15.1135,55.861],[15.153,55.898],[15.299,55.889],[15.4915,55.783],[15.6255,55.7485],[15.7655,55.7475],[15.855,55.7635],[15.9565,55.8025],[16.032,55.854],[16.6065,56.028],[16.783,56.124],[16.8755,56.2185],[16.9345,56.3625],[16.989,56.463],[17.066,56.5685],[17.098,56.6545],[17.214,56.782],[17.4225,57.1125],[17.443,57.177],[17.499,57.241],[17.5205,57.3115],[17.5055,57.366],[17.4425,57.458],[17.261,57.615],[17.279,57.7215],[17.2435,57.8265],[17.528,58.241],[17.577,58.397],[18.1305,58.5265],[18.193,58.545],[18.829,58.798],[19.188,58.982],[19.4635,59.1565],[19.7295,59.253],[19.843,59.3155],[20.014,59.5425],[19.9405,59.595],[19.6585,59.7915],[19.083,60.1915],[19.161,60.375],[19.1965,60.517],[19.0985,60.5805],[19.0275,60.605],[18.242,60.8135],[17.8895,60.941],[17.7755,61.172],[17.818,61.2525],[17.8885,61.507],[17.9815,61.695],[18.069,61.979],[18.296,62.4175],[18.41,62.479],[18.9705,62.8195],[19.4325,63.0735],[19.821,63.13],[20.276,63.2435],[20.401,63.332],[20.6965,63.483],[20.9385,63.5195],[21.376,63.633],[21.457,63.753],[21.476,63.8165],[21.4045,63.957],[21.5095,64.041],[21.8515,64.194],[21.9455,64.2545],[22.071,64.386],[22.0895,64.4465],[22.065,64.5205],[21.825,64.8365],[21.91,64.888],[22.1365,64.9555],[22.8335,65.1025],[23.018,65.1715],[23.13,65.2855],[23.6405,65.3215],[24.085,65.3915],[24.1445,65.3955],[24.132,65.5155],[24.1775,65.6605],[24.172,65.7255],[24.132,65.7715],[24.153,65.8625],[24.042,65.9635],[24.0375,66.009],[23.9365,66.0795],[23.916,66.162],[23.7335,66.206],[23.646,66.3005],[23.689,66.3875],[23.65,66.454],[23.8605,66.5595],[23.8685,66.657],[23.908,66.7215],[23.8795,66.762],[23.978,66.784],[23.9945,66.8235],[23.864,66.922],[23.8565,66.956],[23.674,67.065],[23.6535,67.104],[23.557,67.166],[23.596,67.207],[23.5835,67.2695],[23.731,67.2875],[23.775,67.3395],[23.718,67.385],[23.7635,67.426],[23.5365,67.46],[23.4105,67.4675],[23.408,67.5015],[23.545,67.583],[23.559,67.6195],[23.493,67.664],[23.51,67.88],[23.641,67.915],[23.663,67.942],[23.531,68.0065],[23.3965,68.044],[23.2785,68.1575],[23.1415,68.1545],[23.153,68.231],[23.0705,68.2995],[22.918,68.3335],[22.804,68.393],[22.466,68.4415],[22.351,68.4805],[22.153,68.47],[22.018,68.496],[21.89,68.5845],[21.7015,68.5965],[21.7015,68.6305],[21.565,68.6755],[21.43,68.6915],[21.3895,68.765],[21.3195,68.7595],[21.234,68.814],[20.997,68.8965],[20.844,68.9365],[20.9135,68.9605],[20.775,69.0325],[20.5485,69.06],[20.06,69.046],[20.3065,68.926],[20.336,68.8025],[20.203,68.666],[20.0525,68.591],[19.9375,68.558],[20.2265,68.491],[19.9215,68.356],[18.984,68.517],[18.621,68.507],[18.4055,68.582],[18.126,68.5365],[18.101,68.406],[18.1515,68.199],[17.9,67.9695],[17.6645,68.0385],[17.2815,68.119],[17.1805,68.0505],[16.738,67.914],[16.556,67.647],[16.4075,67.534],[16.158,67.519],[16.09,67.4355],[16.404,67.205],[16.3875,67.0455],[16.194,66.9825],[16.039,66.9125],[15.6215,66.5945],[15.377,66.4845],[15.4845,66.2825],[15.0355,66.1535],[14.5165,66.1325],[14.5845,65.9015],[14.6255,65.812],[14.5415,65.7005],[14.499,65.5215],[14.507,65.3095],[14.379,65.2475],[14.326,65.119],[14.13,64.9785],[13.7055,64.64],[13.6545,64.5805],[13.891,64.507],[14.114,64.4625],[14.157,64.195],[13.9675,64.008],[13.7155,64.0465],[13.211,64.0955],[12.9265,64.058],[12.6835,63.974],[12.48,63.819],[12.3305,63.715],[12.2995,63.672],[12.15,63.594],[12.213,63.4785],[12.084,63.3555],[11.9745,63.269],[12.218,63.0005],[12.0745,62.9025],[12.1365,62.748],[12.056,62.612],[12.2995,62.2675],[12.1375,61.724],[12.4195,61.563],[12.5695,61.5685],[12.871,61.3565],[12.834,61.2585],[12.7905,61.197],[12.707,61.1435],[12.6825,61.061],[12.6105,61.0465],[12.4475,61.0505],[12.224,61.013],[12.333,60.89],[12.3345,60.8525],[12.3955,60.734],[12.511,60.6425],[12.516,60.6],[12.607,60.5125],[12.606,60.406],[12.499,60.3235],[12.542,60.1935],[12.5005,60.099],[12.4485,60.039],[12.341,59.9655],[12.231,59.9275],[12.1745,59.89],[11.9415,59.89],[11.8395,59.841],[11.926,59.794],[11.94,59.6945],[11.8555,59.6485],[11.7205,59.6255],[11.691,59.5895],[11.816,59.3445],[11.83,59.2425],[11.783,59.206],[11.775,59.0865],[11.7105,59.0335],[11.688,58.9555],[11.63,58.9085],[11.4555,58.8895],[11.4645,58.991],[11.3685,59.0985],[11.1535,59.0795],[11.117,59.015],[11.0665,58.9775],[10.918,58.9425],[10.639,58.8925],[10.593,58.761]]],[[[17.679,57.3085],[17.7325,57.202],[17.7825,57.0505],[17.756,56.9035],[17.796,56.8275],[17.924,56.75],[18.104,56.7105],[18.271,56.7085],[18.392,56.7275],[18.5345,56.7705],[18.659,56.8435],[19.002,57.1035],[19.287,57.308],[19.356,57.39],[19.3645,57.4725],[19.2935,57.5675],[19.6615,57.8395],[19.72,57.9045],[19.7345,57.9565],[19.7155,58.022],[19.6725,58.0695],[19.562,58.138],[19.4875,58.1655],[19.5745,58.204],[19.666,58.2715],[19.7085,58.3765],[19.671,58.453],[19.5985,58.507],[19.504,58.5495],[19.346,58.587],[19.1555,58.599],[18.9825,58.572],[18.8385,58.4965],[18.8,58.4475],[18.8,58.3395],[18.859,58.2595],[18.9755,58.1785],[18.623,58.1265],[18.362,58.0415],[18.3005,57.9495],[18.1765,57.8495],[17.958,57.7365],[17.8725,57.7095],[17.7795,57.6345],[17.743,57.5845],[17.679,57.3085]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/4c/Flag_of_Sweden.svg","name:en":"Sweden","wikidata":"Q34","ISO3166-1:alpha2":"SE","ISO3166-1:alpha3":"SWE","ISO3166-1:numeric":"752"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[22.5655,49.088],[22.5455,49.007],[22.4775,48.991],[22.4285,48.9315],[22.421,48.8845],[22.376,48.834],[22.3505,48.7715],[22.3395,48.684],[22.1895,48.61],[22.161,48.5675],[22.1375,48.4325],[22.1555,48.4035],[22.2155,48.4245],[22.2645,48.404],[22.2675,48.361],[22.315,48.3285],[22.369,48.245],[22.49,48.253],[22.566,48.179],[22.603,48.104],[22.6755,48.092],[22.7205,48.1155],[22.827,48.1155],[22.8815,48.055],[22.8365,47.99],[22.8975,47.954],[23.091,48.006],[23.1055,48.066],[23.1465,48.0995],[23.2695,48.086],[23.295,48.0445],[23.495,47.968],[23.539,48.0115],[23.6375,48.0025],[23.6655,47.9835],[23.751,47.9985],[23.8195,47.983],[23.882,47.9435],[24.01,47.9665],[24.217,47.903],[24.345,47.9145],[24.436,47.9705],[24.502,47.9525],[24.58,47.967],[24.674,47.8965],[24.6725,47.8635],[24.753,47.83],[24.8275,47.8205],[24.836,47.7795],[24.883,47.7245],[25.0405,47.7305],[25.113,47.756],[25.2215,47.859],[25.237,47.8955],[25.311,47.9145],[25.5925,47.9385],[25.6255,47.949],[25.775,47.9395],[25.861,47.9705],[25.952,47.971],[26.1845,47.9945],[26.214,48.0525],[26.2725,48.082],[26.335,48.1835],[26.46,48.2145],[26.529,48.21],[26.63,48.2595],[26.6485,48.305],[26.696,48.327],[26.7945,48.291],[26.824,48.3445],[26.765,48.3535],[26.775,48.418],[26.8515,48.413],[26.8945,48.3835],[26.9955,48.3595],[27.087,48.413],[27.124,48.379],[27.1925,48.395],[27.2865,48.3715],[27.3195,48.4435],[27.3825,48.411],[27.447,48.409],[27.465,48.4475],[27.581,48.491],[27.6435,48.442],[27.7475,48.458],[27.869,48.407],[27.882,48.373],[27.964,48.325],[28.042,48.326],[28.0935,48.2935],[28.0825,48.2375],[28.1825,48.2555],[28.2315,48.21],[28.303,48.2415],[28.361,48.2415],[28.383,48.175],[28.4635,48.074],[28.5445,48.169],[28.6775,48.143],[28.8335,48.129],[28.8555,48.108],[28.8395,48.035],[28.926,47.96],[28.9615,47.979],[29.026,47.9455],[29.0865,47.9465],[29.093,47.983],[29.1755,47.994],[29.2195,47.7985],[29.2485,47.7515],[29.2045,47.719],[29.231,47.684],[29.2175,47.6125],[29.117,47.5545],[29.182,47.449],[29.2425,47.462],[29.3175,47.449],[29.3375,47.3705],[29.3835,47.345],[29.3985,47.3005],[29.4855,47.306],[29.4875,47.356],[29.574,47.3705],[29.5985,47.2575],[29.552,47.2515],[29.58,47.136],[29.612,47.1],[29.596,46.9635],[29.648,46.9195],[29.731,46.9195],[29.7495,46.861],[29.884,46.886],[29.906,46.8205],[29.9755,46.7555],[29.971,46.685],[29.946,46.6485],[29.9685,46.595],[29.9265,46.502],[29.9925,46.501],[30.0235,46.442],[30.11,46.4315],[30.0915,46.383],[29.9385,46.4005],[29.8865,46.3715],[29.805,46.3855],[29.7765,46.4545],[29.686,46.426],[29.6775,46.361],[29.5805,46.361],[29.566,46.4165],[29.507,46.423],[29.502,46.4615],[29.4485,46.4985],[29.387,46.4465],[29.308,46.465],[29.2965,46.417],[29.2465,46.416],[29.2295,46.4585],[29.236,46.5585],[29.162,46.5445],[29.164,46.5155],[29.0385,46.506],[29.0255,46.4625],[28.931,46.4575],[28.9865,46.343],[28.9525,46.2595],[29.067,46.1955],[28.9505,46.0935],[29.0055,46.0495],[28.979,46.0035],[28.7765,45.9705],[28.755,45.9275],[28.786,45.8325],[28.697,45.818],[28.7095,45.78],[28.6245,45.766],[28.5895,45.729],[28.5195,45.736],[28.4805,45.678],[28.516,45.6655],[28.544,45.58],[28.491,45.571],[28.515,45.5],[28.421,45.513],[28.305,45.547],[28.214,45.467],[28.2865,45.4335],[28.2855,45.3995],[28.349,45.3205],[28.569,45.248],[28.714,45.2235],[28.7925,45.2435],[28.8165,45.3365],[28.9145,45.2875],[29.044,45.3605],[29.179,45.4],[29.238,45.4325],[29.294,45.428],[29.4295,45.4425],[29.589,45.39],[29.651,45.3395],[29.6905,45.1915],[29.952,45.1695],[29.9955,45.1145],[30.0455,45.0865],[30.166,45.055],[30.2405,45.055],[30.378,45.095],[30.4685,45.178],[30.488,45.282],[30.431,45.3785],[30.313,45.442],[30.166,45.4555],[30.073,45.435],[30.0665,45.506],[30.193,45.5675],[30.3445,45.669],[30.4475,45.7275],[30.6385,45.911],[30.6675,45.96],[30.7355,45.986],[30.8275,46.0655],[30.929,46.1375],[30.968,46.2325],[31.03,46.3145],[31.0475,46.377],[31.159,46.4095],[31.2375,46.403],[31.242,46.27],[31.3085,46.1715],[31.3935,46.107],[31.4595,46.0765],[31.597,46.0395],[31.8925,45.997],[32.08,45.9475],[32.2475,45.9205],[32.447,45.8705],[32.528,45.8645],[32.7725,45.8265],[33.5405,46.012],[33.591,46.061],[33.572,46.1025],[33.6355,46.1465],[33.615,46.226],[33.736,46.186],[33.8115,46.204],[34.0525,46.109],[34.1175,46.1075],[34.1885,46.066],[34.248,46.053],[34.3295,46.0605],[34.405,46.0095],[34.443,45.962],[34.4985,45.9425],[34.561,45.9945],[34.67,45.9675],[34.7545,45.9085],[34.802,45.9005],[34.799,45.8105],[34.975,45.762],[35.2335,45.7915],[35.151,45.9315],[35.273,45.956],[35.3275,45.9855],[35.4325,46.091],[35.468,46.1465],[35.582,46.263],[35.6855,46.313],[35.7205,46.3485],[35.8445,46.41],[35.9355,46.336],[36.0795,46.3005],[36.1935,46.3115],[36.262,46.336],[36.3585,46.418],[36.4345,46.455],[36.499,46.528],[36.5455,46.5355],[36.644,46.478],[36.7095,46.463],[36.8125,46.463],[36.8925,46.484],[36.989,46.5505],[37.024,46.639],[37.098,46.69],[37.1685,46.7105],[37.297,46.6875],[37.4375,46.716],[37.62,46.83],[38.141,46.861],[38.338,46.981],[38.23,47.1195],[38.236,47.1985],[38.2605,47.2385],[38.3245,47.257],[38.336,47.3065],[38.221,47.3055],[38.2575,47.373],[38.3025,47.393],[38.2845,47.5445],[38.3505,47.6165],[38.457,47.617],[38.4565,47.644],[38.6165,47.6445],[38.669,47.699],[38.7725,47.6855],[38.789,47.816],[38.8375,47.8645],[38.884,47.875],[39.083,47.8695],[39.1495,47.8425],[39.2415,47.867],[39.3875,47.871],[39.412,47.831],[39.478,47.8605],[39.521,47.8255],[39.5655,47.8365],[39.7385,47.828],[39.7935,47.9195],[39.816,48.0],[39.7765,48.04],[39.8835,48.0415],[39.8765,48.119],[39.937,48.181],[39.942,48.2285],[40.021,48.254],[39.9905,48.317],[39.9365,48.291],[39.841,48.31],[39.8455,48.333],[39.946,48.352],[39.898,48.4475],[39.853,48.4665],[39.8555,48.561],[39.7875,48.5935],[39.688,48.587],[39.668,48.621],[39.7185,48.6875],[39.7255,48.7525],[39.779,48.7855],[39.807,48.8385],[39.9755,48.793],[40.08,48.87],[40.0585,48.904],[39.8525,48.8905],[39.7765,48.9205],[39.7495,48.978],[39.6635,49.001],[39.693,49.0505],[39.7665,49.04],[39.805,49.06],[39.9385,49.083],[40.031,49.18],[40.0775,49.187],[40.186,49.281],[40.196,49.3445],[40.1145,49.3855],[40.0295,49.4535],[40.039,49.5205],[40.1695,49.569],[40.1355,49.617],[39.952,49.597],[39.891,49.5585],[39.806,49.558],[39.7505,49.5975],[39.6605,49.615],[39.5915,49.7185],[39.4785,49.7565],[39.379,49.7385],[39.285,49.7555],[39.2275,49.8075],[39.18,49.889],[39.067,49.8155],[38.9515,49.797],[38.9095,49.8195],[38.901,49.8695],[38.848,49.8645],[38.745,49.8975],[38.724,49.927],[38.5825,49.9775],[38.4885,49.963],[38.3505,50.007],[38.3295,50.0855],[38.178,50.08],[38.186,50.023],[38.222,49.979],[38.171,49.942],[38.056,49.923],[37.962,49.983],[37.9265,50.034],[37.797,50.084],[37.755,50.0785],[37.639,50.1795],[37.615,50.217],[37.6265,50.2935],[37.485,50.357],[37.465,50.431],[37.333,50.437],[37.2925,50.4015],[37.1815,50.363],[37.1055,50.352],[36.9375,50.3505],[36.693,50.269],[36.6485,50.2175],[36.5605,50.251],[36.575,50.275],[36.4735,50.313],[36.3665,50.288],[36.297,50.292],[36.284,50.3355],[36.1605,50.4315],[36.068,50.451],[35.932,50.431],[35.8315,50.434],[35.738,50.3545],[35.6285,50.3545],[35.584,50.3955],[35.5865,50.448],[35.4755,50.489],[35.436,50.53],[35.4255,50.603],[35.392,50.642],[35.4595,50.689],[35.4885,50.777],[35.4105,50.808],[35.419,50.844],[35.388,50.9235],[35.333,50.9345],[35.33,50.996],[35.406,51.048],[35.305,51.076],[35.2135,51.0465],[35.166,51.0775],[35.168,51.1265],[35.1235,51.165],[35.1505,51.2235],[34.951,51.2265],[34.818,51.169],[34.669,51.1975],[34.6625,51.2475],[34.4865,51.2445],[34.384,51.274],[34.3315,51.24],[34.2445,51.2635],[34.2375,51.2925],[34.337,51.3655],[34.2855,51.375],[34.2225,51.4285],[34.308,51.5155],[34.1155,51.6845],[34.4325,51.7295],[34.4155,51.826],[34.2035,51.941],[34.1895,51.969],[34.094,52.011],[34.0625,52.073],[34.1165,52.141],[34.0545,52.1715],[33.875,52.3065],[33.8365,52.3615],[33.7855,52.3665],[33.6065,52.3345],[33.56,52.3025],[33.5045,52.306],[33.519,52.355],[33.289,52.3575],[33.208,52.376],[33.073,52.325],[32.996,52.2725],[32.8335,52.279],[32.763,52.2565],[32.685,52.2665],[32.55,52.327],[32.3515,52.3165],[32.3955,52.2465],[32.3245,52.2235],[32.3445,52.1825],[32.29,52.103],[32.1255,52.046],[31.9175,52.053],[31.954,52.0815],[31.8565,52.111],[31.7815,52.112],[31.722,52.096],[31.6585,52.1175],[31.466,52.1205],[31.4025,52.1425],[31.331,52.107],[31.2975,52.0515],[31.253,52.042],[31.1365,52.103],[30.9915,52.0775],[30.888,51.9685],[30.805,51.947],[30.707,51.8685],[30.6485,51.7505],[30.578,51.6805],[30.529,51.602],[30.5285,51.563],[30.6555,51.376],[30.618,51.288],[30.5385,51.2625],[30.4625,51.3065],[30.424,51.3055],[30.324,51.3605],[30.347,51.4235],[30.18,51.5125],[30.12,51.4875],[30.0355,51.504],[29.9715,51.475],[29.8935,51.4855],[29.7975,51.4465],[29.7495,51.457],[29.7165,51.53],[29.542,51.4825],[29.4955,51.397],[29.4215,51.415],[29.3405,51.3855],[29.309,51.454],[29.251,51.4955],[29.2665,51.531],[29.1775,51.6075],[29.1635,51.6525],[29.106,51.651],[28.9995,51.5735],[28.9125,51.59],[28.807,51.5485],[28.7615,51.4875],[28.772,51.4295],[28.688,51.4445],[28.6355,51.574],[28.559,51.573],[28.4715,51.5935],[28.3975,51.551],[28.3615,51.558],[28.287,51.6235],[28.2645,51.6815],[28.1735,51.644],[28.1175,51.583],[27.946,51.558],[27.9115,51.6125],[27.858,51.6305],[27.807,51.533],[27.742,51.4775],[27.6835,51.541],[27.7275,51.6045],[27.619,51.6055],[27.55,51.637],[27.393,51.605],[27.2675,51.606],[27.2735,51.6465],[27.213,51.6665],[27.207,51.774],[26.9935,51.7685],[26.9505,51.736],[26.8505,51.767],[26.8095,51.7565],[26.755,51.8035],[26.6115,51.8245],[26.5465,51.796],[26.4665,51.7995],[26.371,51.863],[26.1555,51.866],[26.093,51.9125],[25.99,51.932],[25.9205,51.916],[25.819,51.929],[25.708,51.916],[25.596,51.9235],[25.41,51.9215],[25.2635,51.9685],[25.1005,51.9535],[24.934,51.891],[24.81,51.912],[24.7495,51.881],[24.625,51.9025],[24.5675,51.8895],[24.388,51.882],[24.3,51.814],[24.3195,51.751],[24.2685,51.716],[24.1205,51.6665],[24.0755,51.619],[23.9955,51.5805],[23.8805,51.6085],[23.867,51.643],[23.7825,51.6675],[23.6785,51.653],[23.6055,51.617],[23.6645,51.577],[23.618,51.508],[23.6715,51.475],[23.6975,51.403],[23.636,51.319],[23.695,51.287],[23.7265,51.238],[23.8215,51.1645],[23.8765,51.079],[23.9985,50.9275],[24.099,50.877],[24.0945,50.836],[23.99,50.8375],[23.9575,50.795],[24.0095,50.772],[24.0185,50.7255],[24.0715,50.7205],[24.0985,50.5995],[24.07,50.5035],[23.9975,50.412],[23.8035,50.405],[23.703,50.3755],[23.6865,50.3315],[23.639,50.3205],[23.581,50.266],[23.47,50.218],[23.2795,50.0865],[23.0335,49.8815],[22.9705,49.8385],[22.848,49.71],[22.641,49.53],[22.6965,49.495],[22.747,49.36],[22.7475,49.2165],[22.7075,49.175],[22.789,49.158],[22.8645,49.0665],[22.8915,49.008],[22.7655,49.0535],[22.6395,49.0595],[22.5655,49.088]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/49/Flag_of_Ukraine.svg","name:en":"Ukraine","wikidata":"Q212","ISO3166-1:alpha2":"UA","ISO3166-1:alpha3":"UKR","ISO3166-1:numeric":"804"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[100.0845,20.3525],[100.047,20.3915],[99.9905,20.4235],[99.9655,20.4625],[99.862,20.4435],[99.81,20.3365],[99.73,20.35],[99.6815,20.316],[99.647,20.3415],[99.6115,20.3325],[99.5195,20.3615],[99.472,20.394],[99.4555,20.363],[99.507,20.326],[99.523,20.233],[99.5625,20.207],[99.5375,20.147],[99.499,20.118],[99.4,20.086],[99.3445,20.105],[99.242,20.114],[99.159,20.133],[99.1085,20.098],[99.077,20.101],[99.044,20.0355],[99.045,19.9485],[99.0235,19.9195],[99.034,19.8555],[98.9845,19.739],[98.9315,19.774],[98.83,19.796],[98.7775,19.752],[98.6725,19.752],[98.608,19.709],[98.519,19.715],[98.474,19.695],[98.3715,19.69],[98.3205,19.697],[98.2485,19.6765],[98.238,19.708],[98.1355,19.788],[98.0835,19.81],[98.036,19.801],[98.0245,19.722],[98.026,19.6375],[97.983,19.6405],[97.975,19.602],[97.8635,19.577],[97.8555,19.531],[97.876,19.5065],[97.792,19.395],[97.822,19.329],[97.8415,19.225],[97.8355,19.093],[97.77,19.0735],[97.736,19.0405],[97.7385,18.9785],[97.6905,18.9485],[97.697,18.9045],[97.737,18.887],[97.739,18.8285],[97.774,18.689],[97.768,18.578],[97.6505,18.577],[97.605,18.537],[97.5495,18.531],[97.535,18.491],[97.4905,18.51],[97.444,18.495],[97.426,18.5465],[97.393,18.561],[97.346,18.546],[97.387,18.5055],[97.4265,18.408],[97.452,18.39],[97.444,18.3405],[97.502,18.2675],[97.5365,18.286],[97.551,18.334],[97.62,18.318],[97.6395,18.2875],[97.6135,18.225],[97.676,18.1615],[97.677,18.115],[97.7365,17.9865],[97.7315,17.9525],[97.6875,17.925],[97.672,17.8825],[97.718,17.8195],[97.794,17.6795],[97.9065,17.5835],[97.923,17.5395],[97.9925,17.51],[98.0575,17.438],[98.083,17.3905],[98.1105,17.3855],[98.113,17.319],[98.1755,17.2585],[98.248,17.2085],[98.2875,17.1565],[98.2795,17.1155],[98.347,17.048],[98.3935,17.059],[98.4375,17.037],[98.49,16.9685],[98.5345,16.873],[98.536,16.8295],[98.4855,16.7895],[98.464,16.7325],[98.5115,16.701],[98.5115,16.649],[98.574,16.6205],[98.579,16.557],[98.658,16.455],[98.637,16.4255],[98.6575,16.3785],[98.676,16.2805],[98.7355,16.338],[98.817,16.385],[98.8285,16.4135],[98.8825,16.4225],[98.9135,16.393],[98.9315,16.329],[98.9035,16.2545],[98.852,16.214],[98.8515,16.1365],[98.8055,16.1045],[98.7,16.136],[98.625,16.0425],[98.599,16.0735],[98.5605,16.034],[98.6085,15.9725],[98.584,15.9185],[98.609,15.8735],[98.577,15.8],[98.5485,15.685],[98.553,15.6015],[98.5855,15.471],[98.5805,15.3755],[98.5375,15.371],[98.4895,15.3935],[98.471,15.371],[98.3945,15.3425],[98.4175,15.2665],[98.394,15.2625],[98.306,15.3135],[98.252,15.225],[98.198,15.2345],[98.2125,15.178],[98.1835,15.0965],[98.2315,15.0445],[98.2085,14.9805],[98.2335,14.9665],[98.257,14.8635],[98.246,14.831],[98.2945,14.77],[98.3235,14.7105],[98.436,14.6085],[98.487,14.536],[98.524,14.4555],[98.5495,14.4345],[98.597,14.3275],[98.631,14.316],[98.756,14.2145],[98.835,14.172],[98.8785,14.1235],[98.968,14.0825],[98.972,14.033],[98.9985,14.0195],[99.021,13.9455],[99.077,13.895],[99.1215,13.7735],[99.167,13.726],[99.1725,13.563],[99.207,13.3805],[99.187,13.291],[99.2105,13.2075],[99.139,13.2055],[99.1275,13.103],[99.0995,13.0725],[99.18,12.9935],[99.1725,12.943],[99.1885,12.847],[99.218,12.8285],[99.2325,12.74],[99.2895,12.715],[99.2945,12.678],[99.348,12.6395],[99.4125,12.6155],[99.4355,12.566],[99.401,12.46],[99.442,12.4005],[99.434,12.343],[99.4715,12.2345],[99.515,12.1475],[99.5705,12.145],[99.534,12.0655],[99.538,12.014],[99.592,11.9925],[99.58,11.9065],[99.625,11.8315],[99.659,11.829],[99.638,11.7325],[99.5835,11.6795],[99.5655,11.6265],[99.5165,11.637],[99.4675,11.615],[99.4745,11.533],[99.4405,11.4795],[99.3935,11.461],[99.402,11.3885],[99.367,11.3725],[99.3125,11.3165],[99.3245,11.282],[99.2635,11.215],[99.229,11.113],[99.19,11.084],[99.1585,11.034],[99.104,11.013],[99.077,10.954],[99.024,10.9705],[98.9975,10.911],[99.0115,10.8565],[98.959,10.812],[98.913,10.81],[98.8615,10.7785],[98.7795,10.678],[98.774,10.6165],[98.81,10.5815],[98.817,10.4985],[98.795,10.445],[98.7555,10.393],[98.743,10.3245],[98.7075,10.231],[98.6705,10.1705],[98.5795,9.9865],[98.567,9.9435],[98.519,9.926],[98.471,9.9565],[98.3665,9.943],[98.3345,9.891],[98.3215,9.832],[98.275,9.7505],[98.2175,9.5655],[98.1185,9.4415],[98.112,9.3485],[98.077,9.283],[98.0475,9.127],[98.0475,9.017],[98.017,8.9185],[98.0345,8.836],[98.017,8.7405],[98.0315,8.668],[98.004,8.55],[98.019,8.474],[98.0705,8.282],[98.0895,8.1595],[98.07,8.0765],[98.07,8.009],[98.0545,7.9325],[98.0595,7.8845],[98.102,7.6945],[98.1235,7.552],[98.104,7.479],[98.1145,7.399],[98.155,7.332],[98.2155,7.2835],[98.2915,7.261],[98.369,7.272],[98.4855,7.372],[98.568,7.5405],[98.693,7.4665],[98.698,7.361],[98.764,7.267],[98.856,7.2485],[98.8555,7.1685],[98.8835,7.0925],[98.934,7.037],[99.0035,7.0015],[99.083,6.9995],[99.1955,7.0445],[99.215,6.983],[99.288,6.8835],[99.3015,6.775],[99.247,6.7835],[99.1165,6.7625],[99.006,6.6765],[98.95,6.5505],[98.969,6.449],[99.0105,6.3825],[99.068,6.3255],[99.1115,6.3],[99.3215,6.2715],[99.4585,6.3065],[99.5115,6.4815],[99.5565,6.5035],[99.689,6.4715],[99.812,6.5125],[99.917,6.5235],[100.014,6.442],[100.076,6.406],[100.1335,6.429],[100.159,6.4805],[100.154,6.5575],[100.1705,6.6075],[100.1715,6.694],[100.29,6.689],[100.326,6.6595],[100.316,6.604],[100.3665,6.5395],[100.496,6.5215],[100.5255,6.4875],[100.564,6.495],[100.618,6.46],[100.6625,6.451],[100.7395,6.492],[100.8125,6.442],[100.835,6.2965],[100.904,6.234],[100.984,6.279],[101.015,6.247],[101.0945,6.2605],[101.1265,6.1925],[101.0755,6.171],[101.0585,6.1415],[101.119,6.1145],[101.1005,6.0495],[101.107,5.9905],[101.087,5.911],[101.037,5.918],[100.985,5.81],[101.034,5.738],[101.06,5.7445],[101.111,5.681],[101.136,5.613],[101.215,5.6615],[101.26,5.7115],[101.2495,5.7625],[101.276,5.8115],[101.343,5.8095],[101.397,5.8725],[101.4845,5.87],[101.5405,5.919],[101.5815,5.9345],[101.6625,5.8685],[101.658,5.817],[101.691,5.756],[101.7535,5.797],[101.7935,5.7505],[101.88,5.832],[101.944,5.8725],[101.9305,5.9095],[101.9425,5.981],[102.0035,6.0505],[102.057,6.0855],[102.0875,6.141],[102.0805,6.2255],[102.1665,6.4585],[102.2595,6.6535],[102.1985,7.349],[102.1745,7.427],[102.129,7.4835],[101.5025,8.0125],[100.8995,8.521],[100.5955,9.0935],[100.295,9.6585],[100.282,9.786],[100.262,9.846],[100.179,10.026],[100.152,10.069],[99.9815,10.2725],[99.6225,10.5945],[99.703,10.661],[99.7315,10.717],[99.746,10.782],[99.719,10.9435],[99.758,11.011],[99.763,11.1165],[99.786,11.214],[99.7785,11.265],[99.8355,11.4285],[99.886,11.4525],[99.948,11.5145],[99.978,11.616],[100.0225,11.68],[100.0395,11.7515],[100.0385,11.857],[100.074,11.9315],[100.1475,12.009],[100.227,12.1505],[100.233,12.2525],[100.207,12.3085],[100.2045,12.396],[100.792,12.396],[100.8125,12.367],[100.8785,12.3235],[100.9565,12.3085],[101.053,12.3295],[101.119,12.3725],[101.163,12.437],[101.2485,12.446],[101.283,12.3975],[101.3495,12.3425],[101.4405,12.3205],[101.5225,12.3305],[101.57,12.354],[101.649,12.3685],[101.748,12.372],[101.7895,12.3865],[101.846,12.338],[102.051,12.209],[102.038,12.0635],[102.053,11.9755],[102.11,11.8445],[102.2275,11.652],[102.378,11.4675],[102.4775,11.4095],[102.5705,11.388],[102.621,11.3895],[102.914,11.6545],[102.9115,11.7455],[102.8665,11.8065],[102.8195,11.8925],[102.815,11.9335],[102.785,11.9765],[102.769,12.0755],[102.734,12.111],[102.705,12.18],[102.7175,12.2225],[102.726,12.3645],[102.7875,12.4155],[102.7655,12.457],[102.6775,12.543],[102.6435,12.612],[102.567,12.658],[102.511,12.67],[102.5005,12.7385],[102.5325,12.7675],[102.508,12.8975],[102.488,12.944],[102.494,13.014],[102.399,13.1565],[102.386,13.218],[102.3495,13.2775],[102.345,13.347],[102.3625,13.4245],[102.363,13.506],[102.334,13.5405],[102.3665,13.578],[102.439,13.56],[102.5375,13.567],[102.57,13.586],[102.5715,13.635],[102.549,13.6595],[102.5805,13.7005],[102.6885,13.752],[102.7665,13.8565],[102.783,13.933],[102.869,14.005],[102.9085,14.021],[102.9005,14.0835],[102.941,14.1525],[102.952,14.207],[103.022,14.2285],[103.079,14.294],[103.1885,14.3345],[103.219,14.3235],[103.2665,14.3495],[103.402,14.358],[103.435,14.391],[103.4675,14.3645],[103.497,14.4105],[103.578,14.4335],[103.642,14.4145],[103.6555,14.4395],[103.707,14.433],[103.7005,14.392],[103.78,14.3615],[103.837,14.371],[103.9,14.338],[104.0095,14.356],[104.029,14.34],[104.0935,14.3535],[104.108,14.3785],[104.1715,14.359],[104.217,14.3675],[104.2775,14.408],[104.307,14.383],[104.4765,14.361],[104.5,14.3765],[104.5645,14.354],[104.641,14.423],[104.6985,14.4275],[104.7525,14.4055],[104.806,14.4375],[104.8335,14.4025],[104.8795,14.412],[105.0025,14.362],[105.0005,14.305],[105.026,14.238],[105.05,14.215],[105.1025,14.213],[105.154,14.2545],[105.151,14.2835],[105.2065,14.3435],[105.2815,14.3535],[105.322,14.398],[105.36,14.3875],[105.4305,14.4245],[105.5235,14.5495],[105.531,14.6225],[105.512,14.801],[105.5765,14.897],[105.557,14.9245],[105.6195,14.98],[105.573,15.001],[105.481,15.0985],[105.4745,15.1775],[105.54,15.252],[105.591,15.271],[105.576,15.3225],[105.5085,15.3185],[105.4675,15.3475],[105.491,15.3845],[105.5785,15.408],[105.6025,15.4805],[105.5935,15.5125],[105.625,15.5635],[105.6345,15.6625],[105.596,15.7245],[105.497,15.767],[105.4335,15.7555],[105.3905,15.8085],[105.342,15.922],[105.408,16.016],[105.195,16.055],[105.0405,16.11],[105.011,16.202],[105.015,16.244],[104.9205,16.3375],[104.895,16.343],[104.83,16.4645],[104.768,16.5],[104.735,16.5455],[104.744,16.6375],[104.7625,16.6895],[104.738,16.794],[104.759,16.8415],[104.733,16.9365],[104.7295,17.0085],[104.8015,17.1645],[104.804,17.3715],[104.796,17.402],[104.744,17.4605],[104.708,17.521],[104.4965,17.635],[104.457,17.667],[104.355,17.8225],[104.271,17.87],[104.213,17.9995],[104.1055,18.1155],[104.057,18.2235],[103.973,18.337],[103.953,18.3395],[103.856,18.2845],[103.834,18.328],[103.794,18.343],[103.6955,18.344],[103.6035,18.405],[103.524,18.4255],[103.457,18.425],[103.4035,18.4485],[103.313,18.4335],[103.2465,18.3705],[103.281,18.2935],[103.1685,18.2575],[103.136,18.1605],[103.0835,18.129],[103.073,18.0225],[103.042,17.9825],[103.009,17.9765],[102.969,18.004],[102.862,17.974],[102.7805,17.929],[102.766,17.8995],[102.679,17.8565],[102.667,17.8055],[102.589,17.8495],[102.613,17.9075],[102.594,17.96],[102.46,17.971],[102.341,18.0465],[102.3,18.053],[102.1855,18.148],[102.1645,18.2025],[102.0885,18.2205],[102.045,18.1985],[102.0345,18.1615],[101.9455,18.0875],[101.907,18.031],[101.8745,18.0285],[101.786,18.071],[101.766,18.054],[101.7105,17.902],[101.618,17.894],[101.6025,17.857],[101.5645,17.841],[101.548,17.7875],[101.456,17.747],[101.3455,17.659],[101.3065,17.6505],[101.2645,17.6035],[101.2295,17.5365],[101.175,17.5235],[101.161,17.467],[101.08,17.5025],[101.0215,17.557],[100.9675,17.5715],[100.9915,17.619],[101.007,17.732],[100.978,17.7535],[101.0285,17.829],[101.02,17.8925],[101.1165,17.951],[101.1215,18.0],[101.184,18.065],[101.161,18.1245],[101.1925,18.212],[101.166,18.217],[101.161,18.3045],[101.1825,18.34],[101.083,18.3845],[101.0525,18.4265],[101.0915,18.478],[101.1035,18.5215],[101.1835,18.5605],[101.182,18.612],[101.225,18.6235],[101.2725,18.6885],[101.2335,18.72],[101.2585,18.8095],[101.246,18.8895],[101.297,18.943],[101.2925,18.981],[101.3355,19.004],[101.3555,19.051],[101.257,19.124],[101.245,19.181],[101.2595,19.231],[101.187,19.3995],[101.215,19.4315],[101.2105,19.4735],[101.264,19.4705],[101.2805,19.5825],[101.2065,19.605],[101.198,19.577],[101.123,19.572],[101.036,19.6285],[100.9125,19.627],[100.883,19.606],[100.779,19.4925],[100.7405,19.52],[100.6505,19.5565],[100.608,19.547],[100.5805,19.499],[100.4855,19.5435],[100.478,19.6125],[100.428,19.6725],[100.446,19.7065],[100.404,19.7525],[100.438,19.791],[100.4425,19.8355],[100.501,19.8705],[100.57,20.099],[100.5765,20.17],[100.5075,20.151],[100.456,20.225],[100.4185,20.25],[100.374,20.352],[100.3305,20.398],[100.2725,20.4005],[100.227,20.3555],[100.2175,20.3145],[100.181,20.3065],[100.1715,20.246],[100.113,20.25],[100.0845,20.3525]]],[[[97.7245,9.6005],[97.642,9.4965],[97.6265,9.4205],[97.642,9.3445],[97.703,9.25],[97.665,9.2245],[97.621,9.16],[97.6055,9.0685],[97.6205,8.9925],[97.645,8.956],[97.604,8.872],[97.5635,8.864],[97.498,8.821],[97.4545,8.7565],[97.428,8.5765],[97.4525,8.409],[97.4965,8.3445],[97.574,8.2895],[97.651,8.2745],[97.728,8.2895],[97.7935,8.3325],[97.837,8.3975],[97.858,8.516],[97.8455,8.583],[97.856,8.635],[97.9425,8.6835],[97.986,8.7485],[98.0025,8.8285],[97.987,8.9045],[97.9665,8.935],[98.0045,8.991],[98.02,9.067],[98.0045,9.143],[97.969,9.1975],[98.009,9.224],[98.082,9.3195],[98.106,9.44],[98.042,9.4835],[97.8535,9.5815],[97.7245,9.6005]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/a/a9/Flag_of_Thailand.svg","name:en":"Thailand","wikidata":"Q869","ISO3166-1:alpha2":"TH","ISO3166-1:alpha3":"THA","ISO3166-1:numeric":"764"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[102.1445,22.4],[102.182,22.3235],[102.2345,22.2825],[102.2465,22.2365],[102.3125,22.208],[102.434,22.1125],[102.4455,22.0745],[102.5095,22.0365],[102.4875,21.997],[102.514,21.9665],[102.5945,21.925],[102.642,21.849],[102.6615,21.7865],[102.648,21.7355],[102.6665,21.678],[102.7395,21.6595],[102.8265,21.736],[102.817,21.84],[102.8515,21.8425],[102.8565,21.722],[102.8855,21.712],[102.973,21.7365],[102.9885,21.721],[102.9815,21.647],[102.992,21.586],[102.8865,21.4765],[102.88,21.4245],[102.896,21.385],[102.897,21.3045],[102.8515,21.2985],[102.8585,21.2565],[102.912,21.2295],[102.909,21.1805],[102.9495,21.139],[102.962,21.0775],[103.0345,21.0585],[103.1095,20.9005],[103.168,20.9135],[103.2145,20.8995],[103.227,20.8515],[103.261,20.8235],[103.2945,20.835],[103.39,20.784],[103.4415,20.794],[103.463,20.826],[103.5095,20.753],[103.6015,20.7425],[103.6675,20.6985],[103.6835,20.6615],[103.735,20.667],[103.7335,20.7315],[103.791,20.7465],[103.7805,20.8015],[103.9015,20.9035],[103.9615,20.8975],[104.0485,20.9115],[104.054,20.95],[104.1215,20.9705],[104.2815,20.925],[104.3335,20.881],[104.3375,20.856],[104.428,20.79],[104.483,20.767],[104.4935,20.718],[104.552,20.719],[104.5855,20.6805],[104.6415,20.6625],[104.601,20.6055],[104.5415,20.5985],[104.5225,20.549],[104.376,20.467],[104.4135,20.423],[104.421,20.3765],[104.4745,20.369],[104.565,20.4165],[104.611,20.423],[104.6255,20.462],[104.6615,20.4745],[104.7095,20.4105],[104.6175,20.3615],[104.6105,20.275],[104.63,20.2295],[104.719,20.201],[104.7745,20.1985],[104.862,20.1415],[104.9135,20.1555],[104.9765,20.08],[104.907,19.9985],[104.903,19.968],[104.8465,19.9295],[104.835,19.802],[104.796,19.7905],[104.7535,19.75],[104.7125,19.754],[104.6465,19.6545],[104.567,19.6155],[104.506,19.626],[104.49,19.6555],[104.4275,19.673],[104.4125,19.705],[104.3515,19.6925],[104.236,19.7075],[104.105,19.662],[104.072,19.682],[104.0605,19.613],[104.1055,19.568],[104.073,19.4505],[104.0145,19.402],[103.958,19.384],[103.9085,19.3295],[103.924,19.2855],[104.003,19.233],[104.068,19.248],[104.128,19.205],[104.179,19.1975],[104.2125,19.1295],[104.272,19.1215],[104.3045,19.0935],[104.3975,19.055],[104.4455,18.984],[104.496,19.004],[104.545,18.97],[104.581,18.901],[104.63,18.886],[104.739,18.801],[104.7945,18.783],[104.913,18.787],[104.941,18.7395],[105.018,18.7455],[105.052,18.7085],[105.1065,18.7065],[105.198,18.6405],[105.1805,18.6025],[105.136,18.593],[105.115,18.539],[105.1035,18.4475],[105.1715,18.392],[105.192,18.319],[105.2425,18.2965],[105.256,18.255],[105.3315,18.2525],[105.3195,18.2015],[105.3635,18.164],[105.409,18.154],[105.46,18.206],[105.5015,18.1885],[105.5225,18.1255],[105.5805,18.062],[105.6425,17.9625],[105.608,17.877],[105.6605,17.8565],[105.701,17.7615],[105.78,17.675],[105.862,17.6275],[106.0815,17.3715],[106.2355,17.25],[106.305,17.258],[106.328,17.18],[106.373,17.112],[106.416,17.0785],[106.402,17.053],[106.4275,17.008],[106.5155,16.9715],[106.548,16.9285],[106.5145,16.896],[106.5515,16.865],[106.5515,16.6945],[106.565,16.643],[106.672,16.5625],[106.6475,16.5405],[106.6605,16.4745],[106.726,16.425],[106.772,16.4325],[106.8065,16.4785],[106.831,16.549],[106.8865,16.522],[106.8785,16.4685],[106.8975,16.391],[106.964,16.355],[106.9685,16.307],[107.0865,16.3125],[107.157,16.2565],[107.1465,16.22],[107.16,16.1675],[107.1975,16.144],[107.242,16.1475],[107.2835,16.1185],[107.3385,16.0555],[107.3775,16.074],[107.4455,16.054],[107.464,16.016],[107.4155,15.912],[107.393,15.887],[107.347,15.8935],[107.215,15.824],[107.2755,15.704],[107.291,15.624],[107.3365,15.6215],[107.365,15.57],[107.386,15.4925],[107.471,15.4965],[107.513,15.473],[107.5305,15.4055],[107.606,15.3885],[107.6245,15.311],[107.6215,15.2145],[107.6025,15.1835],[107.623,15.1075],[107.598,15.088],[107.583,15.0285],[107.54,15.0495],[107.468,14.9875],[107.4785,14.9575],[107.5895,14.864],[107.5075,14.7925],[107.542,14.7505],[107.5565,14.686],[107.5595,14.623],[107.5225,14.588],[107.532,14.5465],[107.488,14.406],[107.4465,14.403],[107.391,14.3265],[107.4095,14.2645],[107.3775,14.1795],[107.3385,14.128],[107.367,14.0835],[107.3655,14.013],[107.3865,13.9895],[107.4555,13.98],[107.4515,13.7945],[107.536,13.7405],[107.5705,13.6625],[107.5745,13.6205],[107.617,13.5265],[107.6275,13.3665],[107.548,13.17],[107.496,13.028],[107.5045,12.976],[107.4845,12.9445],[107.492,12.8975],[107.562,12.7925],[107.559,12.716],[107.582,12.6795],[107.5695,12.642],[107.5895,12.5595],[107.58,12.4955],[107.5435,12.3995],[107.544,12.351],[107.447,12.291],[107.434,12.246],[107.387,12.271],[107.363,12.3195],[107.268,12.3245],[107.2305,12.296],[107.1555,12.278],[107.14,12.2335],[107.1005,12.2065],[107.054,12.1425],[106.99,12.085],[106.927,12.0635],[106.7915,12.0815],[106.724,11.9755],[106.496,11.969],[106.4555,11.989],[106.412,11.9735],[106.465,11.8725],[106.418,11.77],[106.4535,11.6905],[106.4405,11.669],[106.3685,11.697],[106.3125,11.674],[106.2495,11.7285],[106.19,11.754],[106.1155,11.743],[106.069,11.776],[106.024,11.7745],[106.016,11.7285],[105.9535,11.6405],[105.884,11.677],[105.8525,11.662],[105.8095,11.5965],[105.8625,11.5655],[105.8855,11.5325],[105.874,11.4055],[105.885,11.361],[105.8755,11.2875],[105.9415,11.2055],[106.014,11.1905],[106.023,11.1405],[106.1215,11.0885],[106.154,11.102],[106.188,11.0505],[106.206,10.978],[106.154,10.983],[106.14,10.915],[106.1865,10.842],[106.1655,10.8115],[106.0725,10.811],[105.9725,10.8935],[105.9,10.8445],[105.8485,10.864],[105.8645,10.8995],[105.807,10.9735],[105.7795,11.0315],[105.73,11.0255],[105.6675,10.9875],[105.5365,10.952],[105.4955,10.9485],[105.4335,10.9725],[105.344,10.8665],[105.265,10.897],[105.2385,10.893],[105.1575,10.9225],[105.1075,10.9185],[105.083,10.9565],[105.0295,10.8925],[105.0655,10.7805],[105.0985,10.721],[104.957,10.638],[104.897,10.547],[104.869,10.522],[104.753,10.518],[104.6945,10.537],[104.588,10.5275],[104.5505,10.4555],[104.439,10.423],[104.2275,10.2715],[104.1245,10.232],[104.1105,10.3835],[104.0885,10.4205],[104.011,10.4805],[103.9715,10.48],[103.9265,10.4305],[103.914,10.3905],[103.838,10.402],[103.8005,10.3675],[103.8265,10.291],[103.901,10.2615],[103.935,10.154],[103.86,10.0045],[103.7785,9.949],[103.118,9.4755],[103.125,9.38],[103.1655,9.2775],[103.292,9.125],[103.3445,9.0785],[103.482,8.9945],[104.7675,8.209],[104.8325,8.183],[104.9025,8.181],[105.674,8.291],[106.0555,8.3455],[106.653,8.431],[106.8,8.486],[108.431,9.378],[109.1805,9.7905],[109.26,9.868],[109.284,9.9385],[109.402,10.763],[109.5375,11.7075],[109.666,12.602],[109.671,12.6605],[109.657,12.9165],[109.585,13.623],[109.554,13.926],[109.463,14.605],[109.3555,15.411],[109.3365,15.4725],[109.2985,15.525],[109.0235,15.7975],[108.6565,16.1665],[108.4805,16.334],[107.6885,17.1145],[107.4915,17.3075],[107.4325,17.348],[107.3625,17.3665],[107.2905,17.3605],[107.2245,17.3315],[107.173,17.2825],[106.9465,17.453],[106.8175,17.579],[106.73,17.718],[106.8275,17.775],[106.8665,17.831],[106.884,17.896],[106.882,17.947],[106.839,18.0385],[106.6725,18.217],[106.5285,18.315],[106.34,18.4225],[106.3035,18.4625],[106.23,18.5025],[106.1555,18.512],[106.0815,18.6225],[106.127,18.6605],[106.1635,18.72],[106.177,18.7875],[106.171,18.842],[106.1405,18.9085],[106.0425,18.9875],[105.9335,19.0055],[105.9485,19.121],[106.0145,19.1435],[106.0725,19.1875],[106.114,19.2385],[106.1415,19.3015],[106.149,19.376],[106.1365,19.445],[106.1005,19.5065],[106.046,19.552],[106.048,19.581],[106.1255,19.693],[106.1745,19.7025],[106.244,19.7375],[106.4705,19.9905],[106.5445,19.992],[106.628,20.009],[106.7425,20.069],[106.786,20.127],[106.8055,20.183],[106.839,20.337],[106.8355,20.4655],[106.954,20.5005],[107.0065,20.451],[107.0745,20.421],[107.129,20.412],[107.282,20.4245],[107.574,20.5495],[107.6445,20.615],[107.7205,20.7435],[107.8165,20.756],[107.959,20.8305],[108.048,20.927],[108.1865,21.063],[108.2235,21.1235],[108.2085,21.2095],[108.1345,21.2755],[108.0955,21.4525],[108.1005,21.4735],[108.0335,21.5475],[107.955,21.537],[107.9425,21.57],[107.8965,21.596],[107.872,21.647],[107.8155,21.6585],[107.6885,21.6115],[107.594,21.605],[107.5435,21.5895],[107.4935,21.605],[107.4825,21.657],[107.361,21.612],[107.36,21.6645],[107.301,21.7425],[107.249,21.7055],[107.1965,21.7225],[107.0855,21.808],[107.0045,21.8335],[107.0635,21.896],[107.05,21.928],[106.988,21.9485],[106.9335,21.9295],[106.919,21.9745],[106.8125,21.973],[106.7845,22.004],[106.6825,22.002],[106.71,22.1015],[106.673,22.181],[106.6985,22.2095],[106.69,22.279],[106.6595,22.334],[106.5715,22.3415],[106.5855,22.378],[106.5575,22.459],[106.5825,22.4825],[106.607,22.5985],[106.6485,22.5765],[106.6935,22.5845],[106.753,22.6925],[106.7645,22.7415],[106.833,22.797],[106.783,22.8145],[106.6915,22.8915],[106.6485,22.867],[106.593,22.933],[106.567,22.919],[106.513,22.9485],[106.5005,22.909],[106.4405,22.8895],[106.2865,22.8675],[106.238,22.9515],[106.2,22.9865],[106.001,22.992],[106.003,22.9475],[105.8725,22.9335],[105.825,22.9965],[105.645,23.083],[105.5695,23.0755],[105.5735,23.161],[105.543,23.1935],[105.499,23.2025],[105.445,23.2955],[105.4115,23.288],[105.3515,23.345],[105.322,23.393],[105.2585,23.3195],[105.2275,23.26],[105.179,23.288],[105.107,23.246],[105.082,23.267],[105.025,23.219],[104.9625,23.1985],[104.9275,23.1565],[104.8815,23.166],[104.876,23.1255],[104.802,23.116],[104.8315,23.0035],[104.8655,22.9625],[104.8395,22.9215],[104.771,22.8955],[104.7355,22.8275],[104.6735,22.8185],[104.5785,22.855],[104.478,22.7635],[104.353,22.6935],[104.3395,22.723],[104.269,22.743],[104.256,22.845],[104.115,22.8115],[104.046,22.734],[104.029,22.6875],[104.0125,22.524],[103.962,22.5065],[103.826,22.6155],[103.767,22.6905],[103.6385,22.795],[103.564,22.706],[103.5795,22.664],[103.531,22.595],[103.489,22.6185],[103.4265,22.713],[103.44,22.7535],[103.372,22.7995],[103.3205,22.7805],[103.2875,22.7375],[103.2835,22.684],[103.1855,22.645],[103.14,22.5405],[103.0725,22.4915],[103.0805,22.451],[103.032,22.4425],[102.9235,22.507],[102.88,22.5605],[102.8835,22.589],[102.8255,22.6265],[102.789,22.6255],[102.689,22.703],[102.656,22.6895],[102.6075,22.732],[102.5665,22.722],[102.5105,22.7785],[102.452,22.7525],[102.434,22.7],[102.384,22.6795],[102.4045,22.6335],[102.346,22.583],[102.2495,22.461],[102.1445,22.4]]],[[[107.504,20.143],[107.5255,20.0405],[107.595,19.9635],[107.7065,19.9275],[107.8185,19.944],[107.88,19.9835],[107.9245,20.041],[107.9445,20.091],[107.9455,20.1925],[107.9155,20.2575],[107.8635,20.308],[107.7955,20.3385],[107.721,20.3455],[107.6305,20.318],[107.54,20.242],[107.504,20.143]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/21/Flag_of_Vietnam.svg","name:en":"Vietnam","wikidata":"Q881","ISO3166-1:alpha2":"VN","ISO3166-1:alpha3":"VNM","ISO3166-1:numeric":"704"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[114.288,10.347],[114.336,10.3265],[114.4235,10.349],[114.427,10.399],[114.3985,10.4405],[114.329,10.4365],[114.294,10.4155],[114.288,10.347]]],[[[116.5795,20.697],[116.5895,20.6325],[116.622,20.5755],[116.673,20.532],[116.7375,20.5075],[116.8065,20.5055],[116.8725,20.5255],[116.927,20.565],[116.964,20.62],[116.978,20.6835],[116.9685,20.7475],[116.936,20.805],[116.885,20.848],[116.8205,20.8725],[116.7515,20.8745],[116.6855,20.855],[116.631,20.815],[116.594,20.76],[116.5795,20.697]]],[[[119.103,23.4],[119.115,23.2985],[119.153,23.205],[119.27,23.054],[120.202,22.181],[120.661,21.6295],[120.739,21.573],[120.815,21.5575],[120.861,21.563],[121.648,21.7505],[121.694,21.7655],[121.759,21.8085],[121.803,21.8735],[121.816,21.9775],[121.717,22.698],[121.709,23.454],[122.041,24.412],[122.089,24.5485],[122.196,24.9695],[122.293,25.4395],[122.294,25.514],[122.26,25.7015],[122.216,25.7665],[122.141,25.8145],[122.065,25.83],[121.962,25.8015],[121.408,25.4685],[120.995,25.246],[120.884,25.163],[119.406,23.9405],[119.361,23.8885],[119.118,23.4765],[119.103,23.4]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/72/Flag_of_the_Republic_of_China.svg","name:en":"Taiwan","wikidata":"Q865","ISO3166-1:alpha2":"TW","ISO3166-1:alpha3":"TWN","ISO3166-1:numeric":"158"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[124.4165,38.05],[124.3795,38.05],[124.3725,37.91],[124.411,37.84],[124.416,37.7425],[124.4815,37.657],[124.575,37.5975],[124.6595,37.5685],[124.784,37.5665],[124.8775,37.5885],[124.941,37.6325],[124.9775,37.6875],[124.8665,37.7665],[124.85,38.0],[124.6335,38.05],[124.4165,38.05]]],[[[125.4265,37.649],[125.471,37.527],[125.5525,37.4455],[125.718,37.3925],[125.868,37.4095],[125.7525,37.302],[125.713,37.181],[125.635,37.17],[125.561,37.1155],[125.517,37.0595],[125.3045,36.6975],[125.2885,36.5915],[125.339,36.4935],[125.751,36.015],[125.8105,35.8795],[125.836,35.797],[125.848,35.678],[125.8,35.5475],[125.776,35.4495],[125.5695,35.295],[125.0055,34.852],[124.9385,34.7325],[124.8365,34.109],[124.8565,34.003],[124.9015,33.9415],[124.9745,33.8845],[125.061,33.852],[125.417,33.7965],[126.059,33.7],[126.057,33.66],[126.0815,33.581],[126.0355,33.544],[125.925,33.407],[125.906,33.3465],[125.908,33.2725],[125.9255,33.232],[126.01,33.119],[126.072,32.9925],[126.143,32.9385],[126.227,32.911],[126.355,32.9205],[126.414,32.95],[126.4785,33.016],[126.597,33.023],[126.713,33.02],[126.7845,33.046],[126.851,33.098],[126.929,33.119],[127.013,33.1685],[127.1545,33.343],[127.2105,33.4665],[127.215,33.5415],[127.1925,33.597],[127.102,33.6815],[126.992,33.7115],[126.9155,33.759],[127.324,33.8045],[127.5755,33.8245],[127.6625,33.836],[127.7835,33.884],[127.999,34.1235],[128.3065,34.205],[128.8165,34.3435],[128.7925,34.51],[128.9485,34.7255],[129.101,34.9495],[129.174,35.0415],[129.2665,35.1335],[129.4655,35.131],[129.489,35.186],[129.5255,35.2055],[129.5925,35.2845],[129.607,35.3375],[129.669,35.4],[129.7205,35.5465],[129.712,35.627],[129.7435,35.7475],[129.782,35.838],[129.783,35.894],[129.8325,35.9925],[129.8315,36.074],[129.799,36.163],[129.7135,36.2475],[129.63,36.2875],[129.6595,36.322],[129.7025,36.506],[129.687,36.5925],[129.721,36.662],[129.7305,36.776],[129.6705,36.94],[129.6815,37.064],[129.674,37.114],[129.6215,37.1945],[129.6245,37.271],[129.57,37.3615],[129.5025,37.4275],[129.4575,37.5055],[129.3805,37.5875],[129.3375,37.679],[129.2755,37.7825],[129.1405,37.899],[128.942,38.1385],[128.8695,38.2145],[128.8245,38.324],[128.7375,38.4685],[128.6975,38.5065],[128.656,38.6175],[128.3745,38.6235],[128.3065,38.57],[128.3135,38.514],[128.283,38.4365],[128.1575,38.343],[128.0675,38.3085],[127.882,38.331],[127.8205,38.3065],[127.7425,38.341],[127.691,38.3245],[127.5735,38.3335],[127.5055,38.301],[127.3855,38.337],[127.306,38.3175],[127.2235,38.328],[127.1105,38.2955],[127.042,38.259],[126.976,38.199],[126.9645,38.135],[126.87,38.0885],[126.8565,38.041],[126.8125,37.999],[126.7215,37.955],[126.6695,37.9445],[126.6915,37.8395],[126.652,37.781],[126.575,37.7625],[126.4135,37.8445],[126.206,37.823],[126.187,37.749],[126.1605,37.7175],[126.111,37.7125],[126.0165,37.6585],[125.75,37.7145],[125.695,37.6915],[125.5165,37.682],[125.4265,37.649]]],[[[130.5155,37.5225],[130.551,37.4],[130.597,37.347],[130.663,37.305],[130.8275,37.256],[130.9255,37.2565],[131.024,37.285],[131.142,37.367],[131.206,37.474],[131.2165,37.55],[131.1645,37.651],[131.1005,37.702],[130.9645,37.7495],[130.849,37.749],[130.676,37.706],[130.589,37.659],[130.525,37.5735],[130.5155,37.5225]]],[[[131.585,37.2415],[131.608,37.159],[131.666,37.098],[131.7815,37.048],[131.883,37.0385],[132.0075,37.066],[132.0955,37.124],[132.142,37.204],[132.146,37.266],[132.1165,37.335],[132.0185,37.4155],[131.9125,37.447],[131.79,37.443],[131.702,37.413],[131.6465,37.3735],[131.5925,37.2935],[131.585,37.2415]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/09/Flag_of_South_Korea.svg","name:en":"South Korea","wikidata":"Q884","ISO3166-1:alpha2":"KR","ISO3166-1:alpha3":"KOR","ISO3166-1:numeric":"410"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[31.3075,-22.424],[31.3765,-22.3815],[31.858,-21.8905],[32.2775,-21.459],[32.4055,-21.316],[32.476,-21.3115],[32.3995,-21.2065],[32.4,-21.163],[32.3665,-21.1365],[32.388,-21.0985],[32.481,-20.9925],[32.517,-20.9145],[32.498,-20.8075],[32.4945,-20.701],[32.4835,-20.657],[32.514,-20.5985],[32.567,-20.561],[32.668,-20.556],[32.808,-20.339],[32.864,-20.287],[32.8755,-20.1875],[32.8615,-20.1365],[32.9235,-20.1265],[32.943,-20.0425],[33.029,-20.035],[33.0245,-19.966],[33.063,-19.777],[32.9585,-19.723],[32.9755,-19.6515],[32.892,-19.684],[32.8345,-19.6735],[32.857,-19.6185],[32.8585,-19.5465],[32.8455,-19.4835],[32.784,-19.47],[32.7765,-19.3615],[32.85,-19.2935],[32.849,-19.2335],[32.8775,-19.098],[32.8335,-19.0165],[32.7135,-19.025],[32.699,-18.939],[32.7305,-18.9225],[32.7045,-18.857],[32.734,-18.818],[32.827,-18.7775],[32.8775,-18.797],[32.928,-18.7665],[32.954,-18.69],[32.886,-18.579],[32.8845,-18.5225],[32.9085,-18.5],[33.0205,-18.4975],[33.0185,-18.421],[33.0675,-18.3485],[33.0255,-18.3105],[33.0215,-18.2285],[33.003,-18.201],[32.941,-18.011],[32.9735,-17.9385],[32.951,-17.882],[32.9725,-17.8005],[33.0185,-17.786],[33.006,-17.7425],[33.0155,-17.6885],[33.046,-17.637],[33.042,-17.5875],[32.9975,-17.5685],[32.9635,-17.4945],[33.0065,-17.4355],[33.0065,-17.402],[33.0495,-17.3355],[33.0005,-17.314],[32.9865,-17.1795],[32.938,-17.0685],[32.8385,-16.932],[32.914,-16.889],[32.9665,-16.7725],[32.9785,-16.707],[32.9215,-16.6975],[32.888,-16.719],[32.838,-16.6965],[32.761,-16.7055],[32.7025,-16.677],[32.7125,-16.603],[32.6255,-16.5595],[32.412,-16.4695],[32.2855,-16.4345],[32.0375,-16.4425],[31.9135,-16.413],[31.894,-16.3365],[31.8305,-16.3085],[31.771,-16.2365],[31.6925,-16.197],[31.5645,-16.186],[31.412,-16.144],[31.322,-16.0255],[31.1405,-15.9835],[31.0325,-16.0205],[30.982,-16.059],[30.9225,-16.0],[30.4245,-16.0],[30.425,-15.628],[30.421,-15.621],[30.354,-15.6555],[30.2745,-15.6445],[30.2195,-15.667],[30.176,-15.6235],[29.9705,-15.6435],[29.9325,-15.6235],[29.8295,-15.6095],[29.63,-15.6645],[29.571,-15.6445],[29.487,-15.6925],[29.4155,-15.6865],[29.288,-15.7545],[29.222,-15.7685],[29.0465,-15.8985],[29.0305,-15.932],[28.9335,-15.9435],[28.8495,-16.0405],[28.87,-16.1035],[28.8435,-16.1535],[28.836,-16.2125],[28.857,-16.2495],[28.821,-16.303],[28.851,-16.3745],[28.8165,-16.4455],[28.8225,-16.4705],[28.7355,-16.5545],[28.642,-16.5715],[28.269,-16.718],[28.1355,-16.8245],[27.9185,-16.9225],[27.8335,-16.969],[27.801,-17.0055],[27.63,-17.2465],[27.6245,-17.3325],[27.559,-17.4105],[27.424,-17.51],[27.2985,-17.6125],[27.226,-17.6905],[27.144,-17.8065],[27.153,-17.837],[27.0845,-17.901],[27.041,-17.959],[26.9675,-17.965],[26.895,-17.9895],[26.8365,-17.991],[26.803,-18.024],[26.752,-18.021],[26.698,-18.062],[26.602,-18.051],[26.5635,-17.988],[26.51,-17.9905],[26.411,-17.9385],[26.3085,-17.937],[26.245,-17.918],[26.2075,-17.8885],[26.0885,-17.9385],[26.092,-17.981],[26.0015,-17.977],[25.9705,-18.003],[25.863,-17.975],[25.856,-17.918],[25.771,-17.8515],[25.705,-17.8385],[25.682,-17.809],[25.6185,-17.841],[25.5465,-17.8395],[25.5155,-17.8655],[25.453,-17.8435],[25.379,-17.8525],[25.3315,-17.8365],[25.263,-17.7915],[25.2375,-17.9125],[25.2655,-17.9485],[25.2885,-18.027],[25.319,-18.078],[25.3935,-18.121],[25.492,-18.3],[25.5235,-18.384],[25.6105,-18.4835],[25.648,-18.4965],[25.692,-18.573],[25.7825,-18.6245],[25.803,-18.687],[25.7905,-18.72],[25.8255,-18.8375],[25.952,-18.913],[25.985,-18.9735],[25.9965,-19.0315],[25.967,-19.0635],[25.975,-19.1315],[26.042,-19.2105],[26.0435,-19.2365],[26.142,-19.438],[26.1655,-19.5385],[26.2465,-19.585],[26.3125,-19.576],[26.3585,-19.615],[26.3285,-19.6515],[26.388,-19.6695],[26.445,-19.751],[26.5335,-19.77],[26.583,-19.804],[26.622,-19.883],[26.703,-19.8875],[26.7265,-19.94],[26.8515,-19.962],[26.9805,-20.015],[27.046,-20.011],[27.1435,-20.0795],[27.217,-20.0935],[27.285,-20.232],[27.3045,-20.331],[27.2885,-20.499],[27.3625,-20.4685],[27.434,-20.4825],[27.4715,-20.468],[27.5765,-20.4945],[27.622,-20.4795],[27.6955,-20.4905],[27.73,-20.523],[27.7025,-20.604],[27.7035,-20.6575],[27.7325,-20.706],[27.7275,-20.7975],[27.6895,-20.9335],[27.7045,-21.037],[27.693,-21.0875],[27.7515,-21.1545],[27.8055,-21.176],[27.921,-21.335],[28.022,-21.576],[28.0995,-21.5785],[28.199,-21.6035],[28.2925,-21.5895],[28.3665,-21.603],[28.5035,-21.669],[28.5825,-21.6305],[28.6645,-21.6735],[28.7475,-21.701],[28.861,-21.755],[29.065,-21.804],[29.0225,-21.904],[29.0405,-22.0035],[29.075,-22.042],[29.139,-22.072],[29.264,-22.0715],[29.288,-22.1215],[29.3755,-22.1955],[29.472,-22.1655],[29.528,-22.178],[29.541,-22.1525],[29.6055,-22.1555],[29.6545,-22.125],[29.677,-22.139],[29.7735,-22.1415],[29.805,-22.1655],[29.923,-22.189],[29.972,-22.224],[30.0165,-22.229],[30.1215,-22.3085],[30.2275,-22.295],[30.283,-22.354],[30.379,-22.3515],[30.492,-22.3165],[30.626,-22.3365],[30.667,-22.3105],[30.704,-22.317],[30.839,-22.292],[30.9995,-22.3165],[31.094,-22.3485],[31.162,-22.3285],[31.221,-22.37],[31.2735,-22.375],[31.3075,-22.424]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/6a/Flag_of_Zimbabwe.svg","name:en":"Zimbabwe","wikidata":"Q954","ISO3166-1:alpha2":"ZW","ISO3166-1:alpha3":"ZWE","ISO3166-1:numeric":"716"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-180.0,51.794],[-179.93,51.864],[-179.8935,51.928],[-179.8855,51.9975],[-179.9085,52.056],[-180.0,52.1385],[-180.0,52.0895],[-180.0,52.0405],[-180.0,51.991],[-180.0,51.942],[-180.0,51.893],[-180.0,51.8435],[-180.0,51.794]]],[[[-179.4515,51.292],[-179.434,51.184],[-179.3615,51.0915],[-179.285,51.0515],[-179.166,51.025],[-179.078,51.025],[-178.9685,51.048],[-178.8805,51.084],[-178.758,51.1725],[-178.6315,51.239],[-178.5935,51.2815],[-178.5655,51.359],[-178.4635,51.315],[-178.248,51.2885],[-178.124,51.3055],[-178.0445,51.337],[-177.957,51.413],[-177.8765,51.411],[-177.762,51.428],[-177.665,51.4675],[-177.531,51.4775],[-177.376,51.508],[-177.221,51.4955],[-177.089,51.431],[-176.9175,51.407],[-176.6995,51.4325],[-176.561,51.4625],[-176.349,51.543],[-176.277,51.546],[-175.821,51.6445],[-175.6565,51.7115],[-175.555,51.7715],[-175.3475,51.799],[-175.2485,51.823],[-175.0865,51.8135],[-174.949,51.823],[-174.864,51.84],[-174.773,51.8225],[-174.5975,51.831],[-174.4865,51.8235],[-174.2715,51.848],[-174.1195,51.915],[-173.973,51.872],[-173.8545,51.858],[-173.689,51.866],[-173.6,51.8455],[-173.485,51.838],[-173.2985,51.8495],[-173.1605,51.8235],[-173.055,51.83],[-172.9055,51.884],[-172.737,51.9275],[-172.604,52.018],[-172.572,52.0655],[-172.463,52.066],[-172.221,52.1235],[-172.0645,52.1935],[-171.971,52.2815],[-171.9615,52.3845],[-171.995,52.4545],[-172.0965,52.5325],[-172.239,52.5865],[-172.4135,52.6095],[-172.5475,52.5955],[-172.766,52.526],[-172.8515,52.464],[-172.9185,52.378],[-172.9455,52.3155],[-173.0945,52.325],[-173.3025,52.327],[-173.373,52.3545],[-173.494,52.373],[-173.653,52.365],[-173.6735,52.4305],[-173.7235,52.494],[-173.779,52.5305],[-173.9245,52.594],[-174.052,52.6285],[-174.169,52.636],[-174.3625,52.6175],[-174.441,52.599],[-174.581,52.5195],[-174.684,52.472],[-174.762,52.3755],[-174.835,52.3475],[-174.8915,52.3895],[-174.997,52.43],[-175.1485,52.442],[-175.236,52.43],[-175.3565,52.3825],[-175.4905,52.4015],[-175.584,52.3945],[-175.7085,52.3555],[-175.8115,52.273],[-175.9795,52.325],[-176.1545,52.3325],[-176.2385,52.322],[-176.3315,52.2915],[-176.455,52.208],[-176.6445,52.213],[-176.876,52.1665],[-176.959,52.1375],[-177.091,52.157],[-177.238,52.1515],[-177.324,52.131],[-177.451,52.1425],[-177.5975,52.116],[-177.692,52.0645],[-177.7995,52.1145],[-177.896,52.1335],[-178.0895,52.136],[-178.298,52.1105],[-178.4305,52.052],[-178.509,51.9725],[-178.5955,52.0165],[-178.7795,52.0525],[-178.927,52.039],[-179.087,51.966],[-179.15,51.8965],[-179.1785,51.7925],[-179.2855,51.745],[-179.3665,51.6435],[-179.3645,51.5255],[-179.321,51.466],[-179.4275,51.3735],[-179.4515,51.292]]],[[[-178.533,28.3915],[-178.519,28.3235],[-178.4795,28.2635],[-178.42,28.2185],[-178.308,28.189],[-178.2345,28.221],[-178.1795,28.272],[-178.147,28.336],[-178.1405,28.4055],[-178.1605,28.4735],[-178.2045,28.5315],[-178.2685,28.5735],[-178.3445,28.5945],[-178.4575,28.546],[-178.5055,28.4915],[-178.533,28.3915]]],[[[-177.6235,28.213],[-177.6085,28.142],[-177.568,28.0785],[-177.506,28.0305],[-177.43,28.0025],[-177.3475,27.9985],[-177.2505,28.03],[-177.179,28.083],[-177.1325,28.154],[-177.116,28.235],[-177.132,28.316],[-177.1785,28.387],[-177.25,28.44],[-177.3375,28.468],[-177.4415,28.444],[-177.5455,28.3765],[-177.595,28.3185],[-177.6235,28.213]]],[[[-176.824,0.817],[-176.813,0.7485],[-176.7775,0.674],[-176.7075,0.615],[-176.648,0.5955],[-176.539,0.608],[-176.47,0.655],[-176.4275,0.721],[-176.4135,0.7735],[-176.4215,0.8705],[-176.455,0.936],[-176.4885,0.972],[-176.549,1.0075],[-176.632,1.021],[-176.6935,1.008],[-176.759,0.967],[-176.7945,0.923],[-176.824,0.817]]],[[[-176.686,0.1925],[-176.6575,0.088],[-176.5915,0.02],[-176.508,-0.0095],[-176.433,-0.0075],[-176.3785,0.0125],[-176.311,0.0705],[-176.2735,0.155],[-176.2835,0.2685],[-176.317,0.3265],[-176.395,0.386],[-176.456,0.402],[-176.546,0.3925],[-176.593,0.369],[-176.6535,0.3045],[-176.686,0.1925]]],[[[-176.147,27.7575],[-176.1335,27.6915],[-176.0965,27.6335],[-176.0395,27.589],[-175.969,27.564],[-175.8935,27.561],[-175.733,27.6],[-175.6575,27.645],[-175.604,27.71],[-175.579,27.7875],[-175.526,27.8725],[-175.5205,27.937],[-175.5405,27.999],[-175.5825,28.052],[-175.6425,28.089],[-175.713,28.1065],[-175.786,28.1015],[-175.9275,28.0585],[-175.993,28.022],[-176.0435,27.97],[-176.0735,27.907],[-176.12,27.8545],[-176.147,27.7575]]],[[[-174.2355,26.0505],[-174.2195,25.98],[-174.178,25.9185],[-174.116,25.873],[-174.0405,25.8485],[-173.9605,25.8485],[-173.8835,25.869],[-173.816,25.9075],[-173.767,25.9645],[-173.7425,26.0325],[-173.745,26.104],[-173.774,26.171],[-173.8265,26.225],[-173.8965,26.2595],[-173.975,26.2715],[-174.055,26.263],[-174.1285,26.2345],[-174.187,26.1855],[-174.2245,26.122],[-174.2355,26.0505]]],[[[-173.504,60.655],[-173.4485,60.5555],[-173.451,60.4885],[-173.362,60.3855],[-173.2515,60.3375],[-173.136,60.306],[-173.175,60.1935],[-173.1155,60.109],[-173.0385,60.064],[-172.9215,60.029],[-172.801,60.0135],[-172.656,60.0175],[-172.4615,60.0685],[-172.3855,60.119],[-172.19,60.1135],[-172.102,60.124],[-171.9265,60.183],[-171.848,60.2375],[-171.8035,60.3245],[-171.8165,60.381],[-171.8915,60.4555],[-172.0835,60.5485],[-172.249,60.5995],[-172.4755,60.6145],[-172.5315,60.71],[-172.662,60.7845],[-172.75,60.8535],[-172.8645,60.8945],[-172.9725,60.9115],[-173.113,60.912],[-173.285,60.8785],[-173.3835,60.833],[-173.4745,60.7515],[-173.504,60.655]]],[[[-172.2945,63.56],[-172.266,63.433],[-172.1975,63.343],[-172.0855,63.2625],[-171.953,63.21],[-171.66,63.1415],[-171.503,63.1235],[-171.3595,63.1215],[-171.196,63.143],[-170.9955,63.1985],[-170.9165,63.2335],[-170.7125,63.1915],[-170.6645,63.116],[-170.572,63.061],[-170.4635,63.027],[-170.2345,62.9955],[-170.186,62.974],[-170.1765,62.919],[-170.097,62.8405],[-169.9795,62.792],[-169.794,62.7595],[-169.602,62.7525],[-169.3885,62.7835],[-169.1955,62.849],[-169.0975,62.9195],[-168.899,62.882],[-168.7775,62.881],[-168.6205,62.9035],[-168.45,62.964],[-168.348,63.06],[-168.346,63.131],[-168.2365,63.2705],[-168.228,63.331],[-168.2975,63.4235],[-168.45,63.4895],[-168.737,63.539],[-168.9115,63.559],[-169.2635,63.5605],[-169.459,63.62],[-169.5835,63.6355],[-169.6035,63.7385],[-169.6525,63.782],[-169.7735,63.836],[-170.0435,63.891],[-170.1725,63.9055],[-170.4135,63.914],[-170.577,63.9045],[-170.8545,63.853],[-170.9855,63.812],[-171.197,63.841],[-171.319,63.943],[-171.4335,63.98],[-171.609,64.003],[-171.78,64.0075],[-171.9885,63.9775],[-172.0985,63.9365],[-172.1685,63.8905],[-172.211,63.8205],[-172.2145,63.72],[-172.273,63.642],[-172.2945,63.56]]],[[[-171.967,25.771],[-171.9525,25.697],[-171.911,25.6325],[-171.847,25.5845],[-171.7275,25.5555],[-171.647,25.57],[-171.5765,25.608],[-171.5245,25.665],[-171.497,25.7345],[-171.497,25.8085],[-171.525,25.878],[-171.577,25.935],[-171.6475,25.9725],[-171.7285,25.9865],[-171.81,25.9735],[-171.882,25.936],[-171.935,25.8785],[-171.967,25.771]]],[[[-169.016,65.481],[-168.798,65.442],[-168.5705,65.4495],[-168.441,65.476],[-168.2595,65.412],[-167.9725,65.3515],[-167.734,65.2695],[-167.5375,65.2295],[-167.402,65.218],[-167.396,65.128],[-167.3515,65.0775],[-167.2185,64.988],[-167.132,64.9495],[-167.015,64.864],[-166.925,64.8315],[-166.924,64.7345],[-166.8695,64.6435],[-166.805,64.576],[-166.669,64.5015],[-166.6145,64.409],[-166.4425,64.3225],[-166.2995,64.2955],[-166.0855,64.295],[-165.9605,64.3155],[-165.8395,64.357],[-165.4325,64.309],[-165.101,64.2575],[-164.9735,64.252],[-164.7835,64.2615],[-164.568,64.291],[-164.198,64.381],[-163.92,64.3695],[-163.677,64.38],[-163.526,64.294],[-163.3725,64.2385],[-162.885,64.147],[-162.699,64.1435],[-162.467,64.191],[-162.2435,64.284],[-162.1675,64.3525],[-161.728,64.2195],[-161.756,64.121],[-161.7125,64.0485],[-161.6255,63.994],[-161.452,63.9435],[-161.2975,63.9305],[-161.258,63.8965],[-161.233,63.8165],[-161.372,63.7525],[-161.507,63.802],[-161.6355,63.822],[-161.8605,63.8165],[-162.025,63.776],[-162.219,63.8335],[-162.3785,63.8495],[-162.6575,63.845],[-162.859,63.802],[-163.0535,63.718],[-163.128,63.6535],[-163.1465,63.5865],[-163.107,63.5125],[-162.947,63.414],[-163.099,63.3725],[-163.2215,63.303],[-163.4905,63.4015],[-163.8615,63.467],[-164.106,63.48],[-164.237,63.472],[-164.521,63.4205],[-164.6345,63.3885],[-164.863,63.3025],[-165.0215,63.221],[-165.2445,63.214],[-165.348,63.1915],[-165.4595,63.144],[-165.5185,63.095],[-165.5465,63.027],[-165.5045,62.937],[-165.4375,62.891],[-165.313,62.845],[-165.3575,62.7795],[-165.5825,62.7305],[-165.6765,62.6765],[-165.721,62.6215],[-165.77,62.483],[-165.762,62.4385],[-165.974,62.291],[-166.102,62.277],[-166.2715,62.2195],[-166.3445,62.1705],[-166.407,62.0965],[-166.403,61.9805],[-166.536,61.8215],[-166.5775,61.6555],[-166.5775,61.5655],[-166.531,61.464],[-166.436,61.378],[-166.38,61.304],[-166.213,61.2295],[-166.141,61.175],[-166.025,61.123],[-165.9455,61.0075],[-165.7575,60.9165],[-165.5285,60.873],[-165.443,60.7985],[-165.5785,60.758],[-165.708,60.699],[-165.794,60.6055],[-165.841,60.598],[-166.0325,60.6585],[-166.18,60.668],[-166.305,60.655],[-166.451,60.6075],[-166.601,60.59],[-166.712,60.559],[-166.862,60.54],[-166.9785,60.501],[-167.042,60.4615],[-167.388,60.4515],[-167.5295,60.427],[-167.7055,60.368],[-167.767,60.33],[-167.8275,60.242],[-167.827,60.193],[-167.7855,60.127],[-167.7165,60.068],[-167.672,59.9895],[-167.539,59.909],[-167.2315,59.808],[-167.0505,59.788],[-166.788,59.6875],[-166.655,59.663],[-166.5065,59.659],[-166.4385,59.6175],[-166.2815,59.569],[-166.1365,59.557],[-165.994,59.567],[-165.85,59.5975],[-165.744,59.647],[-165.697,59.6875],[-165.571,59.697],[-165.4025,59.73],[-165.3225,59.757],[-165.227,59.813],[-165.1545,59.886],[-165.1125,59.97],[-165.1125,60.015],[-164.943,60.003],[-164.804,60.014],[-164.709,59.9515],[-164.6815,59.8665],[-164.55,59.7425],[-164.4525,59.6695],[-164.26,59.556],[-164.135,59.5105],[-163.858,59.472],[-163.7055,59.4775],[-163.5765,59.509],[-163.4445,59.589],[-163.2395,59.601],[-163.005,59.6375],[-162.8595,59.638],[-162.717,59.6545],[-162.6155,59.6835],[-162.5325,59.603],[-162.4335,59.5605],[-162.2485,59.532],[-162.3925,59.372],[-162.4285,59.3055],[-162.414,59.22],[-162.3005,59.062],[-162.1565,58.938],[-162.152,58.8845],[-162.342,58.8445],[-162.497,58.758],[-162.5345,58.702],[-162.539,58.6425],[-162.4925,58.5665],[-162.408,58.5125],[-162.286,58.4695],[-162.04,58.435],[-161.961,58.3995],[-161.837,58.371],[-161.737,58.365],[-161.5145,58.385],[-161.3595,58.44],[-161.271,58.3965],[-161.076,58.363],[-160.8035,58.382],[-160.6785,58.416],[-160.5745,58.467],[-160.3975,58.4125],[-160.2915,58.4055],[-160.125,58.4275],[-159.9315,58.408],[-159.7455,58.441],[-159.674,58.4725],[-159.5775,58.5545],[-159.4285,58.4035],[-159.387,58.3225],[-159.2855,58.2575],[-159.185,58.224],[-159.073,58.2065],[-158.8235,58.2075],[-158.6465,58.236],[-158.523,58.2785],[-158.377,58.355],[-158.18,58.3985],[-157.9665,58.2365],[-157.9545,58.145],[-158.003,57.9],[-158.058,57.7635],[-158.1355,57.695],[-158.156,57.6455],[-158.2635,57.5705],[-158.3695,57.522],[-158.517,57.476],[-158.6415,57.416],[-158.7495,57.3165],[-158.972,57.1255],[-159.003,57.089],[-159.141,57.039],[-159.2745,56.969],[-159.6255,56.859],[-159.7295,56.8095],[-159.9535,56.745],[-160.062,56.7025],[-160.2145,56.615],[-160.3465,56.575],[-160.649,56.409],[-160.713,56.3385],[-160.768,56.2445],[-161.041,56.2435],[-161.24,56.2205],[-161.3745,56.195],[-161.6645,56.1515],[-161.9265,56.0975],[-162.0495,56.0515],[-162.143,55.9965],[-162.2825,55.9535],[-162.5145,55.835],[-162.751,55.6535],[-162.882,55.6005],[-163.0135,55.6555],[-163.1355,55.6785],[-163.234,55.6785],[-163.351,55.657],[-163.4715,55.597],[-163.5245,55.5345],[-163.534,55.452],[-163.4485,55.318],[-163.6445,55.261],[-163.78,55.2725],[-163.9855,55.247],[-164.132,55.1955],[-164.254,55.1775],[-164.3365,55.147],[-164.465,55.1505],[-164.6415,55.114],[-164.7795,55.0505],[-164.837,55.0095],[-164.9465,54.84],[-165.16,54.756],[-165.2525,54.6715],[-165.2765,54.6115],[-165.2595,54.505],[-165.3405,54.4975],[-165.4855,54.5145],[-165.6665,54.5105],[-165.779,54.4885],[-165.881,54.4405],[-166.092,54.422],[-166.2785,54.349],[-166.356,54.297],[-166.407,54.227],[-166.5345,54.2215],[-166.66,54.2335],[-166.7895,54.2275],[-166.973,54.196],[-167.1505,54.1545],[-167.3445,54.054],[-167.4395,53.969],[-167.469,53.9225],[-167.4725,53.807],[-167.429,53.7415],[-167.4725,53.6865],[-167.521,53.6685],[-167.626,53.717],[-167.7605,53.7385],[-167.8475,53.7665],[-167.7325,53.8285],[-167.686,53.8885],[-167.6735,53.942],[-167.71,54.0345],[-167.834,54.125],[-167.97,54.158],[-168.088,54.158],[-168.2355,54.12],[-168.3065,54.0775],[-168.352,54.025],[-168.3685,53.9335],[-168.2965,53.8255],[-168.193,53.7705],[-168.4045,53.7125],[-168.551,53.648],[-168.6705,53.551],[-168.7185,53.48],[-168.7985,53.4545],[-168.9955,53.3365],[-169.0815,53.267],[-169.123,53.1695],[-169.178,53.129],[-169.3315,53.0985],[-169.422,53.1925],[-169.5325,53.252],[-169.6355,53.287],[-169.7375,53.299],[-169.914,53.2745],[-170.018,53.2275],[-170.0965,53.1405],[-170.1785,53.13],[-170.313,53.077],[-170.4205,52.979],[-170.4475,52.893],[-170.565,52.9125],[-170.6985,52.9165],[-170.813,52.8935],[-170.9265,52.8525],[-171.038,52.7965],[-171.1935,52.8015],[-171.302,52.7775],[-171.5375,52.655],[-171.611,52.568],[-171.626,52.5065],[-171.597,52.396],[-171.5505,52.3435],[-171.437,52.286],[-171.3375,52.2655],[-171.1665,52.2655],[-171.017,52.305],[-170.9185,52.3765],[-170.7505,52.358],[-170.627,52.3795],[-170.535,52.42],[-170.367,52.4745],[-170.2685,52.5455],[-170.1805,52.5345],[-170.018,52.533],[-169.8655,52.571],[-169.8145,52.6],[-169.6485,52.591],[-169.48,52.6345],[-169.359,52.588],[-169.2355,52.576],[-169.146,52.5855],[-169.0235,52.618],[-168.9455,52.6515],[-168.7305,52.722],[-168.576,52.75],[-168.4935,52.7915],[-168.333,52.7985],[-168.1555,52.866],[-168.0885,52.92],[-168.0525,53.029],[-167.959,53.0825],[-167.8675,53.0885],[-167.779,53.0535],[-167.6845,53.038],[-167.525,53.05],[-167.305,53.1015],[-167.221,53.1445],[-167.086,53.1885],[-166.988,53.239],[-166.769,53.244],[-166.673,53.257],[-166.5225,53.3025],[-166.322,53.404],[-166.1985,53.4985],[-166.0125,53.5535],[-165.8655,53.642],[-165.7715,53.6865],[-165.6955,53.776],[-165.6855,53.8595],[-165.592,53.8425],[-165.486,53.842],[-165.365,53.8595],[-165.2195,53.8545],[-165.1,53.88],[-164.8625,53.8935],[-164.695,53.9495],[-164.5985,54.0365],[-164.4715,54.105],[-164.4245,54.157],[-164.403,54.2345],[-164.245,54.2695],[-164.088,54.344],[-163.992,54.4405],[-163.7765,54.4505],[-163.615,54.4165],[-163.49,54.418],[-163.406,54.4355],[-163.3095,54.478],[-163.2375,54.478],[-163.2605,54.3725],[-163.2015,54.2715],[-163.105,54.211],[-162.944,54.176],[-162.8915,54.1485],[-162.7475,54.1125],[-162.607,54.1085],[-162.4225,54.1295],[-162.2195,54.1985],[-162.085,54.253],[-162.0215,54.3045],[-161.983,54.3815],[-161.9865,54.4355],[-161.8665,54.4635],[-161.783,54.508],[-161.738,54.5515],[-161.641,54.5795],[-161.4935,54.6035],[-161.352,54.667],[-161.2895,54.7455],[-161.293,54.839],[-161.1335,54.9185],[-161.089,54.9715],[-161.0135,54.9745],[-160.9065,54.9435],[-160.726,54.94],[-160.584,54.8985],[-160.555,54.8315],[-160.466,54.7465],[-160.37,54.703],[-160.183,54.68],[-160.065,54.697],[-159.955,54.639],[-159.66,54.578],[-159.557,54.569],[-159.3725,54.5875],[-159.264,54.629],[-159.19,54.68],[-159.035,54.718],[-158.9305,54.7805],[-158.8705,54.8355],[-158.8345,54.9345],[-158.8695,55.0265],[-158.924,55.075],[-158.9525,55.153],[-159.062,55.2355],[-159.125,55.258],[-159.13,55.347],[-159.171,55.4015],[-159.2885,55.4675],[-159.231,55.5485],[-159.0515,55.616],[-158.944,55.595],[-158.7955,55.5975],[-158.6425,55.644],[-158.539,55.6625],[-158.3905,55.7355],[-158.334,55.814],[-158.232,55.836],[-158.126,55.8885],[-158.065,55.95],[-157.963,55.9635],[-157.8675,55.9955],[-157.742,56.0885],[-157.6025,56.0965],[-157.507,56.1215],[-157.393,56.1905],[-157.357,56.2425],[-157.3485,56.331],[-157.1005,56.329],[-157.1485,56.2335],[-157.1535,56.152],[-157.0955,56.06],[-157.0715,55.947],[-157.0,55.8705],[-156.846,55.81],[-156.731,55.798],[-156.627,55.806],[-156.478,55.8375],[-156.3455,55.9115],[-156.2675,56.0025],[-156.255,56.061],[-156.2785,56.1425],[-156.367,56.235],[-156.4195,56.328],[-156.5285,56.4025],[-156.6565,56.438],[-156.608,56.493],[-156.5925,56.566],[-156.61,56.6155],[-156.4705,56.6995],[-156.321,56.739],[-156.2555,56.777],[-156.098,56.9325],[-156.035,57.0125],[-155.912,57.105],[-155.8945,57.198],[-155.923,57.2525],[-155.7365,57.3125],[-155.6735,57.36],[-155.5295,57.3855],[-155.424,57.434],[-155.37,57.482],[-155.151,57.437],[-155.192,57.3215],[-155.1385,57.215],[-155.014,57.146],[-154.874,57.103],[-154.877,56.985],[-154.7915,56.879],[-154.6535,56.8205],[-154.7095,56.786],[-154.922,56.687],[-155.01,56.629],[-155.102,56.5425],[-155.137,56.487],[-155.131,56.392],[-155.0355,56.292],[-154.8955,56.2345],[-154.7515,56.2155],[-154.544,56.242],[-154.4095,56.3045],[-154.0215,56.312],[-153.781,56.34],[-153.6435,56.3885],[-153.538,56.4595],[-153.4815,56.538],[-153.482,56.638],[-153.4025,56.726],[-153.2275,56.809],[-153.1155,56.8245],[-152.9535,56.8785],[-152.876,56.938],[-152.7555,56.957],[-152.5805,57.0285],[-152.492,57.1225],[-152.3725,57.182],[-152.2145,57.1815],[-152.019,57.2305],[-151.9445,57.27],[-151.8735,57.3515],[-151.879,57.459],[-151.7955,57.523],[-151.7445,57.613],[-151.743,57.679],[-151.8145,57.7695],[-151.833,57.8555],[-151.9265,57.9445],[-151.9365,57.979],[-151.7165,57.992],[-151.5225,58.059],[-151.4435,58.1195],[-151.396,58.2035],[-151.3875,58.2615],[-151.423,58.429],[-151.5425,58.5195],[-151.6775,58.5565],[-151.7675,58.564],[-151.84,58.648],[-151.9185,58.7005],[-151.821,58.7215],[-151.6285,58.811],[-151.5505,58.917],[-151.406,58.9215],[-151.269,58.9385],[-151.1125,59.0025],[-151.0285,59.0155],[-150.8535,59.0175],[-150.742,59.0415],[-150.62,59.103],[-150.4745,59.1365],[-150.386,59.1335],[-150.23,59.155],[-150.058,59.233],[-149.938,59.332],[-149.894,59.3945],[-149.7805,59.354],[-149.5885,59.334],[-149.4245,59.3555],[-149.3455,59.3825],[-149.2525,59.4425],[-149.157,59.5715],[-149.1475,59.6305],[-148.956,59.7465],[-148.577,59.729],[-148.411,59.7575],[-148.2995,59.7525],[-148.235,59.6805],[-148.149,59.632],[-148.004,59.592],[-147.782,59.5795],[-147.5245,59.6255],[-147.428,59.6525],[-147.2685,59.677],[-147.136,59.7145],[-147.006,59.775],[-146.9315,59.8525],[-146.913,59.981],[-146.8055,59.9755],[-146.6395,59.9935],[-146.4695,60.0615],[-146.386,60.081],[-146.223,60.1465],[-146.0925,60.161],[-145.9715,60.192],[-145.784,60.1675],[-145.698,60.133],[-145.3905,60.064],[-145.2595,60.045],[-144.8215,60.015],[-144.9185,59.942],[-144.9815,59.85],[-144.9915,59.7955],[-144.9595,59.7245],[-144.8965,59.662],[-144.7335,59.5945],[-144.5805,59.5775],[-144.4585,59.586],[-144.3,59.633],[-144.089,59.784],[-143.7795,59.8115],[-143.6175,59.84],[-143.407,59.853],[-143.03,59.8605],[-142.797,59.902],[-142.6425,59.9075],[-142.5295,59.879],[-142.316,59.874],[-142.0255,59.8395],[-141.8395,59.7835],[-141.7375,59.773],[-141.5835,59.712],[-141.325,59.6525],[-141.0395,59.57],[-140.6345,59.5235],[-140.4215,59.519],[-139.6755,59.2455],[-139.475,59.1825],[-139.107,59.093],[-138.8435,58.9835],[-138.754,58.9575],[-138.4585,58.894],[-138.3145,58.8245],[-138.2915,58.749],[-138.174,58.653],[-138.035,58.59],[-137.9365,58.4965],[-137.7475,58.4285],[-137.505,58.323],[-137.4065,58.2915],[-137.3265,58.2445],[-137.1495,58.1995],[-137.0015,58.106],[-136.939,58.081],[-136.933,57.919],[-136.9075,57.86],[-136.737,57.7085],[-136.628,57.6485],[-136.5595,57.5265],[-136.4405,57.4415],[-136.2765,57.379],[-136.2325,57.343],[-136.2275,57.2215],[-136.1885,57.1485],[-136.2115,57.021],[-136.177,56.9295],[-136.0785,56.8595],[-135.9065,56.8115],[-135.85,56.7365],[-135.703,56.633],[-135.5735,56.5635],[-135.4475,56.523],[-135.3805,56.471],[-135.293,56.3715],[-135.2645,56.3175],[-135.11,56.152],[-134.886,56.0215],[-134.9475,55.9395],[-134.9535,55.8945],[-134.9245,55.823],[-134.8415,55.75],[-134.709,55.692],[-134.512,55.6755],[-134.426,55.6465],[-134.2875,55.6305],[-134.1235,55.6535],[-134.0835,55.6025],[-134.15,55.5085],[-134.16,55.4425],[-134.113,55.3535],[-134.0205,55.294],[-133.96,55.1925],[-133.8895,55.1275],[-133.8105,55.0815],[-133.8175,55.013],[-133.888,54.9135],[-133.8635,54.749],[-133.7925,54.6625],[-133.716,54.612],[-133.648,54.632],[-132.9255,54.659],[-132.684,54.662],[-131.7515,54.6825],[-131.435,54.6895],[-130.6155,54.7075],[-130.659,54.763],[-130.568,54.792],[-130.346,54.9175],[-130.2735,54.974],[-130.1875,55.063],[-130.103,55.1925],[-129.974,55.282],[-130.021,55.338],[-130.041,55.4515],[-130.09,55.498],[-130.128,55.581],[-130.1115,55.683],[-130.1525,55.7665],[-130.0835,55.8215],[-130.0175,55.912],[-130.0035,56.008],[-130.104,56.123],[-130.246,56.0965],[-130.426,56.1415],[-130.468,56.243],[-130.6235,56.267],[-130.782,56.367],[-131.0875,56.406],[-131.1735,56.4495],[-131.472,56.5525],[-131.581,56.612],[-131.8355,56.599],[-131.8605,56.703],[-131.901,56.7535],[-131.873,56.806],[-132.1235,56.8735],[-132.045,57.045],[-132.369,57.0915],[-132.248,57.2115],[-132.37,57.3495],[-132.5535,57.4965],[-132.661,57.6165],[-132.751,57.696],[-132.8695,57.8395],[-133.07,58.0005],[-133.1725,58.1535],[-133.3455,58.2765],[-133.4615,58.3875],[-133.3775,58.4305],[-133.558,58.523],[-133.707,58.6125],[-133.8405,58.7295],[-134.258,58.861],[-134.3365,58.9235],[-134.3135,58.962],[-134.407,58.979],[-134.382,59.039],[-134.4835,59.1315],[-134.5655,59.131],[-134.679,59.1925],[-134.7005,59.249],[-134.96,59.281],[-135.0305,59.3465],[-134.9895,59.387],[-135.101,59.4275],[-135.0275,59.4745],[-135.029,59.5635],[-135.1575,59.6255],[-135.253,59.6985],[-135.479,59.798],[-135.9525,59.662],[-136.193,59.64],[-136.3465,59.6005],[-136.24,59.5595],[-136.2385,59.5245],[-136.305,59.4645],[-136.368,59.449],[-136.477,59.466],[-136.469,59.284],[-136.5845,59.166],[-136.8285,59.16],[-136.9695,59.101],[-137.283,59.0],[-137.4515,58.9085],[-137.526,58.9065],[-137.5,58.9855],[-137.542,59.1065],[-137.6075,59.244],[-138.626,59.7685],[-138.6685,59.8095],[-138.7075,59.9065],[-139.0535,59.995],[-139.199,60.0885],[-139.072,60.3195],[-139.075,60.3525],[-139.693,60.335],[-139.981,60.182],[-140.458,60.308],[-140.5205,60.22],[-140.769,60.26],[-141.002,60.3065],[-141.002,60.8185],[-141.002,61.249],[-141.0015,61.597],[-141.0015,62.224],[-141.0015,62.615],[-141.0015,63.022],[-141.0015,63.5185],[-141.0015,63.9315],[-141.0015,64.543],[-141.0015,64.992],[-141.0015,65.494],[-141.0015,66.179],[-141.001,66.8875],[-141.001,67.5985],[-141.001,68.095],[-141.001,68.8115],[-141.0005,69.419],[-141.0025,69.846],[-140.996,69.8885],[-141.126,69.8985],[-141.3175,69.9555],[-141.796,70.046],[-142.3805,70.219],[-142.7405,70.2895],[-142.956,70.3435],[-143.2065,70.3685],[-143.7085,70.357],[-144.1365,70.319],[-144.269,70.29],[-144.758,70.218],[-145.1165,70.2545],[-145.294,70.2855],[-145.5265,70.349],[-145.727,70.3845],[-146.229,70.44],[-146.502,70.449],[-146.7885,70.508],[-146.9565,70.52],[-147.1695,70.582],[-147.4135,70.6105],[-147.6085,70.6785],[-147.7955,70.706],[-148.01,70.711],[-148.1925,70.695],[-148.444,70.6985],[-148.6135,70.681],[-149.3265,70.768],[-149.7205,70.786],[-150.2745,70.7695],[-150.4735,70.7465],[-150.6,70.7155],[-150.999,70.692],[-151.2215,70.757],[-151.3845,70.7755],[-151.5245,70.846],[-151.671,70.9475],[-151.846,71.004],[-152.253,71.0785],[-152.681,71.118],[-153.078,71.1385],[-153.3705,71.131],[-153.5645,71.1045],[-153.8065,71.1065],[-154.102,71.1545],[-154.346,71.243],[-154.7575,71.345],[-155.1875,71.4105],[-155.4645,71.4425],[-155.678,71.501],[-156.206,71.588],[-156.57,71.602],[-156.8675,71.5575],[-157.303,71.4165],[-157.519,71.308],[-157.749,71.1735],[-157.8885,71.1155],[-158.213,71.0365],[-158.428,71.102],[-158.704,71.1275],[-158.9855,71.117],[-159.4835,71.0615],[-159.8255,71.007],[-159.9995,70.9695],[-160.361,70.8365],[-160.6595,70.7055],[-160.897,70.6195],[-161.237,70.536],[-161.4065,70.5115],[-161.705,70.539],[-162.022,70.539],[-162.2185,70.5075],[-162.668,70.372],[-162.923,70.2635],[-163.1865,70.0905],[-163.45,69.9545],[-163.5865,69.841],[-163.6965,69.6725],[-163.7035,69.446],[-163.7455,69.402],[-164.0605,69.2355],[-164.2695,69.1695],[-164.4215,69.136],[-164.738,69.125],[-165.2905,69.0785],[-165.5785,69.0675],[-166.21,69.0975],[-166.3875,69.085],[-166.579,69.0425],[-166.691,68.9905],[-166.7635,68.8985],[-166.7275,68.7935],[-166.764,68.629],[-166.813,68.584],[-167.0405,68.5455],[-167.206,68.499],[-167.3225,68.435],[-167.364,68.3325],[-167.245,68.228],[-167.103,68.1825],[-166.9475,68.1595],[-166.7215,68.158],[-166.484,68.112],[-166.319,68.003],[-166.1625,67.9585],[-165.8015,67.902],[-165.593,67.822],[-165.203,67.697],[-164.972,67.604],[-164.7435,67.5345],[-164.5205,67.49],[-164.383,67.3485],[-164.339,67.2795],[-164.408,67.2135],[-164.4175,67.165],[-164.3795,67.0975],[-164.2085,67.009],[-163.9695,66.938],[-163.7105,66.899],[-163.184,66.8475],[-163.131,66.8355],[-163.1195,66.7625],[-163.0255,66.6745],[-162.782,66.5735],[-162.541,66.5375],[-162.3785,66.4575],[-162.865,66.3205],[-163.155,66.383],[-163.0015,66.4815],[-162.985,66.563],[-163.0285,66.626],[-163.1015,66.674],[-163.2485,66.7305],[-163.4755,66.7815],[-163.6845,66.8055],[-163.9035,66.813],[-164.152,66.8095],[-164.502,66.7925],[-165.0175,66.727],[-165.703,66.6005],[-166.066,66.5195],[-166.6515,66.362],[-167.1,66.226],[-167.6265,66.0495],[-167.786,66.005],[-168.151,65.93],[-168.415,65.846],[-168.5605,65.937],[-168.7645,65.9805],[-168.9245,65.986],[-169.016,65.481]]],[[[-171.295,-11.0535],[-171.2835,-11.1225],[-171.2175,-11.2175],[-171.1575,-11.2545],[-171.055,-11.2675],[-170.976,-11.242],[-170.8985,-11.1725],[-170.862,-11.0765],[-170.8635,-11.023],[-170.884,-10.9615],[-170.9435,-10.8895],[-170.9885,-10.8625],[-171.0695,-10.845],[-171.1615,-10.861],[-171.2145,-10.8905],[-171.2825,-10.9825],[-171.295,-11.0535]]],[[[-171.053,-14.3205],[-171.025,-14.423],[-170.9585,-14.4935],[-170.8555,-14.552],[-170.791,-14.573],[-170.712,-14.57],[-170.6195,-14.528],[-170.5735,-14.4905],[-170.5005,-14.484],[-170.448,-14.464],[-170.3675,-14.3865],[-170.341,-14.309],[-170.345,-14.237],[-170.3775,-14.154],[-170.408,-14.111],[-170.4675,-14.0665],[-170.5205,-14.048],[-170.6715,-14.028],[-170.7615,-14.0485],[-170.8225,-14.0895],[-170.883,-14.1055],[-170.995,-14.1805],[-171.0365,-14.242],[-171.053,-14.3205]]],[[[-170.802,25.4225],[-170.789,25.3545],[-170.751,25.2945],[-170.693,25.2495],[-170.621,25.225],[-170.546,25.224],[-170.476,25.245],[-170.4175,25.286],[-170.377,25.3415],[-170.359,25.406],[-170.365,25.4725],[-170.395,25.5335],[-170.4455,25.5825],[-170.5105,25.6135],[-170.5835,25.624],[-170.6585,25.6105],[-170.7245,25.5755],[-170.773,25.522],[-170.802,25.4225]]],[[[-170.773,57.177],[-170.7505,57.109],[-170.7515,57.019],[-170.7155,56.9665],[-170.6465,56.9165],[-170.4825,56.866],[-170.3845,56.859],[-170.23,56.876],[-170.1435,56.9055],[-169.984,56.998],[-169.835,57.0015],[-169.7405,57.022],[-169.627,57.0775],[-169.5775,57.126],[-169.5515,57.2145],[-169.5795,57.279],[-169.712,57.3665],[-169.949,57.4515],[-170.065,57.465],[-170.1795,57.4605],[-170.2985,57.4345],[-170.4925,57.412],[-170.645,57.3605],[-170.746,57.2755],[-170.773,57.177]]],[[[-170.131,56.6285],[-170.1105,56.5625],[-170.008,56.474],[-169.868,56.406],[-169.7365,56.365],[-169.521,56.3495],[-169.343,56.3865],[-169.1975,56.459],[-169.0995,56.5575],[-169.0865,56.6175],[-169.129,56.7055],[-169.257,56.7815],[-169.3625,56.8105],[-169.509,56.823],[-169.76,56.8305],[-169.892,56.8175],[-170.0545,56.752],[-170.1025,56.7065],[-170.131,56.6285]]],[[[-169.8935,-14.165],[-169.871,-14.258],[-169.834,-14.3125],[-169.778,-14.3595],[-169.6605,-14.389],[-169.5875,-14.451],[-169.523,-14.472],[-169.4675,-14.473],[-169.35,-14.4455],[-169.2965,-14.416],[-169.24,-14.3505],[-169.221,-14.3],[-169.213,-14.206],[-169.235,-14.1275],[-169.281,-14.0695],[-169.359,-14.0215],[-169.468,-14.011],[-169.539,-13.968],[-169.5915,-13.9555],[-169.723,-13.9625],[-169.798,-13.9945],[-169.8755,-14.083],[-169.8935,-14.165]]],[[[-169.7705,16.6975],[-169.753,16.6305],[-169.7135,16.572],[-169.656,16.53],[-169.587,16.508],[-169.5145,16.509],[-169.4085,16.5325],[-169.3485,16.5645],[-169.3025,16.612],[-169.2565,16.738],[-169.2545,16.8095],[-169.2775,16.8775],[-169.3225,16.934],[-169.385,16.9735],[-169.457,16.9905],[-169.5315,16.984],[-169.6395,16.93],[-169.7075,16.8675],[-169.752,16.7995],[-169.7705,16.6975]]],[[[-168.5515,64.9825],[-168.509,64.8995],[-168.4365,64.851],[-168.3295,64.811],[-168.099,64.7775],[-167.9225,64.7825],[-167.799,64.8035],[-167.663,64.8535],[-167.6065,64.892],[-167.5625,64.9625],[-167.5695,65.031],[-167.6245,65.0965],[-167.8065,65.173],[-168.03,65.198],[-168.208,65.186],[-168.33,65.16],[-168.501,65.0735],[-168.5515,64.9825]]],[[[-168.3785,-14.5405],[-168.355,-14.635],[-168.314,-14.693],[-168.2465,-14.742],[-168.187,-14.759],[-168.0965,-14.752],[-168.0295,-14.721],[-167.966,-14.6595],[-167.9345,-14.5765],[-167.938,-14.502],[-167.9585,-14.45],[-168.0295,-14.368],[-168.084,-14.3405],[-168.1635,-14.3295],[-168.214,-14.338],[-168.2955,-14.378],[-168.366,-14.4705],[-168.3785,-14.5405]]],[[[-168.221,24.999],[-168.2075,24.93],[-168.169,24.8695],[-168.11,24.8245],[-167.999,24.797],[-167.9235,24.8095],[-167.8565,24.8445],[-167.807,24.8985],[-167.781,24.964],[-167.781,25.034],[-167.8075,25.1],[-167.857,25.1535],[-167.9235,25.1885],[-167.9995,25.201],[-168.075,25.1885],[-168.142,25.1535],[-168.191,25.0995],[-168.221,24.999]]],[[[-166.5445,23.8495],[-166.531,23.783],[-166.4455,23.654],[-166.386,23.6025],[-166.3555,23.534],[-166.302,23.479],[-166.2315,23.4435],[-166.157,23.427],[-166.0845,23.428],[-166.016,23.4495],[-165.923,23.534],[-165.867,23.643],[-165.855,23.7625],[-165.8875,23.8785],[-165.962,23.977],[-166.0685,24.047],[-166.1945,24.0795],[-166.325,24.071],[-166.445,24.022],[-166.499,23.976],[-166.5335,23.9165],[-166.5445,23.8495]]],[[[-164.923,23.5765],[-164.9095,23.5075],[-164.8715,23.4465],[-164.8125,23.401],[-164.7405,23.3765],[-164.6655,23.3755],[-164.564,23.415],[-164.5135,23.4645],[-164.4835,23.5265],[-164.4775,23.594],[-164.4955,23.6595],[-164.5365,23.7155],[-164.5955,23.7565],[-164.6655,23.7775],[-164.741,23.7765],[-164.813,23.752],[-164.8715,23.707],[-164.9095,23.646],[-164.923,23.5765]]],[[[-162.6815,6.3775],[-162.664,6.306],[-162.622,6.2455],[-162.5615,6.204],[-162.4695,6.1795],[-162.3485,6.1795],[-162.2775,6.191],[-162.217,6.222],[-162.1705,6.2715],[-162.134,6.3725],[-162.141,6.445],[-162.1685,6.5075],[-162.2185,6.563],[-162.3105,6.629],[-162.369,6.6485],[-162.446,6.649],[-162.5115,6.626],[-162.6195,6.54],[-162.669,6.4595],[-162.6815,6.3775]]],[[[-162.329,5.8885],[-162.307,5.797],[-162.2635,5.7375],[-162.2205,5.702],[-162.1605,5.6745],[-162.014,5.664],[-161.973,5.672],[-161.8985,5.7115],[-161.8555,5.7625],[-161.8265,5.853],[-161.8385,5.937],[-161.8665,5.995],[-161.9555,6.076],[-162.021,6.098],[-162.1715,6.087],[-162.2345,6.06],[-162.282,6.0185],[-162.3145,5.9645],[-162.329,5.8885]]],[[[-162.1455,23.071],[-162.1365,23.003],[-162.1045,22.9415],[-162.052,22.8935],[-161.9855,22.8635],[-161.912,22.8555],[-161.8365,22.8705],[-161.7715,22.908],[-161.724,22.964],[-161.7,23.0315],[-161.7025,23.1025],[-161.731,23.1685],[-161.7825,23.2215],[-161.8505,23.255],[-161.927,23.2655],[-161.9995,23.253],[-162.064,23.219],[-162.1125,23.168],[-162.1455,23.071]]],[[[-160.7615,21.6615],[-160.7505,21.592],[-160.715,21.53],[-160.6585,21.4825],[-160.551,21.451],[-160.4755,21.46],[-160.408,21.4925],[-160.3565,21.544],[-160.3265,21.6085],[-160.262,21.582],[-160.1925,21.576],[-160.092,21.605],[-160.0385,21.647],[-160.0015,21.7025],[-159.916,21.7575],[-159.878,21.812],[-159.7925,21.777],[-159.7455,21.7365],[-159.6365,21.6925],[-159.44,21.667],[-159.3815,21.6775],[-159.268,21.7325],[-159.177,21.8155],[-159.1195,21.9185],[-159.1145,21.985],[-159.078,22.1025],[-159.078,22.1645],[-159.124,22.279],[-159.219,22.3765],[-159.336,22.428],[-159.3985,22.4345],[-159.589,22.4305],[-159.705,22.3945],[-159.755,22.357],[-159.84,22.319],[-159.909,22.2605],[-159.957,22.1855],[-160.0615,22.228],[-160.138,22.227],[-160.21,22.202],[-160.268,22.156],[-160.306,22.0945],[-160.3885,22.031],[-160.4435,21.9455],[-160.464,21.848],[-160.5765,21.8585],[-160.648,21.835],[-160.707,21.791],[-160.7465,21.7305],[-160.7615,21.6615]]],[[[-160.2135,-0.3785],[-160.2005,-0.451],[-160.1475,-0.5285],[-160.0875,-0.5665],[-159.9755,-0.5815],[-159.879,-0.5505],[-159.825,-0.501],[-159.8005,-0.458],[-159.785,-0.3565],[-159.798,-0.295],[-159.8565,-0.2125],[-159.9225,-0.1745],[-159.9895,-0.1625],[-160.076,-0.176],[-160.1505,-0.227],[-160.207,-0.326],[-160.2135,-0.3785]]],[[[-158.4945,21.565],[-158.479,21.498],[-158.3715,21.3095],[-158.323,21.2525],[-158.285,21.181],[-158.227,21.129],[-158.112,21.092],[-157.991,21.1065],[-157.8905,21.065],[-157.8145,21.051],[-157.697,21.059],[-157.6195,21.0745],[-157.5505,21.1115],[-157.528,21.1055],[-157.523,21.04],[-157.494,20.9805],[-157.4455,20.933],[-157.3825,20.904],[-157.2785,20.885],[-157.2495,20.798],[-157.1975,20.7235],[-157.1475,20.6265],[-157.0845,20.573],[-157.008,20.5395],[-156.9165,20.533],[-156.9,20.456],[-156.8575,20.388],[-156.7945,20.336],[-156.6755,20.3],[-156.489,20.316],[-156.3895,20.373],[-156.3135,20.379],[-156.1665,20.4175],[-156.119,20.4215],[-155.929,20.474],[-155.8465,20.5315],[-155.789,20.6105],[-155.763,20.7035],[-155.771,20.799],[-155.8125,20.8865],[-155.883,20.956],[-155.974,21.0],[-156.132,21.1075],[-156.23,21.143],[-156.335,21.1495],[-156.387,21.142],[-156.503,21.2155],[-156.5605,21.2925],[-156.641,21.348],[-156.737,21.3765],[-156.8375,21.3745],[-156.933,21.413],[-157.003,21.4145],[-157.0715,21.3955],[-157.178,21.4065],[-157.3135,21.39],[-157.4355,21.3345],[-157.4575,21.4105],[-157.503,21.4775],[-157.5165,21.5415],[-157.551,21.598],[-157.603,21.642],[-157.6675,21.6675],[-157.744,21.785],[-157.848,21.8805],[-157.9375,21.9095],[-158.0345,21.908],[-158.126,21.877],[-158.201,21.82],[-158.2295,21.7835],[-158.303,21.784],[-158.373,21.763],[-158.432,21.722],[-158.474,21.6655],[-158.4945,21.565]]],[[[-157.747,55.7925],[-157.736,55.744],[-157.663,55.664],[-157.578,55.622],[-157.422,55.5925],[-157.317,55.5965],[-157.176,55.633],[-157.092,55.6835],[-157.0535,55.727],[-157.035,55.81],[-157.0935,55.9045],[-157.1845,55.9565],[-157.321,55.9895],[-157.4445,55.9905],[-157.6015,55.954],[-157.68,55.91],[-157.738,55.837],[-157.747,55.7925]]],[[[-156.2695,19.757],[-156.259,19.645],[-156.21,19.543],[-156.1725,19.4995],[-156.146,19.403],[-156.1035,19.3115],[-156.1245,19.192],[-156.1225,19.07],[-156.0765,18.956],[-155.9915,18.8635],[-155.7725,18.7295],[-155.6765,18.7085],[-155.6115,18.718],[-155.491,18.7875],[-155.438,18.839],[-155.3865,18.9305],[-155.3385,18.98],[-155.215,19.056],[-155.0905,19.075],[-154.871,19.1735],[-154.778,19.2405],[-154.6565,19.3485],[-154.604,19.453],[-154.593,19.53],[-154.635,19.641],[-154.693,19.6975],[-154.7675,19.733],[-154.841,19.8785],[-154.9395,20.01],[-155.0605,20.124],[-155.201,20.2165],[-155.356,20.285],[-155.521,20.328],[-155.6625,20.4235],[-155.8065,20.4665],[-155.9095,20.465],[-156.004,20.422],[-156.044,20.3895],[-156.1,20.306],[-156.119,20.2115],[-156.1095,20.1175],[-156.078,20.025],[-156.1735,19.958],[-156.2395,19.865],[-156.2695,19.757]]],[[[-156.129,55.8455],[-156.106,55.768],[-155.9675,55.652],[-155.8445,55.601],[-155.6685,55.5715],[-155.4745,55.578],[-155.3725,55.609],[-155.2785,55.663],[-155.2125,55.7395],[-155.178,55.819],[-155.193,55.9415],[-155.27,56.0455],[-155.4285,56.1115],[-155.606,56.127],[-155.776,56.0975],[-156.0165,55.9935],[-156.11,55.9105],[-156.129,55.8455]]],[[[-146.7845,59.405],[-146.7705,59.3535],[-146.7165,59.294],[-146.588,59.233],[-146.456,59.2055],[-146.3145,59.202],[-146.0785,59.247],[-145.936,59.314],[-145.872,59.363],[-145.8345,59.447],[-145.878,59.5505],[-145.973,59.65],[-146.0585,59.704],[-146.222,59.747],[-146.3655,59.7515],[-146.497,59.731],[-146.5765,59.704],[-146.662,59.65],[-146.6975,59.6045],[-146.708,59.533],[-146.746,59.496],[-146.7845,59.405]]],[[[-125.0035,48.495],[-125.034,48.45],[-125.0435,48.3635],[-125.021,48.308],[-125.074,48.2365],[-125.084,48.1845],[-125.06,48.1065],[-125.017,48.048],[-125.028,47.998],[-125.004,47.9125],[-124.9415,47.83],[-124.8625,47.7505],[-124.7895,47.697],[-124.7605,47.5905],[-124.6805,47.5225],[-124.699,47.471],[-124.6885,47.395],[-124.5485,47.2075],[-124.4935,47.156],[-124.4685,47.067],[-124.4725,46.937],[-124.4625,46.8745],[-124.3945,46.7895],[-124.38,46.6405],[-124.3595,46.5855],[-124.3465,46.431],[-124.3745,46.301],[-124.3755,46.242],[-124.32,46.136],[-124.234,46.07],[-124.2855,46.0115],[-124.3055,45.9265],[-124.2725,45.844],[-124.2845,45.7605],[-124.2345,45.6515],[-124.277,45.467],[-124.289,45.299],[-124.267,45.252],[-124.266,45.1685],[-124.3015,45.0735],[-124.3125,44.9325],[-124.3425,44.8765],[-124.359,44.7805],[-124.357,44.5735],[-124.374,44.5115],[-124.3645,44.432],[-124.392,44.3185],[-124.408,44.0895],[-124.4215,43.983],[-124.4615,43.764],[-124.4885,43.711],[-124.504,43.6055],[-124.5455,43.5075],[-124.5935,43.474],[-124.6635,43.394],[-124.6845,43.2985],[-124.6765,43.232],[-124.708,43.1915],[-124.729,43.1205],[-124.721,43.065],[-124.74,43.021],[-124.8555,42.8935],[-124.896,42.8265],[-124.9075,42.768],[-124.891,42.6995],[-124.817,42.6195],[-124.721,42.5785],[-124.7695,42.483],[-124.7615,42.4105],[-124.7035,42.323],[-124.68,42.19],[-124.636,42.1165],[-124.6085,42.02],[-124.5715,41.974],[-124.623,41.915],[-124.642,41.861],[-124.632,41.778],[-124.5415,41.6705],[-124.4015,41.5925],[-124.361,41.486],[-124.399,41.4525],[-124.4415,41.3685],[-124.4405,41.309],[-124.409,41.242],[-124.45,41.138],[-124.444,41.064],[-124.399,40.964],[-124.4725,40.865],[-124.503,40.801],[-124.5995,40.6435],[-124.692,40.5545],[-124.7315,40.445],[-124.694,40.3455],[-124.628,40.2895],[-124.618,40.1905],[-124.546,40.103],[-124.326,39.9595],[-124.261,39.8825],[-124.2,39.846],[-124.0975,39.723],[-124.0515,39.6135],[-124.048,39.5645],[-124.077,39.4725],[-124.082,39.308],[-124.016,39.127],[-123.9735,39.0525],[-124.0005,38.987],[-123.994,38.907],[-123.9375,38.806],[-123.84,38.711],[-123.705,38.6045],[-123.6505,38.5705],[-123.542,38.449],[-123.423,38.359],[-123.3325,38.308],[-123.3155,38.25],[-123.2195,38.136],[-123.274,38.0305],[-123.2635,37.93],[-123.3175,37.884],[-123.348,37.8335],[-123.358,37.746],[-123.3365,37.686],[-123.292,37.634],[-123.13,37.52],[-123.0565,37.4975],[-122.9515,37.4975],[-122.8655,37.526],[-122.777,37.6025],[-122.7665,37.482],[-122.736,37.414],[-122.6595,37.318],[-122.672,37.2215],[-122.6225,37.09],[-122.544,36.9935],[-122.442,36.9255],[-122.3915,36.8755],[-122.3005,36.814],[-122.2325,36.782],[-122.2015,36.676],[-122.2275,36.6075],[-122.2245,36.545],[-122.1565,36.356],[-122.131,36.227],[-122.078,36.157],[-121.9785,36.0825],[-121.8595,36.0215],[-121.7745,35.903],[-121.718,35.857],[-121.666,35.7645],[-121.5565,35.6685],[-121.499,35.5615],[-121.443,35.5085],[-121.3295,35.4625],[-121.2675,35.3955],[-121.143,35.295],[-121.1425,35.213],[-121.0915,35.1205],[-121.0415,35.0715],[-120.8995,34.989],[-120.9145,34.92],[-120.905,34.8405],[-120.8795,34.794],[-120.8695,34.687],[-120.894,34.569],[-120.8795,34.5075],[-120.8375,34.446],[-120.764,34.388],[-120.691,34.3605],[-120.617,34.286],[-120.702,34.2335],[-120.756,34.1435],[-120.7565,34.0635],[-120.7345,34.011],[-120.623,33.886],[-120.499,33.83],[-120.3925,33.8155],[-120.3335,33.7685],[-120.2305,33.7165],[-120.1445,33.695],[-120.0785,33.696],[-119.882,33.755],[-119.8085,33.75],[-119.663,33.7645],[-119.595,33.787],[-119.4745,33.808],[-119.328,33.809],[-119.239,33.8405],[-119.162,33.8975],[-119.04,33.8585],[-118.961,33.844],[-118.8935,33.811],[-118.7825,33.799],[-118.6595,33.8305],[-118.669,33.7815],[-118.642,33.6775],[-118.764,33.6305],[-118.8145,33.579],[-118.85,33.6225],[-118.923,33.6685],[-119.021,33.6895],[-119.149,33.6655],[-119.211,33.6305],[-119.261,33.5745],[-119.283,33.5215],[-119.2795,33.408],[-119.4495,33.477],[-119.509,33.4885],[-119.558,33.527],[-119.6295,33.5555],[-119.7575,33.5565],[-119.8525,33.514],[-119.903,33.4625],[-119.9345,33.362],[-119.9255,33.3065],[-119.886,33.2415],[-119.819,33.1905],[-119.7775,33.1395],[-119.6995,33.079],[-119.639,33.048],[-119.4865,33.0145],[-119.3495,33.034],[-119.236,33.102],[-119.1945,33.166],[-119.184,33.2105],[-119.193,33.287],[-119.0535,33.2625],[-118.982,33.2695],[-118.8855,33.308],[-118.8055,33.363],[-118.7065,33.2725],[-118.677,33.2275],[-118.7545,33.1965],[-118.814,33.143],[-118.8395,33.0995],[-118.8485,32.999],[-118.816,32.9205],[-118.75,32.812],[-118.6755,32.7145],[-118.5625,32.633],[-118.4565,32.5995],[-118.3745,32.603],[-118.243,32.639],[-118.1635,32.694],[-118.1185,32.768],[-118.1185,32.872],[-118.153,32.935],[-118.319,33.0715],[-118.2065,33.123],[-118.1345,33.1675],[-118.082,33.236],[-118.068,33.284],[-118.0705,33.365],[-118.1035,33.4365],[-118.0685,33.444],[-117.938,33.378],[-117.8505,33.2925],[-117.7915,33.267],[-117.674,33.1885],[-117.544,33.0325],[-117.5025,32.9195],[-117.521,32.8395],[-117.4865,32.654],[-117.465,32.5905],[-117.264,32.5535],[-117.239,32.5275],[-117.1245,32.5345],[-116.3375,32.6],[-115.718,32.6485],[-115.1805,32.6875],[-114.72,32.7185],[-114.765,32.643],[-114.8105,32.61],[-114.8135,32.494],[-114.4385,32.381],[-113.924,32.2235],[-113.1485,31.9805],[-112.3995,31.752],[-111.864,31.5845],[-111.075,31.332],[-110.456,31.333],[-109.7465,31.334],[-109.2785,31.334],[-108.851,31.3325],[-108.2085,31.3335],[-108.2085,31.7835],[-107.8655,31.7835],[-107.2905,31.7835],[-106.529,31.784],[-106.49,31.7485],[-106.455,31.7645],[-106.382,31.7325],[-106.308,31.6295],[-106.2795,31.561],[-106.245,31.539],[-106.219,31.4795],[-106.0775,31.398],[-106.0055,31.3925],[-105.955,31.3655],[-105.9315,31.313],[-105.8705,31.29],[-105.7695,31.165],[-105.649,31.116],[-105.606,31.085],[-105.556,30.989],[-105.4,30.8895],[-105.387,30.8535],[-105.2665,30.8085],[-105.196,30.792],[-105.13,30.7505],[-105.0525,30.6815],[-105.007,30.6865],[-104.9715,30.61],[-104.8995,30.571],[-104.872,30.5105],[-104.8595,30.3905],[-104.811,30.363],[-104.812,30.333],[-104.761,30.3],[-104.687,30.179],[-104.6905,30.016],[-104.68,29.9205],[-104.6335,29.8705],[-104.5655,29.7695],[-104.5445,29.681],[-104.5125,29.6465],[-104.3985,29.5715],[-104.3375,29.5195],[-104.262,29.5135],[-104.209,29.481],[-104.1605,29.39],[-103.975,29.296],[-103.783,29.264],[-103.719,29.181],[-103.5925,29.15],[-103.557,29.1555],[-103.5005,29.116],[-103.474,29.0705],[-103.416,29.038],[-103.3115,29.0135],[-103.2505,28.9805],[-103.1525,28.972],[-103.098,29.0265],[-103.08,29.0875],[-103.032,29.104],[-102.99,29.1835],[-102.955,29.1775],[-102.868,29.223],[-102.9065,29.2615],[-102.877,29.355],[-102.8395,29.359],[-102.8085,29.523],[-102.7785,29.5435],[-102.742,29.633],[-102.6935,29.6765],[-102.677,29.7415],[-102.548,29.7445],[-102.4875,29.7865],[-102.404,29.7655],[-102.34,29.8695],[-102.3005,29.8775],[-102.144,29.8035],[-102.0735,29.7865],[-101.9815,29.815],[-101.9545,29.796],[-101.8035,29.78],[-101.774,29.789],[-101.71,29.7615],[-101.6615,29.771],[-101.3995,29.76],[-101.3945,29.711],[-101.3645,29.6655],[-101.3005,29.638],[-101.312,29.5865],[-101.2415,29.5645],[-101.2505,29.5195],[-101.193,29.5205],[-101.1485,29.4755],[-101.06,29.4585],[-101.005,29.3655],[-100.8865,29.3075],[-100.8785,29.281],[-100.795,29.2435],[-100.76,29.1575],[-100.685,29.1115],[-100.664,29.075],[-100.6455,28.988],[-100.6515,28.946],[-100.5465,28.825],[-100.5145,28.752],[-100.5005,28.662],[-100.447,28.609],[-100.3985,28.5855],[-100.411,28.5495],[-100.336,28.43],[-100.3495,28.404],[-100.288,28.316],[-100.294,28.2845],[-100.212,28.1945],[-100.0885,28.148],[-100.0525,28.0845],[-100.018,28.065],[-99.9905,27.9935],[-99.9335,27.9815],[-99.9375,27.9415],[-99.8935,27.899],[-99.875,27.7965],[-99.8135,27.7745],[-99.7215,27.666],[-99.6005,27.6415],[-99.539,27.603],[-99.511,27.565],[-99.528,27.499],[-99.4785,27.4795],[-99.504,27.339],[-99.4965,27.2735],[-99.442,27.251],[-99.426,27.1755],[-99.4285,27.092],[-99.453,27.063],[-99.445,27.02],[-99.3835,26.9775],[-99.388,26.9425],[-99.3305,26.9205],[-99.3285,26.8785],[-99.2685,26.8435],[-99.24,26.746],[-99.209,26.725],[-99.2,26.656],[-99.178,26.6205],[-99.1675,26.5365],[-99.1275,26.525],[-99.0915,26.476],[-99.111,26.427],[-99.0835,26.3975],[-99.04,26.413],[-98.9785,26.4],[-98.889,26.357],[-98.825,26.3705],[-98.789,26.3305],[-98.7445,26.3155],[-98.678,26.2435],[-98.4805,26.22],[-98.3845,26.157],[-98.3385,26.153],[-98.3225,26.1195],[-98.249,26.072],[-98.1585,26.0535],[-98.105,26.0675],[-98.0385,26.0415],[-98.0005,26.0655],[-97.9645,26.052],[-97.875,26.0635],[-97.793,26.0535],[-97.779,26.03],[-97.6675,26.0245],[-97.556,25.937],[-97.5105,25.8875],[-97.456,25.884],[-97.4435,25.848],[-97.3715,25.842],[-97.367,25.9035],[-97.2755,25.952],[-97.178,25.962],[-96.924,25.9755],[-96.9225,26.0645],[-96.9465,26.158],[-96.9595,26.258],[-97.0,26.4325],[-97.1095,26.764],[-97.1445,26.909],[-97.153,26.987],[-97.144,27.171],[-97.0975,27.3285],[-96.9845,27.5555],[-96.915,27.6595],[-96.8575,27.706],[-96.812,27.8005],[-96.7115,27.9025],[-96.605,27.9885],[-96.4955,28.062],[-96.2805,28.1695],[-96.125,28.3055],[-95.939,28.3925],[-95.702,28.4895],[-95.3685,28.662],[-95.238,28.7065],[-95.1275,28.7895],[-95.0875,28.839],[-94.951,28.9345],[-94.887,28.989],[-94.646,29.1305],[-94.556,29.165],[-94.5035,29.2125],[-94.454,29.305],[-94.0215,29.469],[-93.9635,29.4815],[-93.868,29.446],[-93.8,29.4445],[-93.719,29.466],[-93.6665,29.498],[-93.6195,29.5515],[-93.453,29.5695],[-93.3585,29.545],[-93.2795,29.552],[-93.2235,29.573],[-93.0505,29.5265],[-92.8245,29.431],[-92.721,29.398],[-92.6155,29.377],[-92.36,29.3365],[-92.268,29.3325],[-92.143,29.362],[-92.0555,29.3105],[-92.0185,29.267],[-91.9575,29.228],[-91.838,29.2055],[-91.7685,29.2185],[-91.6525,29.2145],[-91.521,29.176],[-91.471,29.127],[-91.3935,29.075],[-91.184,29.0035],[-91.1495,28.9425],[-91.1005,28.8985],[-90.982,28.8485],[-90.9105,28.8385],[-90.7435,28.8385],[-90.567,28.875],[-90.5115,28.8595],[-90.3965,28.8495],[-90.2945,28.8605],[-90.122,28.9005],[-89.961,28.996],[-89.635,29.0435],[-89.663,28.898],[-89.651,28.8435],[-89.602,28.772],[-89.533,28.7255],[-89.481,28.709],[-89.398,28.706],[-89.328,28.725],[-89.245,28.7845],[-89.1645,28.7665],[-89.052,28.785],[-88.993,28.82],[-88.851,28.9755],[-88.7965,29.0205],[-88.746,29.115],[-88.737,29.208],[-88.7595,29.296],[-88.8475,29.3805],[-88.905,29.403],[-88.788,29.5185],[-88.7395,29.585],[-88.6835,29.637],[-88.6445,29.693],[-88.6095,29.7765],[-88.5875,29.894],[-88.6015,30.02],[-88.4585,29.995],[-88.383,30.002],[-88.2985,30.028],[-88.2095,30.0395],[-88.08,30.008],[-87.9335,30.0305],[-87.8275,30.026],[-87.597,30.056],[-87.299,30.117],[-87.1175,30.13],[-86.8745,30.174],[-86.644,30.196],[-86.511,30.1805],[-86.4525,30.1825],[-86.3245,30.1615],[-86.1705,30.1155],[-86.0525,30.0705],[-85.9215,30.001],[-85.8665,29.9555],[-85.745,29.894],[-85.6465,29.8105],[-85.6295,29.721],[-85.5925,29.6235],[-85.5055,29.511],[-85.4435,29.474],[-85.342,29.455],[-85.269,29.4665],[-85.1155,29.3935],[-85.0235,29.387],[-84.868,29.427],[-84.7685,29.4785],[-84.6975,29.5015],[-84.6145,29.548],[-84.558,29.5955],[-84.4605,29.635],[-84.3885,29.693],[-84.3165,29.697],[-84.22,29.729],[-84.174,29.764],[-84.12,29.84],[-84.055,29.863],[-83.9485,29.7985],[-83.828,29.754],[-83.7705,29.6375],[-83.734,29.5955],[-83.6355,29.5265],[-83.6235,29.4535],[-83.5845,29.381],[-83.517,29.314],[-83.4105,29.233],[-83.3805,29.1605],[-83.3355,29.113],[-83.3275,29.067],[-83.2895,28.989],[-83.231,28.929],[-83.1805,28.8995],[-83.065,28.8715],[-82.99,28.874],[-82.985,28.7815],[-82.9425,28.6285],[-82.912,28.5655],[-82.927,28.466],[-82.9605,28.404],[-83.022,28.355],[-83.05,28.313],[-83.0785,28.214],[-83.0725,28.071],[-83.0555,28.0045],[-83.0755,27.9085],[-83.0725,27.8335],[-83.035,27.7375],[-82.976,27.669],[-82.992,27.5955],[-82.9855,27.539],[-82.945,27.4425],[-82.878,27.325],[-82.7875,27.2245],[-82.7615,27.1715],[-82.7155,27.1235],[-82.6455,26.969],[-82.4895,26.7295],[-82.493,26.6505],[-82.384,26.3945],[-82.3505,26.346],[-82.25,26.269],[-82.161,26.2325],[-82.0455,26.2235],[-82.0235,26.067],[-81.9795,25.957],[-81.96,25.8755],[-81.918,25.8],[-81.8445,25.694],[-81.7385,25.6305],[-81.638,25.616],[-81.574,25.6245],[-81.508,25.5445],[-81.435,25.477],[-81.373,25.351],[-81.395,25.2215],[-81.3765,25.1475],[-81.3355,25.0615],[-81.446,25.052],[-81.722,24.9045],[-81.912,24.8135],[-82.033,24.7805],[-82.1185,24.7985],[-82.2455,24.7725],[-82.3085,24.7265],[-82.375,24.6245],[-82.3825,24.518],[-82.3645,24.466],[-82.2805,24.378],[-82.1955,24.349],[-82.1075,24.347],[-82.051,24.329],[-81.964,24.268],[-81.901,24.253],[-81.7935,24.2635],[-81.743,24.2815],[-81.6765,24.284],[-81.56,24.312],[-81.464,24.3515],[-81.398,24.3455],[-81.3015,24.3695],[-81.2125,24.449],[-81.127,24.427],[-81.012,24.4485],[-80.9645,24.4785],[-80.913,24.542],[-80.7725,24.61],[-80.677,24.6395],[-80.535,24.723],[-80.389,24.821],[-80.297,24.831],[-80.2075,24.8835],[-80.1725,24.9295],[-80.148,25.021],[-80.072,25.062],[-80.007,25.144],[-79.993,25.247],[-80.0145,25.3105],[-79.9515,25.3915],[-79.9285,25.5005],[-79.939,25.547],[-79.9315,25.664],[-79.9045,25.744],[-79.8895,26.044],[-79.8595,26.23],[-79.841,26.403],[-79.8135,26.5865],[-79.8065,26.7845],[-79.832,26.919],[-79.881,27.082],[-79.9255,27.1795],[-79.937,27.233],[-80.0605,27.489],[-80.1745,27.817],[-80.2965,28.042],[-80.35,28.1545],[-80.38,28.2985],[-80.323,28.362],[-80.297,28.464],[-80.3055,28.5135],[-80.3705,28.6715],[-80.512,28.857],[-80.684,29.118],[-80.7205,29.193],[-80.7545,29.227],[-80.842,29.4005],[-80.952,29.6335],[-81.032,29.831],[-81.0445,29.9355],[-81.081,30.007],[-81.16,30.3255],[-81.15,30.438],[-81.21,30.5845],[-81.171,30.711],[-81.1965,30.8045],[-81.1335,30.909],[-81.133,31.0265],[-81.118,31.066],[-81.0475,31.1485],[-81.0005,31.37],[-80.9415,31.4425],[-80.895,31.575],[-80.8885,31.622],[-80.811,31.7255],[-80.776,31.7455],[-80.6475,31.878],[-80.5765,32.0],[-80.496,32.033],[-80.4305,32.0745],[-80.3965,32.1145],[-80.3185,32.14],[-80.226,32.224],[-80.2055,32.2725],[-80.117,32.344],[-80.0375,32.367],[-79.977,32.4045],[-79.899,32.423],[-79.807,32.4855],[-79.646,32.5775],[-79.6,32.6325],[-79.5115,32.7025],[-79.4435,32.7385],[-79.374,32.7985],[-79.278,32.8095],[-79.206,32.843],[-79.1655,32.878],[-79.1275,32.941],[-78.9595,33.0565],[-78.901,33.159],[-78.919,33.2725],[-78.912,33.33],[-78.8175,33.4275],[-78.7585,33.506],[-78.665,33.585],[-78.5495,33.6415],[-78.4665,33.653],[-78.396,33.6855],[-78.29,33.7095],[-78.174,33.7135],[-78.061,33.637],[-77.974,33.6185],[-77.9095,33.6225],[-77.79,33.677],[-77.742,33.734],[-77.7165,33.844],[-77.627,34.044],[-77.5725,34.124],[-77.5135,34.1815],[-77.406,34.2605],[-77.214,34.3545],[-77.1125,34.4165],[-76.968,34.467],[-76.762,34.4965],[-76.7165,34.4315],[-76.612,34.3735],[-76.52,34.363],[-76.458,34.3725],[-76.3735,34.412],[-76.3315,34.451],[-76.2865,34.5585],[-76.1555,34.7005],[-75.979,34.839],[-75.7955,34.951],[-75.597,35.0225],[-75.528,35.0145],[-75.445,35.0265],[-75.368,35.064],[-75.328,35.1],[-75.291,35.1665],[-75.2415,35.383],[-75.215,35.5625],[-75.239,35.723],[-75.271,35.812],[-75.4405,36.116],[-75.4905,36.1935],[-75.542,36.3025],[-75.576,36.398],[-75.645,36.669],[-75.718,36.8405],[-75.7395,36.9235],[-75.723,36.974],[-75.608,37.073],[-75.575,37.118],[-75.554,37.1905],[-75.4945,37.254],[-75.4375,37.355],[-75.411,37.4295],[-75.374,37.4725],[-75.343,37.542],[-75.343,37.6065],[-75.3195,37.6625],[-75.217,37.6975],[-75.162,37.7385],[-74.9825,37.9815],[-74.9155,38.126],[-74.846,38.2555],[-74.8075,38.3655],[-74.794,38.467],[-74.82,38.734],[-74.7005,38.781],[-74.592,38.8625],[-74.532,38.9525],[-74.4655,39.027],[-74.416,39.113],[-74.347,39.163],[-74.251,39.205],[-74.1205,39.318],[-74.0125,39.46],[-73.8515,39.6855],[-73.8395,39.7155],[-73.819,39.883],[-73.771,40.0885],[-73.7375,40.1915],[-73.7115,40.319],[-73.711,40.3825],[-73.589,40.3735],[-73.4905,40.384],[-73.3335,40.4215],[-73.2095,40.425],[-72.9725,40.4765],[-72.81,40.532],[-72.3795,40.652],[-72.1235,40.746],[-71.7435,40.878],[-71.645,40.9495],[-71.5255,40.9505],[-71.414,40.98],[-71.3315,41.035],[-71.2975,41.0805],[-71.278,41.152],[-71.285,41.2065],[-71.1495,41.2505],[-71.094,41.222],[-71.0785,41.1835],[-70.9985,41.1005],[-70.898,41.059],[-70.8005,41.049],[-70.676,41.074],[-70.582,41.1395],[-70.4605,41.1135],[-70.3985,41.1175],[-70.2465,41.064],[-70.145,41.043],[-70.03,41.039],[-69.883,41.059],[-69.783,41.107],[-69.706,41.209],[-69.6955,41.29],[-69.717,41.3575],[-69.7695,41.439],[-69.683,41.593],[-69.6625,41.6705],[-69.6665,41.816],[-69.7035,41.9515],[-69.7585,42.062],[-69.832,42.153],[-69.9045,42.2065],[-69.99,42.2475],[-70.1565,42.283],[-70.3135,42.269],[-70.4625,42.28],[-70.476,42.302],[-70.419,42.47],[-70.3545,42.519],[-70.3085,42.595],[-70.296,42.664],[-70.312,42.7415],[-70.398,42.83],[-70.3645,42.865],[-70.328,42.951],[-70.2645,42.9915],[-70.215,43.055],[-70.2,43.1275],[-70.23,43.2125],[-70.0125,43.411],[-69.804,43.496],[-69.686,43.5105],[-69.578,43.5515],[-69.4485,43.5765],[-69.302,43.5545],[-69.1625,43.589],[-69.0805,43.648],[-69.006,43.611],[-68.9035,43.585],[-68.812,43.5845],[-68.701,43.6165],[-68.647,43.651],[-68.5945,43.714],[-68.5275,43.755],[-68.4685,43.828],[-68.3795,43.881],[-68.3185,43.821],[-68.215,43.777],[-68.1195,43.767],[-68.0115,43.786],[-67.895,43.859],[-67.849,43.9555],[-67.859,44.025],[-67.885,44.0695],[-67.9585,44.13],[-67.8675,44.1625],[-67.7565,44.1785],[-67.654,44.232],[-67.5565,44.2405],[-67.376,44.304],[-67.301,44.3595],[-67.304,44.3935],[-67.2525,44.4965],[-67.157,44.5825],[-67.039,44.6245],[-66.987,44.6955],[-66.8855,44.794],[-66.9655,44.829],[-66.9665,44.91],[-67.0215,44.954],[-67.1125,45.1125],[-67.159,45.162],[-67.2275,45.1635],[-67.291,45.1875],[-67.321,45.1315],[-67.3805,45.152],[-67.464,45.245],[-67.4765,45.2755],[-67.429,45.3445],[-67.427,45.39],[-67.4725,45.423],[-67.5,45.491],[-67.4275,45.501],[-67.4205,45.5495],[-67.4565,45.6045],[-67.499,45.5865],[-67.675,45.6305],[-67.713,45.681],[-67.8035,45.6775],[-67.802,45.803],[-67.7555,45.8235],[-67.8045,45.869],[-67.751,45.918],[-67.7815,45.9435],[-67.782,46.263],[-67.7885,46.6015],[-67.79,47.067],[-67.883,47.1045],[-67.952,47.1945],[-68.2235,47.3445],[-68.3355,47.3595],[-68.3845,47.324],[-68.378,47.2875],[-68.517,47.296],[-68.58,47.287],[-68.6075,47.247],[-68.687,47.2445],[-68.8115,47.215],[-68.905,47.1805],[-69.041,47.245],[-69.0545,47.3765],[-69.0355,47.415],[-69.176,47.457],[-69.2245,47.46],[-69.587,47.1045],[-69.997,46.6955],[-70.0565,46.4165],[-70.095,46.41],[-70.149,46.359],[-70.209,46.3295],[-70.2925,46.1915],[-70.2395,46.149],[-70.2895,46.0945],[-70.3135,46.0225],[-70.3125,45.962],[-70.2395,45.94],[-70.254,45.9025],[-70.417,45.7955],[-70.4005,45.7195],[-70.553,45.668],[-70.6455,45.6065],[-70.7225,45.5135],[-70.681,45.452],[-70.6245,45.406],[-70.6505,45.377],[-70.712,45.3905],[-70.7815,45.431],[-70.8255,45.4005],[-70.806,45.3215],[-70.8845,45.235],[-70.918,45.3115],[-70.989,45.334],[-71.098,45.3015],[-71.133,45.2445],[-71.2305,45.2495],[-71.288,45.301],[-71.3565,45.254],[-71.3975,45.2055],[-71.4295,45.1225],[-71.4955,45.065],[-71.501,45.0135],[-71.914,45.0075],[-72.312,45.004],[-72.6345,45.0145],[-73.0655,45.016],[-73.65,45.003],[-73.883,45.001],[-74.15,44.9915],[-74.6835,44.9995],[-74.731,44.9905],[-74.8265,45.016],[-74.908,44.9835],[-74.9725,44.9835],[-75.14,44.897],[-75.2185,44.878],[-75.505,44.7055],[-75.767,44.5155],[-75.8215,44.432],[-75.913,44.368],[-76.001,44.348],[-76.162,44.2805],[-76.1645,44.24],[-76.2455,44.204],[-76.3125,44.199],[-76.353,44.1345],[-76.439,44.094],[-76.7965,43.631],[-77.442,43.631],[-77.928,43.631],[-78.6905,43.631],[-79.2005,43.4505],[-79.0775,43.2725],[-79.056,43.2545],[-79.0425,43.1435],[-79.074,43.078],[-79.011,43.066],[-79.0165,42.984],[-78.9325,42.956],[-78.906,42.9],[-78.9355,42.8285],[-79.5705,42.5875],[-80.08,42.3935],[-80.521,42.323],[-81.2455,42.2075],[-81.799,41.953],[-81.8545,41.933],[-82.269,41.7415],[-82.3975,41.6765],[-82.6795,41.6765],[-83.069,41.8635],[-83.1495,42.041],[-83.1215,42.1255],[-83.128,42.2385],[-83.0635,42.318],[-82.83,42.3735],[-82.668,42.5335],[-82.6055,42.5485],[-82.5235,42.6075],[-82.4675,42.7625],[-82.482,42.8085],[-82.4555,42.927],[-82.413,42.9775],[-82.414,43.011],[-82.123,43.591],[-82.25,44.165],[-82.4085,44.8585],[-82.5185,45.3385],[-82.757,45.4455],[-83.597,45.8215],[-83.434,45.998],[-83.572,46.106],[-83.655,46.1215],[-83.76,46.1025],[-83.8265,46.119],[-83.9035,46.0605],[-83.9555,46.057],[-84.006,46.1495],[-84.077,46.1875],[-84.108,46.2415],[-84.146,46.419],[-84.111,46.504],[-84.129,46.5305],[-84.226,46.534],[-84.2935,46.493],[-84.374,46.509],[-84.445,46.489],[-84.4755,46.453],[-84.557,46.461],[-84.763,46.6345],[-84.86,46.889],[-85.319,47.0795],[-87.1125,47.818],[-87.6475,48.029],[-88.37,48.306],[-88.6775,48.2455],[-89.3375,47.9745],[-89.489,48.0145],[-89.5855,48.002],[-89.7675,48.023],[-89.899,47.988],[-89.994,48.028],[-90.0235,48.085],[-90.1325,48.1115],[-90.306,48.105],[-90.375,48.091],[-90.6415,48.1035],[-90.7615,48.0985],[-90.8325,48.1735],[-90.8395,48.2395],[-90.8855,48.246],[-91.0825,48.181],[-91.2495,48.084],[-91.371,48.0695],[-91.4295,48.0485],[-91.488,48.0685],[-91.5755,48.049],[-91.5695,48.0935],[-91.7115,48.1145],[-91.7155,48.1995],[-91.8645,48.207],[-91.893,48.238],[-91.9585,48.233],[-92.0065,48.2655],[-92.0,48.321],[-92.055,48.3595],[-92.289,48.343],[-92.3015,48.2885],[-92.2805,48.2445],[-92.375,48.226],[-92.47,48.352],[-92.4565,48.401],[-92.5075,48.448],[-92.656,48.4365],[-92.7125,48.463],[-92.637,48.4995],[-92.635,48.5425],[-92.7285,48.5395],[-92.9845,48.624],[-93.1785,48.6235],[-93.2075,48.6425],[-93.3485,48.6265],[-93.467,48.5885],[-93.4675,48.5465],[-93.6475,48.5175],[-93.7935,48.5165],[-93.804,48.57],[-93.848,48.631],[-94.2445,48.6535],[-94.261,48.6965],[-94.416,48.711],[-94.439,48.695],[-94.5875,48.7175],[-94.691,48.778],[-94.7045,48.824],[-94.6835,48.884],[-94.7495,49.099],[-94.773,49.1205],[-94.8255,49.2945],[-94.854,49.3245],[-94.9575,49.37],[-95.0585,49.3535],[-95.1535,49.3845],[-95.153,48.999],[-95.633,48.9995],[-96.6525,49.0],[-97.4815,49.0005],[-98.2105,49.0005],[-98.98,49.0],[-99.5405,48.9995],[-100.471,48.9995],[-101.17,48.9995],[-101.981,48.999],[-102.4565,48.999],[-103.3775,48.999],[-103.807,48.9995],[-104.685,48.999],[-105.561,48.9995],[-106.2295,48.9995],[-106.986,49.0],[-107.886,49.0],[-108.8355,48.9995],[-109.4205,49.0005],[-110.326,48.999],[-110.88,48.998],[-111.5755,48.9965],[-112.393,48.9985],[-112.924,48.9985],[-113.6945,48.9975],[-114.362,49.001],[-115.2625,48.9995],[-115.8615,49.001],[-116.499,49.0],[-117.135,48.999],[-117.992,49.0005],[-118.3895,49.0],[-119.047,49.0],[-120.0025,48.9995],[-120.6695,49.0],[-121.335,49.001],[-121.7595,48.9975],[-122.098,49.0025],[-122.787,49.002],[-123.322,49.002],[-123.0085,48.831],[-123.0085,48.767],[-123.268,48.694],[-123.219,48.5485],[-123.16,48.4535],[-123.115,48.423],[-123.2485,48.284],[-123.541,48.2245],[-123.679,48.24],[-124.012,48.2965],[-124.727,48.4935],[-124.8405,48.506],[-125.0035,48.495]]],[[[-83.1625,24.619],[-83.149,24.55],[-83.1005,24.4795],[-83.046,24.442],[-82.981,24.4215],[-82.8655,24.412],[-82.777,24.432],[-82.676,24.4865],[-82.6025,24.5705],[-82.5855,24.639],[-82.5915,24.6985],[-82.6345,24.777],[-82.695,24.8245],[-82.782,24.8505],[-82.8985,24.8445],[-82.984,24.828],[-83.087,24.771],[-83.1295,24.7255],[-83.1625,24.619]]],[[[-75.2385,18.3985],[-75.219,18.3255],[-75.172,18.258],[-75.122,18.2195],[-75.0375,18.1905],[-74.95,18.1965],[-74.848,18.255],[-74.805,18.3205],[-74.7905,18.418],[-74.8085,18.4855],[-74.8455,18.541],[-74.8935,18.58],[-74.9525,18.6045],[-75.02,18.6135],[-75.088,18.6055],[-75.1765,18.5545],[-75.2175,18.5],[-75.2385,18.3985]]],[[[-68.1615,18.159],[-68.155,18.063],[-68.1155,17.9645],[-68.0725,17.9215],[-67.9725,17.865],[-67.9155,17.8525],[-67.8415,17.8545],[-67.756,17.8875],[-67.669,17.974],[-67.6375,18.051],[-67.64,18.1445],[-67.6595,18.197],[-67.741,18.2865],[-67.852,18.3405],[-67.912,18.3595],[-67.9925,18.3585],[-68.0445,18.3415],[-68.132,18.2625],[-68.1615,18.159]]],[[[-64.956,18.6115],[-64.99,18.602],[-65.088,18.607],[-65.156,18.5855],[-65.2115,18.547],[-65.2705,18.546],[-65.3675,18.572],[-65.46,18.5555],[-65.5215,18.5855],[-65.6055,18.603],[-65.6675,18.597],[-65.873,18.653],[-66.212,18.686],[-66.4295,18.694],[-66.4965,18.684],[-66.6225,18.695],[-66.7265,18.685],[-66.7825,18.694],[-66.911,18.69],[-67.0345,18.7155],[-67.1335,18.715],[-67.225,18.692],[-67.272,18.665],[-67.336,18.604],[-67.3625,18.558],[-67.457,18.5905],[-67.5375,18.585],[-67.6035,18.5555],[-67.672,18.488],[-67.6965,18.4295],[-67.69,18.3215],[-67.6405,18.245],[-67.5705,18.197],[-67.5135,18.1795],[-67.4105,18.188],[-67.4005,18.1645],[-67.4185,18.0935],[-67.425,17.9405],[-67.4145,17.889],[-67.3705,17.8165],[-67.2765,17.747],[-67.19,17.729],[-67.1095,17.7425],[-66.9705,17.726],[-66.8965,17.728],[-66.7465,17.7595],[-66.6925,17.7495],[-66.608,17.693],[-66.519,17.6805],[-66.405,17.7185],[-66.3185,17.726],[-66.219,17.7115],[-66.1485,17.7255],[-65.9755,17.743],[-65.9035,17.7735],[-65.768,17.8135],[-65.7165,17.846],[-65.655,17.907],[-65.529,17.8795],[-65.4225,17.8875],[-65.207,17.938],[-65.1515,17.964],[-65.091,18.023],[-65.063,18.085],[-65.0125,18.1005],[-64.915,18.0385],[-64.861,18.0295],[-64.7775,18.042],[-64.6935,18.0975],[-64.64,18.1065],[-64.6605,18.29],[-64.6395,18.364],[-64.756,18.377],[-64.799,18.408],[-64.872,18.4085],[-64.897,18.4895],[-64.956,18.6115]]],[[[-65.114,17.68],[-65.1045,17.6205],[-65.054,17.537],[-65.021,17.5095],[-64.944,17.4775],[-64.742,17.477],[-64.5605,17.5305],[-64.459,17.5775],[-64.413,17.615],[-64.376,17.668],[-64.3555,17.7555],[-64.367,17.8215],[-64.4275,17.908],[-64.5255,17.974],[-64.6395,17.996],[-64.708,17.984],[-64.811,17.983],[-64.9495,17.9585],[-64.999,17.934],[-65.0645,17.8705],[-65.0985,17.7925],[-65.114,17.68]]],[[[144.413,13.4475],[144.4415,13.2655],[144.441,13.206],[144.4825,13.11],[144.545,13.058],[144.6405,13.033],[144.7665,13.0475],[144.8155,13.063],[144.877,13.104],[144.926,13.16],[144.9705,13.247],[144.979,13.3055],[145.081,13.389],[145.1105,13.4315],[145.152,13.53],[145.163,13.583],[145.1565,13.65],[145.1095,13.7345],[145.075,13.7645],[144.952,13.8385],[144.8565,13.857],[144.7625,13.8335],[144.71,13.796],[144.6615,13.732],[144.6355,13.67],[144.564,13.6535],[144.471,13.589],[144.4335,13.536],[144.413,13.4475]]],[[[144.673,20.54],[144.684,20.477],[144.7105,20.426],[144.781,20.364],[144.856,20.3385],[144.9785,20.3505],[145.0595,20.405],[145.0985,20.4655],[145.1135,20.578],[145.0905,20.643],[145.05,20.694],[144.974,20.74],[144.8565,20.7515],[144.7885,20.729],[144.7105,20.6625],[144.681,20.6015],[144.673,20.54]]],[[[144.9155,14.12],[144.938,14.0275],[144.962,13.991],[145.026,13.9395],[145.087,13.9195],[145.1805,13.9095],[145.2845,13.927],[145.418,14.0085],[145.478,14.0945],[145.497,14.174],[145.484,14.2605],[145.457,14.309],[145.366,14.379],[145.2865,14.3995],[145.1805,14.393],[145.0635,14.347],[145.006,14.303],[144.9325,14.204],[144.9155,14.12]]],[[[144.9955,20.0165],[145.011,19.941],[145.05,19.88],[145.0915,19.845],[145.2145,19.81],[145.1885,19.759],[145.1765,19.6885],[145.203,19.5855],[145.2715,19.5135],[145.386,19.475],[145.4885,19.4905],[145.5395,19.5185],[145.6145,19.612],[145.6305,19.6875],[145.6225,19.7475],[145.591,19.8145],[145.5025,19.886],[145.416,19.908],[145.4515,20.013],[145.447,20.063],[145.419,20.13],[145.3675,20.187],[145.2685,20.2315],[145.1975,20.2355],[145.113,20.2095],[145.039,20.1485],[145.0055,20.0865],[144.9955,20.0165]]],[[[145.326,14.8345],[145.3485,14.7425],[145.4,14.68],[145.449,14.65],[145.529,14.633],[145.579,14.638],[145.676,14.6755],[145.7265,14.7145],[145.8285,14.8245],[145.893,14.9465],[145.98,15.061],[146.0385,15.2635],[146.03,15.3285],[146.0065,15.3805],[145.9415,15.4495],[145.887,15.478],[145.811,15.4915],[145.7565,15.4845],[145.609,15.4235],[145.549,15.376],[145.5,15.2955],[145.491,15.238],[145.438,15.1825],[145.39,15.097],[145.379,14.9785],[145.345,14.92],[145.326,14.8345]]],[[[145.424,16.3645],[145.4305,16.3035],[145.462,16.233],[145.532,16.163],[145.645,16.13],[145.7285,16.1335],[145.809,16.1585],[145.88,16.215],[145.9285,16.313],[145.931,16.3755],[145.9085,16.4505],[145.8665,16.5055],[145.935,16.55],[145.982,16.62],[145.9985,16.722],[145.979,16.792],[145.9515,16.8375],[145.886,16.8915],[145.828,16.9135],[145.728,16.9125],[145.6465,16.875],[145.573,16.7825],[145.559,16.684],[145.578,16.6175],[145.611,16.5695],[145.5685,16.56],[145.488,16.512],[145.4455,16.4555],[145.424,16.3645]]],[[[145.425,18.7765],[145.4375,18.698],[145.5005,18.5955],[145.6025,18.534],[145.664,18.524],[145.7335,18.534],[145.786,18.5585],[145.858,18.6145],[145.889,18.662],[145.91,18.741],[145.9085,18.8085],[145.8785,18.8935],[145.823,18.9555],[145.726,19.005],[145.6645,19.0145],[145.597,19.006],[145.5235,18.971],[145.4705,18.9175],[145.4455,18.8705],[145.425,18.7765]]],[[[145.498,18.0475],[145.5145,17.9665],[145.5545,17.9085],[145.648,17.852],[145.733,17.8445],[145.811,17.864],[145.9285,17.9425],[145.98,18.0005],[146.0095,18.067],[146.021,18.1355],[145.9915,18.254],[145.9385,18.316],[145.899,18.342],[145.8165,18.3685],[145.6995,18.358],[145.6105,18.303],[145.57,18.251],[145.523,18.155],[145.498,18.0475]]],[[[145.6045,17.602],[145.6135,17.538],[145.67,17.4435],[145.631,17.37],[145.623,17.307],[145.645,17.215],[145.713,17.1385],[145.8265,17.096],[145.906,17.104],[145.9555,17.1255],[146.024,17.192],[146.0555,17.2605],[146.062,17.325],[146.044,17.393],[146.004,17.454],[146.055,17.546],[146.0565,17.6435],[146.0335,17.703],[145.9625,17.7815],[145.8615,17.821],[145.761,17.8115],[145.6925,17.777],[145.646,17.729],[145.62,17.6805],[145.6045,17.602]]],[[[145.844,16.0055],[145.8605,15.927],[145.888,15.882],[145.945,15.833],[146.0555,15.8045],[146.144,15.825],[146.202,15.8655],[146.2655,15.9685],[146.268,16.0695],[146.2165,16.164],[146.1605,16.2055],[146.0735,16.229],[145.9905,16.217],[145.9305,16.184],[145.891,16.143],[145.8545,16.0705],[145.844,16.0055]]],[[[166.383,19.309],[166.3895,19.2585],[166.426,19.182],[166.5275,19.1025],[166.6565,19.0685],[166.7475,19.0875],[166.7995,19.12],[166.8495,19.1845],[166.8695,19.2815],[166.8525,19.3685],[166.8185,19.432],[166.7205,19.501],[166.614,19.525],[166.501,19.4975],[166.4345,19.443],[166.3935,19.3735],[166.383,19.309]]],[[[172.1155,52.9425],[172.147,52.8545],[172.205,52.803],[172.3045,52.753],[172.413,52.7265],[172.5155,52.6635],[172.6035,52.6255],[172.876,52.56],[173.1285,52.565],[173.0755,52.507],[173.042,52.434],[173.0495,52.37],[173.094,52.306],[173.225,52.2355],[173.3635,52.206],[173.487,52.1955],[173.5805,52.17],[173.7795,52.1685],[173.8865,52.1905],[173.9705,52.2295],[174.049,52.3005],[174.0675,52.4135],[174.12,52.521],[174.2855,52.523],[174.367,52.4795],[174.4895,52.452],[174.5915,52.452],[174.7135,52.4795],[174.772,52.5075],[174.8585,52.6005],[174.865,52.68],[174.8345,52.7395],[174.746,52.8075],[174.6185,52.846],[174.4625,52.8465],[174.409,52.892],[174.295,52.9385],[174.097,52.9595],[173.9435,52.989],[173.8365,52.9895],[173.7455,52.974],[173.4665,53.1295],[173.274,53.191],[173.175,53.206],[172.9725,53.209],[172.76,53.226],[172.552,53.211],[172.4875,53.195],[172.2465,53.0985],[172.151,53.0295],[172.1155,52.9425]]],[[[175.5315,52.3985],[175.56,52.3175],[175.691,52.207],[175.7955,52.1655],[175.9665,52.1495],[176.1155,52.1745],[176.193,52.2095],[176.294,52.292],[176.321,52.3555],[176.3015,52.445],[176.239,52.5075],[176.0955,52.5695],[175.9525,52.598],[175.862,52.605],[175.7305,52.588],[175.594,52.5215],[175.5415,52.452],[175.5315,52.3985]]],[[[176.8875,51.893],[176.9185,51.8085],[177.0085,51.7365],[177.228,51.639],[177.344,51.623],[177.427,51.628],[177.549,51.663],[177.6425,51.732],[177.7705,51.754],[177.9035,51.705],[178.0005,51.696],[178.144,51.6115],[178.34,51.571],[178.437,51.49],[178.714,51.3705],[178.803,51.356],[178.9365,51.277],[179.007,51.223],[179.1705,51.1695],[179.2585,51.163],[179.4285,51.1715],[179.5985,51.191],[179.6695,51.211],[179.76,51.262],[179.809,51.3205],[179.825,51.386],[179.8065,51.4505],[179.751,51.511],[179.574,51.604],[179.4255,51.6305],[179.3295,51.6685],[179.2745,51.732],[179.1675,51.7865],[178.878,51.8525],[178.9255,51.9265],[178.9285,51.989],[178.89,52.065],[178.8285,52.1175],[178.662,52.187],[178.553,52.2075],[178.4135,52.1995],[178.2755,52.256],[178.085,52.265],[177.956,52.235],[177.8835,52.2845],[177.7765,52.327],[177.6875,52.3465],[177.5535,52.3485],[177.4595,52.33],[177.346,52.335],[177.1795,52.2935],[177.117,52.2535],[177.0645,52.184],[177.0645,52.0895],[176.937,52.012],[176.901,51.9575],[176.8875,51.893]]],[[[179.167,51.9555],[179.22,51.8365],[179.29,51.7795],[179.3675,51.74],[179.51,51.7005],[179.602,51.687],[179.7315,51.6935],[179.8055,51.7105],[180.0,51.794],[180.0,51.8435],[180.0,51.8925],[180.0,51.942],[180.0,51.991],[180.0,52.0405],[180.0,52.0895],[180.0,52.1385],[179.8985,52.19],[179.754,52.234],[179.584,52.242],[179.459,52.2165],[179.315,52.1655],[179.2355,52.1185],[179.19,52.0665],[179.167,51.9555]]]]},"properties":{"flag":"https://upload.wikimedia.org/wikipedia/commons/a/a4/Flag_of_the_United_States.svg","name:en":"United States","wikidata":"Q30","ISO3166-1:alpha2":"US","ISO3166-1:alpha3":"USA","ISO3166-1:numeric":"840"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[79.4965,8.8975],[79.576,8.873],[79.6925,8.873],[79.7155,8.855],[79.715,8.781],[79.737,8.713],[79.6635,8.678],[79.626,8.641],[79.5815,8.548],[79.5835,8.434],[79.5605,8.362],[79.5245,8.321],[79.4965,8.257],[79.4905,8.207],[79.5075,8.021],[79.55,7.881],[79.597,7.675],[79.585,7.603],[79.5905,7.507],[79.635,7.289],[79.6175,7.227],[79.6235,7.143],[79.6555,7.025],[79.6385,6.939],[79.6425,6.897],[79.6835,6.723],[79.76,6.537],[79.7845,6.397],[79.8315,6.277],[79.8455,6.197],[79.883,6.101],[79.937,6.014],[80.0115,5.933],[80.136,5.836],[80.214,5.805],[80.4085,5.746],[80.594,5.719],[80.6315,5.723],[80.7195,5.759],[80.7905,5.772],[80.9045,5.849],[81.0635,5.908],[81.1745,5.926],[81.263,5.969],[81.4285,6.029],[81.5055,6.08],[81.6365,6.184],[81.753,6.267],[81.8675,6.384],[81.954,6.533],[81.977,6.613],[82.021,6.71],[82.0285,6.766],[82.0785,6.992],[82.0775,7.062],[82.061,7.124],[82.0695,7.304],[82.06,7.382],[82.0155,7.512],[81.9925,7.62],[81.9355,7.755],[81.874,7.854],[81.783,7.947],[81.7265,8.093],[81.6605,8.162],[81.6045,8.294],[81.5935,8.388],[81.5415,8.569],[81.507,8.626],[81.4225,8.698],[81.398,8.786],[81.352,8.861],[81.2795,8.919],[81.2215,9.011],[81.18,9.042],[81.118,9.124],[81.0505,9.245],[81.03,9.313],[80.9845,9.398],[80.9355,9.454],[80.7975,9.576],[80.677,9.668],[80.504,9.808],[80.3905,9.971],[80.34,10.006],[80.2355,10.034],[80.0775,10.015],[79.999,10.016],[79.893,9.99],[79.7635,9.936],[79.6285,9.819],[79.577,9.738],[79.5015,9.683],[79.451,9.58],[79.396,9.549],[79.4205,9.3535],[79.5665,9.258],[79.5715,9.087],[79.4965,8.8975]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/11/Flag_of_Sri_Lanka.svg","name:en":"Sri Lanka","ISO3166-1:alpha2":"LK","ISO3166-1:alpha3":"LKA","ISO3166-1:numeric":"144"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[7.589,47.59],[7.5055,47.5445],[7.511,47.497],[7.3865,47.4325],[7.3385,47.441],[7.255,47.4245],[7.17,47.443],[7.2015,47.4925],[7.139,47.502],[6.986,47.4935],[7.002,47.454],[6.9405,47.4335],[6.924,47.355],[7.05,47.3615],[7.0455,47.3265],[6.9405,47.286],[6.9555,47.244],[6.8805,47.2005],[6.85,47.1565],[6.7565,47.117],[6.6335,46.998],[6.505,46.9655],[6.4325,46.9285],[6.4645,46.8905],[6.435,46.8015],[6.438,46.7615],[6.283,46.6915],[6.1105,46.5765],[6.1565,46.5455],[6.073,46.4655],[6.0975,46.409],[6.17,46.366],[6.1025,46.285],[6.1245,46.2515],[5.979,46.217],[5.995,46.183],[6.0525,46.1515],[6.1365,46.1415],[6.2325,46.206],[6.2945,46.225],[6.2195,46.312],[6.253,46.3605],[6.335,46.4035],[6.4255,46.416],[6.519,46.4565],[6.682,46.4545],[6.821,46.427],[6.7705,46.3555],[6.8645,46.283],[6.804,46.2025],[6.798,46.136],[6.898,46.122],[6.8825,46.095],[7.0105,45.997],[7.0455,45.9225],[7.118,45.859],[7.1535,45.8795],[7.3445,45.915],[7.383,45.8965],[7.4785,45.9525],[7.658,45.9765],[7.735,45.924],[7.7695,45.937],[7.8635,45.9165],[7.9085,45.997],[7.9885,45.996],[8.034,46.0435],[8.0345,46.101],[8.108,46.1115],[8.1655,46.177],[8.139,46.226],[8.081,46.2585],[8.138,46.302],[8.222,46.33],[8.313,46.377],[8.291,46.409],[8.367,46.452],[8.438,46.464],[8.466,46.4425],[8.4655,46.334],[8.428,46.2985],[8.469,46.233],[8.5325,46.2185],[8.6125,46.1215],[8.852,46.0755],[8.787,45.9915],[8.8305,45.988],[8.925,45.904],[8.949,45.8435],[9.031,45.8245],[9.0745,45.9125],[9.0195,45.93],[9.017,46.05],[9.077,46.064],[9.0725,46.118],[9.194,46.1785],[9.2995,46.3265],[9.28,46.4145],[9.2495,46.431],[9.282,46.496],[9.3735,46.504],[9.3905,46.4735],[9.4625,46.498],[9.4545,46.419],[9.534,46.312],[9.6185,46.2875],[9.7145,46.293],[9.77,46.336],[9.9095,46.3795],[9.996,46.3425],[9.996,46.285],[10.0555,46.266],[10.0715,46.218],[10.175,46.2545],[10.1165,46.314],[10.108,46.3515],[10.164,46.3905],[10.143,46.4285],[10.087,46.4215],[10.0545,46.4645],[10.044,46.54],[10.1275,46.605],[10.224,46.629],[10.2415,46.589],[10.296,46.55],[10.364,46.5555],[10.472,46.5435],[10.4915,46.6115],[10.4015,46.637],[10.3845,46.683],[10.444,46.76],[10.4695,46.855],[10.488,46.9385],[10.3555,46.9925],[10.3095,46.95],[10.2415,46.9315],[10.232,46.8665],[10.123,46.8485],[10.0595,46.861],[10.0265,46.896],[9.88,46.935],[9.8735,47.0065],[9.68,47.062],[9.607,47.061],[9.56,47.0485],[9.5195,47.0985],[9.486,47.1805],[9.5305,47.2705],[9.5835,47.3125],[9.652,47.4085],[9.6545,47.4545],[9.593,47.466],[9.5505,47.537],[9.5145,47.537],[9.445,47.595],[9.256,47.659],[9.0265,47.6865],[8.899,47.648],[8.824,47.711],[8.7925,47.675],[8.728,47.6925],[8.7235,47.7455],[8.6825,47.7835],[8.6115,47.802],[8.473,47.764],[8.4055,47.674],[8.467,47.6415],[8.4315,47.5665],[8.325,47.5725],[8.288,47.6105],[8.1845,47.6045],[8.099,47.562],[7.9165,47.548],[7.892,47.5875],[7.8225,47.588],[7.7955,47.5575],[7.6975,47.533],[7.589,47.59]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/f3/Flag_of_Switzerland.svg","name:en":"Switzerland","wikidata":"Q39","ISO3166-1:alpha2":"CH","ISO3166-1:alpha3":"CHE","ISO3166-1:numeric":"756"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-61.7295,10.948],[-62.3235,11.2905],[-62.918,11.633],[-62.968,11.6545],[-63.7455,11.895],[-64.523,12.1345],[-64.625,12.151],[-65.361,12.1365],[-66.0785,12.1225],[-66.602,12.216],[-67.129,12.2695],[-67.678,12.3245],[-67.7335,12.32],[-67.8085,12.2925],[-67.9895,12.1935],[-67.9895,11.6665],[-68.6,11.6665],[-68.829,11.7415],[-68.9545,11.758],[-69.079,11.8795],[-69.7365,12.263],[-70.1405,12.365],[-70.164,12.35],[-70.4165,12.35],[-70.4165,12.5585],[-70.85,12.7045],[-70.926,12.7165],[-70.973,12.7115],[-71.055,12.675],[-71.122,12.6005],[-71.147,12.5235],[-71.08,12.402],[-71.0615,12.315],[-71.0135,12.2345],[-70.927,12.1575],[-70.927,11.961],[-71.345,11.848],[-71.3835,11.8105],[-71.644,11.734],[-71.7825,11.687],[-71.9715,11.6465],[-72.2455,11.145],[-72.3495,11.1515],[-72.4845,11.099],[-72.485,11.0615],[-72.575,10.915],[-72.64,10.8845],[-72.674,10.8215],[-72.689,10.757],[-72.735,10.6515],[-72.8205,10.57],[-72.8455,10.486],[-72.8935,10.4465],[-72.9065,10.353],[-72.8975,10.2175],[-72.9215,10.138],[-72.9115,10.1125],[-72.9545,10.065],[-72.953,10.0265],[-72.999,9.924],[-72.971,9.855],[-72.9455,9.8375],[-72.994,9.783],[-73.0055,9.741],[-73.055,9.6785],[-73.0855,9.556],[-73.128,9.5545],[-73.2035,9.4675],[-73.19,9.4445],[-73.241,9.403],[-73.2655,9.3265],[-73.3475,9.1735],[-73.248,9.1595],[-73.1775,9.1765],[-73.1315,9.2285],[-73.0735,9.243],[-73.011,9.2915],[-72.962,9.1845],[-72.973,9.1365],[-72.9475,9.0915],[-72.8865,9.1035],[-72.881,9.133],[-72.767,9.1065],[-72.719,8.9345],[-72.698,8.8065],[-72.655,8.6145],[-72.6105,8.5785],[-72.4275,8.3815],[-72.393,8.3585],[-72.382,8.3045],[-72.392,8.256],[-72.367,8.185],[-72.371,8.094],[-72.351,8.0805],[-72.3505,8.004],[-72.411,8.0345],[-72.425,7.99],[-72.487,7.945],[-72.4445,7.858],[-72.47,7.789],[-72.476,7.629],[-72.4525,7.563],[-72.4765,7.4895],[-72.442,7.446],[-72.4395,7.4035],[-72.1965,7.382],[-72.156,7.33],[-72.174,7.252],[-72.0475,7.039],[-71.941,7.0095],[-71.835,7.0255],[-71.811,7.0635],[-71.746,7.0655],[-71.7145,7.0345],[-71.656,7.0365],[-71.633,7.056],[-71.593,7.031],[-71.4915,7.031],[-71.459,7.0135],[-71.4295,7.038],[-71.379,7.018],[-71.2675,7.0325],[-71.1915,7.019],[-71.131,7.033],[-71.1115,6.99],[-70.995,6.983],[-70.927,7.0465],[-70.8395,7.079],[-70.697,7.094],[-70.6085,7.064],[-70.549,7.0745],[-70.503,7.006],[-70.4305,7.007],[-70.3715,6.98],[-70.344,6.9445],[-70.2935,6.934],[-70.2245,6.9745],[-70.118,6.9795],[-69.763,6.528],[-69.4305,6.108],[-69.3355,6.1375],[-69.312,6.0905],[-69.2375,6.08],[-69.1775,6.1475],[-69.123,6.1915],[-69.0435,6.2215],[-69.017,6.194],[-68.974,6.1965],[-68.886,6.168],[-68.8345,6.18],[-68.7925,6.1475],[-68.7065,6.152],[-68.6445,6.129],[-68.597,6.1635],[-68.5335,6.1515],[-68.5135,6.176],[-68.446,6.1875],[-68.3735,6.186],[-68.2975,6.1655],[-68.2545,6.1975],[-68.101,6.232],[-68.0415,6.198],[-67.9775,6.2085],[-67.9245,6.2405],[-67.845,6.3125],[-67.811,6.324],[-67.769,6.294],[-67.7215,6.3005],[-67.6255,6.2915],[-67.584,6.262],[-67.535,6.26],[-67.457,6.1935],[-67.4945,6.131],[-67.456,6.0585],[-67.4275,6.038],[-67.412,5.9885],[-67.527,5.9085],[-67.597,5.828],[-67.631,5.7325],[-67.639,5.653],[-67.619,5.564],[-67.599,5.544],[-67.625,5.476],[-67.683,5.427],[-67.797,5.3605],[-67.8495,5.304],[-67.8235,5.247],[-67.8265,5.1915],[-67.8545,5.138],[-67.806,5.055],[-67.8375,4.947],[-67.8235,4.84],[-67.818,4.707],[-67.845,4.645],[-67.839,4.562],[-67.857,4.5245],[-67.8035,4.4895],[-67.798,4.437],[-67.773,4.419],[-67.8055,4.315],[-67.797,4.2295],[-67.7725,4.1655],[-67.742,4.139],[-67.7035,4.0395],[-67.69,3.9365],[-67.6445,3.8525],[-67.644,3.813],[-67.61,3.754],[-67.5015,3.7675],[-67.483,3.712],[-67.439,3.6325],[-67.444,3.585],[-67.412,3.539],[-67.4015,3.48],[-67.339,3.451],[-67.312,3.391],[-67.3345,3.3355],[-67.3815,3.2925],[-67.3825,3.2455],[-67.435,3.2485],[-67.8645,2.866],[-67.864,2.7895],[-67.824,2.826],[-67.7505,2.8375],[-67.735,2.817],[-67.6585,2.7965],[-67.6215,2.814],[-67.584,2.7735],[-67.563,2.723],[-67.5685,2.666],[-67.5195,2.673],[-67.4705,2.624],[-67.3935,2.5745],[-67.352,2.5295],[-67.2975,2.4425],[-67.216,2.3925],[-67.188,2.3505],[-67.2135,2.3095],[-67.2205,2.2505],[-67.1615,2.133],[-67.1145,2.1335],[-67.097,2.0515],[-67.123,1.977],[-67.0655,1.9205],[-67.0355,1.8455],[-67.033,1.7865],[-66.959,1.625],[-66.9075,1.4875],[-66.913,1.4335],[-66.854,1.3675],[-66.8555,1.3215],[-66.881,1.2905],[-66.851,1.229],[-66.598,1.0],[-66.3105,0.745],[-66.1975,0.7815],[-66.1455,0.7475],[-66.077,0.762],[-66.07,0.8125],[-66.0145,0.804],[-65.9435,0.8635],[-65.941,0.891],[-65.888,0.91],[-65.788,0.965],[-65.743,1.001],[-65.627,1.0145],[-65.5575,0.9915],[-65.512,0.896],[-65.502,0.849],[-65.6045,0.715],[-65.556,0.654],[-65.445,0.689],[-65.3965,0.7775],[-65.396,0.822],[-65.356,0.865],[-65.3255,0.936],[-65.265,0.9205],[-65.1755,0.941],[-65.1755,1.0155],[-65.1515,1.109],[-65.1055,1.156],[-65.0105,1.1115],[-64.9715,1.18],[-64.8955,1.251],[-64.8555,1.266],[-64.8025,1.314],[-64.756,1.228],[-64.721,1.2395],[-64.6955,1.284],[-64.6455,1.3005],[-64.581,1.3595],[-64.537,1.4155],[-64.4665,1.4735],[-64.436,1.47],[-64.3945,1.5285],[-64.348,1.495],[-64.41,1.403],[-64.3435,1.382],[-64.3135,1.456],[-64.2435,1.507],[-64.1965,1.5215],[-64.173,1.5735],[-64.1315,1.576],[-64.077,1.645],[-64.061,1.7025],[-64.076,1.782],[-64.0545,1.803],[-64.067,1.8535],[-64.061,1.925],[-63.972,1.992],[-63.893,1.9895],[-63.8355,1.9665],[-63.741,2.0035],[-63.7115,2.0465],[-63.664,2.065],[-63.6355,2.099],[-63.564,2.134],[-63.511,2.1115],[-63.401,2.1505],[-63.3675,2.2685],[-63.384,2.352],[-63.3635,2.376],[-63.3795,2.4195],[-63.4385,2.436],[-63.523,2.417],[-63.5925,2.4475],[-63.6555,2.4365],[-63.703,2.4535],[-63.7725,2.4415],[-63.8425,2.491],[-63.931,2.4555],[-64.0305,2.481],[-64.057,2.5065],[-63.9905,2.6365],[-63.9825,2.715],[-64.0405,2.829],[-64.0715,2.9215],[-64.124,2.9905],[-64.1695,3.072],[-64.2255,3.1365],[-64.198,3.201],[-64.2285,3.3155],[-64.222,3.3875],[-64.2425,3.4355],[-64.1755,3.515],[-64.172,3.559],[-64.201,3.605],[-64.2675,3.6685],[-64.281,3.7095],[-64.3675,3.76],[-64.4775,3.7935],[-64.5435,3.857],[-64.587,3.9325],[-64.6335,3.962],[-64.7225,4.1175],[-64.802,4.174],[-64.8195,4.235],[-64.78,4.287],[-64.695,4.253],[-64.659,4.22],[-64.623,4.135],[-64.592,4.114],[-64.5,4.113],[-64.431,4.135],[-64.335,4.129],[-64.274,4.143],[-64.218,4.116],[-64.171,4.129],[-64.112,4.092],[-64.097,4.026],[-64.035,3.935],[-63.959,3.888],[-63.928,3.925],[-63.85,3.95],[-63.796,3.933],[-63.7,3.945],[-63.685,4.009],[-63.652,4.002],[-63.592,3.929],[-63.593,3.906],[-63.512,3.848],[-63.489,3.874],[-63.452,3.859],[-63.411,3.912],[-63.452,3.956],[-63.393,3.981],[-63.338,3.957],[-63.204,3.952],[-63.233,3.882],[-63.204,3.812],[-63.123,3.805],[-63.06,3.752],[-63.072,3.686],[-63.032,3.666],[-62.984,3.61],[-62.952,3.612],[-62.919,3.673],[-62.886,3.684],[-62.835,3.739],[-62.804,3.73],[-62.748,3.673],[-62.731,3.708],[-62.729,3.805],[-62.788,3.894],[-62.782,3.935],[-62.743,3.975],[-62.768,4.007],[-62.736,4.04],[-62.701,4.031],[-62.609,4.044],[-62.572,4.015],[-62.534,4.052],[-62.552,4.109],[-62.46,4.143],[-62.462,4.178],[-62.389,4.178],[-62.329,4.135],[-62.252,4.13],[-62.148,4.079],[-62.064,4.159],[-61.993,4.175],[-61.9265,4.1245],[-61.922,4.1615],[-61.818,4.168],[-61.802,4.229],[-61.769,4.249],[-61.658,4.262],[-61.631,4.241],[-61.56,4.252],[-61.508,4.322],[-61.513,4.406],[-61.462,4.437],[-61.351,4.419],[-61.278,4.478],[-61.322,4.504],[-61.323,4.535],[-61.269,4.54],[-61.188,4.521],[-61.1475,4.481],[-61.1195,4.5105],[-61.065,4.524],[-60.994,4.519],[-60.964,4.544],[-60.899,4.717],[-60.852,4.704],[-60.806,4.749],[-60.751,4.756],[-60.726,4.792],[-60.659,4.849],[-60.584,4.956],[-60.599,5.0],[-60.644,5.057],[-60.661,5.164],[-60.694,5.197],[-60.7375,5.202],[-60.749,5.2225],[-61.0845,5.599],[-61.392,5.944],[-61.331,5.999],[-61.312,6.052],[-61.2745,6.105],[-61.2095,6.112],[-61.1795,6.187],[-61.142,6.215],[-61.126,6.262],[-61.1755,6.327],[-61.148,6.413],[-61.1525,6.4575],[-61.1825,6.472],[-61.1725,6.5195],[-61.2195,6.566],[-61.183,6.671],[-61.1385,6.7215],[-61.0855,6.7045],[-61.013,6.7285],[-60.9555,6.7155],[-60.9095,6.747],[-60.9235,6.807],[-60.895,6.811],[-60.846,6.7805],[-60.8095,6.794],[-60.7185,6.7555],[-60.6635,6.8295],[-60.5555,6.856],[-60.5005,6.882],[-60.461,6.9245],[-60.4015,6.948],[-60.3665,6.998],[-60.3205,7.0215],[-60.2875,7.0875],[-60.2935,7.1335],[-60.367,7.185],[-60.4205,7.1765],[-60.4495,7.205],[-60.5585,7.1545],[-60.6245,7.211],[-60.638,7.246],[-60.6155,7.2985],[-60.588,7.3145],[-60.614,7.395],[-60.7055,7.4955],[-60.7045,7.56],[-60.6605,7.5645],[-60.618,7.6425],[-60.57,7.7035],[-60.563,7.761],[-60.4995,7.834],[-60.4375,7.836],[-60.405,7.821],[-60.333,7.8415],[-60.2455,7.913],[-60.219,7.9575],[-60.1835,7.9645],[-60.1005,8.0355],[-60.066,8.024],[-60.006,8.06],[-59.995,8.154],[-59.946,8.2135],[-59.8485,8.225],[-59.8015,8.275],[-59.815,8.3115],[-59.981,8.5185],[-59.955,8.5405],[-59.7705,8.604],[-59.5425,8.686],[-60.7295,9.628],[-60.81,9.672],[-60.8535,9.7175],[-60.9755,9.783],[-61.109,9.806],[-61.2075,9.8745],[-61.2235,9.876],[-61.5,9.97],[-61.6305,9.9865],[-61.855,9.9865],[-61.9735,10.008],[-62.083,10.046],[-61.8625,10.5885],[-61.803,10.589],[-61.803,10.7145],[-61.731,10.904],[-61.7295,10.948]]],[[[-63.8575,15.687],[-63.8315,15.586],[-63.796,15.5375],[-63.741,15.494],[-63.6895,15.4725],[-63.613,15.4645],[-63.5325,15.4835],[-63.48,15.515],[-63.4235,15.5825],[-63.3985,15.671],[-63.4055,15.7505],[-63.4495,15.8355],[-63.5165,15.8895],[-63.6075,15.915],[-63.6755,15.91],[-63.7415,15.884],[-63.823,15.806],[-63.8475,15.754],[-63.8575,15.687]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/06/Flag_of_Venezuela.svg","name:en":"Venezuela","wikidata":"Q717","ISO3166-1:alpha2":"VE","ISO3166-1:alpha3":"VEN","ISO3166-1:numeric":"862"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-72.55,21.9485],[-72.4555,22.2505],[-72.4555,22.3625],[-72.5265,22.47],[-73.4415,23.218],[-74.27,24.2415],[-74.3235,24.305],[-74.3855,24.344],[-75.497,24.8055],[-75.514,24.821],[-75.949,25.2765],[-76.7415,26.6025],[-76.7935,26.6915],[-76.8905,26.7785],[-77.2505,27.0065],[-77.338,27.0695],[-77.4615,27.1405],[-77.68,27.2265],[-77.9065,27.275],[-78.0865,27.3845],[-78.183,27.4245],[-78.285,27.4535],[-78.3825,27.4705],[-78.456,27.471],[-78.5395,27.444],[-79.21,27.1285],[-79.274,27.0835],[-79.3155,27.021],[-79.329,26.949],[-79.316,26.0915],[-79.4475,25.9135],[-79.505,25.811],[-79.524,25.7555],[-79.536,25.639],[-79.528,25.563],[-79.4705,25.4285],[-79.407,25.3225],[-79.371,25.2045],[-79.363,25.0045],[-80.0035,24.248],[-80.4825,24.156],[-80.5825,24.111],[-80.654,24.038],[-80.695,23.9495],[-80.6855,23.834],[-80.596,23.6205],[-80.557,23.5605],[-80.484,23.5065],[-80.437,23.4905],[-79.596,23.301],[-79.295,23.143],[-78.791,22.8755],[-78.773,22.8685],[-78.4825,22.7535],[-78.213,22.6425],[-78.1155,22.596],[-77.8015,22.42],[-77.6295,22.292],[-77.5715,22.2585],[-77.215,22.033],[-75.8345,21.5315],[-73.8395,20.8055],[-73.8085,20.7715],[-73.7095,20.7145],[-73.6295,20.706],[-73.159,20.7405],[-73.0815,20.7605],[-72.993,20.836],[-72.8465,21.06],[-72.814,21.134],[-72.6775,21.54],[-72.6725,21.6405],[-72.63,21.804],[-72.6095,21.858],[-72.55,21.9485]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/93/Flag_of_the_Bahamas.svg","name:en":"The Bahamas","wikidata":"Q778","ISO3166-1:alpha2":"BS","ISO3166-1:alpha3":"BHS","ISO3166-1:numeric":"044"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-16.858,13.603],[-16.9805,13.512],[-17.0205,13.439],[-17.026,13.351],[-17.01,13.247],[-16.991,13.187],[-16.976,13.061],[-16.7485,13.061],[-16.735,13.1125],[-16.679,13.1685],[-16.2335,13.165],[-15.8035,13.167],[-15.805,13.3405],[-15.7335,13.35],[-15.683,13.371],[-15.6295,13.354],[-15.5575,13.3595],[-15.4945,13.395],[-15.3795,13.356],[-15.2995,13.3665],[-15.229,13.419],[-15.1975,13.5295],[-15.1415,13.583],[-15.09,13.6005],[-15.051,13.535],[-14.999,13.4915],[-14.8995,13.4475],[-14.85,13.4445],[-14.814,13.418],[-14.7605,13.4175],[-14.7275,13.3715],[-14.6725,13.3445],[-14.578,13.3585],[-14.517,13.3055],[-14.4595,13.3],[-14.4385,13.271],[-14.359,13.2295],[-14.2665,13.247],[-14.1935,13.2295],[-14.129,13.2605],[-14.114,13.2865],[-13.987,13.3045],[-13.9485,13.3235],[-13.867,13.323],[-13.801,13.389],[-13.7995,13.4335],[-13.886,13.5455],[-13.9735,13.583],[-14.0305,13.556],[-14.0705,13.563],[-14.218,13.504],[-14.241,13.4785],[-14.338,13.454],[-14.472,13.535],[-14.4855,13.599],[-14.5355,13.6505],[-14.6115,13.6575],[-14.664,13.6455],[-14.7185,13.612],[-14.792,13.6535],[-14.8185,13.745],[-14.883,13.793],[-14.938,13.8055],[-14.986,13.794],[-15.067,13.825],[-15.1265,13.8065],[-15.2055,13.7545],[-15.265,13.741],[-15.3075,13.791],[-15.3645,13.7815],[-15.412,13.743],[-15.452,13.674],[-15.4775,13.584],[-15.6055,13.5915],[-16.147,13.5925],[-16.817,13.587],[-16.858,13.603]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/77/Flag_of_The_Gambia.svg","name:en":"The Gambia","wikidata":"Q1005","ISO3166-1:alpha2":"GM","ISO3166-1:alpha3":"GMB","ISO3166-1:numeric":"270"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-180.0,-9.585],[-179.938,-9.488],[-179.926,-9.415],[-179.938,-9.3385],[-180.0,-9.228],[-180.0,-9.585]]],[[[175.853,-5.6395],[175.863,-5.7015],[175.8925,-5.7635],[175.9375,-5.8105],[176.0205,-5.8715],[176.114,-5.9085],[176.1705,-5.908],[176.2685,-5.8645],[176.334,-5.771],[176.3445,-5.6875],[176.3305,-5.6265],[176.2965,-5.556],[176.2555,-5.5075],[176.198,-5.47],[176.074,-5.438],[175.962,-5.4595],[175.906,-5.5025],[175.876,-5.5455],[175.853,-5.6395]]],[[[176.1115,-6.29],[176.1185,-6.3435],[176.156,-6.42],[176.243,-6.4885],[176.318,-6.504],[176.3845,-6.494],[176.434,-6.4695],[176.4935,-6.4075],[176.5185,-6.354],[176.528,-6.268],[176.498,-6.1705],[176.4545,-6.119],[176.387,-6.0795],[176.3275,-6.069],[176.264,-6.0775],[176.213,-6.101],[176.1575,-6.153],[176.123,-6.22],[176.1115,-6.29]]],[[[176.9365,-7.194],[176.949,-7.307],[176.9955,-7.3865],[177.029,-7.416],[177.1225,-7.454],[177.2295,-7.4455],[177.2905,-7.4115],[177.35,-7.3335],[177.369,-7.235],[177.3605,-7.163],[177.3295,-7.0875],[177.2835,-7.036],[177.2015,-6.9925],[177.143,-6.9845],[177.0485,-7.007],[177.003,-7.039],[176.9585,-7.098],[176.9365,-7.194]]],[[[177.129,-6.105],[177.138,-6.166],[177.1935,-6.2555],[177.268,-6.303],[177.328,-6.316],[177.391,-6.311],[177.469,-6.2775],[177.517,-6.2295],[177.547,-6.168],[177.555,-6.111],[177.536,-6.022],[177.4955,-5.963],[177.453,-5.93],[177.384,-5.9045],[177.333,-5.9005],[177.225,-5.9325],[177.1645,-5.991],[177.1405,-6.0385],[177.129,-6.105]]],[[[178.102,-8.03],[178.122,-8.1175],[178.18,-8.1905],[178.5775,-8.5065],[178.9375,-8.7925],[179.2605,-9.1225],[179.698,-9.569],[179.7875,-9.624],[179.898,-9.634],[180.0,-9.585],[180.0,-9.228],[179.668,-8.7745],[179.3345,-8.3165],[179.2805,-8.2655],[178.902,-8.021],[178.5295,-7.7805],[178.4375,-7.735],[178.368,-7.726],[178.2715,-7.755],[178.204,-7.8115],[178.17,-7.857],[178.1115,-7.9695],[178.102,-8.03]]],[[[178.4605,-7.4605],[178.477,-7.5445],[178.5205,-7.622],[178.582,-7.6745],[178.6495,-7.6985],[178.732,-7.6955],[178.7785,-7.679],[178.845,-7.6275],[178.882,-7.571],[178.9005,-7.4715],[178.8755,-7.3855],[178.783,-7.2905],[178.7155,-7.2615],[178.6555,-7.2555],[178.58,-7.2745],[178.5365,-7.3015],[178.4855,-7.3625],[178.4605,-7.4605]]],[[[179.264,-10.787],[179.2935,-10.8935],[179.3735,-10.9685],[179.4375,-10.991],[179.509,-10.991],[179.5845,-10.9625],[179.627,-10.9275],[179.6685,-10.862],[179.679,-10.7515],[179.65,-10.679],[179.608,-10.632],[179.533,-10.5925],[179.454,-10.586],[179.3565,-10.619],[179.2875,-10.6945],[179.264,-10.787]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/38/Flag_of_Tuvalu.svg","name:en":"Tuvalu","wikidata":"Q672","ISO3166-1:alpha2":"TV","ISO3166-1:alpha3":"TUV","ISO3166-1:numeric":"798"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[12.4455,41.902],[12.4545,41.9],[12.4575,41.906],[12.4515,41.9065],[12.4455,41.902]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/00/Flag_of_the_Vatican_City.svg","name:en":"Vatican City","wikidata":"Q237","ISO3166-1:alpha2":"VA","ISO3166-1:alpha3":"VAT","ISO3166-1:numeric":"336"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-14.6225,-7.941],[-14.598,-8.061],[-14.565,-8.111],[-14.49,-8.17],[-14.4395,-8.188],[-14.373,-8.192],[-14.2605,-8.166],[-14.2,-8.133],[-14.13,-8.067],[-14.098,-7.993],[-14.0955,-7.917],[-14.1165,-7.854],[-14.2035,-7.759],[-14.2975,-7.705],[-14.355,-7.691],[-14.422,-7.694],[-14.4975,-7.722],[-14.565,-7.784],[-14.6015,-7.843],[-14.6225,-7.941]]],[[[-12.957,-37.299],[-12.94,-37.377],[-12.8575,-37.471],[-12.7165,-37.52],[-12.6735,-37.569],[-12.5865,-37.616],[-12.4805,-37.633],[-12.3945,-37.622],[-12.336,-37.599],[-12.258,-37.535],[-12.223,-37.468],[-12.229,-37.365],[-12.1465,-37.341],[-12.095,-37.31],[-11.9885,-37.201],[-11.9655,-37.133],[-11.97,-37.073],[-12.0095,-36.99],[-12.048,-36.946],[-12.1245,-36.895],[-12.1905,-36.871],[-12.306,-36.863],[-12.3675,-36.873],[-12.466,-36.909],[-12.5165,-36.948],[-12.577,-37.032],[-12.5985,-37.091],[-12.672,-37.082],[-12.774,-37.095],[-12.8995,-37.167],[-12.939,-37.221],[-12.957,-37.299]]],[[[-10.2805,-40.313],[-10.2435,-40.413],[-10.1845,-40.467],[-10.049,-40.544],[-9.9225,-40.57],[-9.7855,-40.553],[-9.698,-40.505],[-9.645,-40.445],[-9.612,-40.349],[-9.621,-40.291],[-9.694,-40.171],[-9.8195,-40.096],[-9.905,-40.076],[-9.9845,-40.073],[-10.0925,-40.092],[-10.1775,-40.132],[-10.2365,-40.189],[-10.2805,-40.313]]],[[[-5.9975,-15.999],[-5.9695,-16.097],[-5.875,-16.193],[-5.8225,-16.219],[-5.757,-16.23],[-5.6845,-16.219],[-5.525,-16.153],[-5.462,-16.097],[-5.4315,-16.037],[-5.4235,-15.977],[-5.4315,-15.905],[-5.468,-15.825],[-5.508,-15.772],[-5.568,-15.731],[-5.705,-15.704],[-5.757,-15.711],[-5.8375,-15.751],[-5.9325,-15.839],[-5.982,-15.921],[-5.9975,-15.999]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/00/Flag_of_Saint_Helena.svg","name:en":"Saint Helena, Ascension and Tristan da Cunha","wikidata":"Q192184","ISO3166-1:alpha2":"SH","ISO3166-1:alpha3":"SHN","ISO3166-1:numeric":"654"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-82.506,9.654],[-82.565,9.572],[-82.6355,9.494],[-82.6845,9.5065],[-82.7595,9.587],[-82.857,9.62],[-82.885,9.5705],[-82.8445,9.5],[-82.936,9.473],[-82.936,9.0785],[-82.887,9.088],[-82.874,9.0575],[-82.7505,8.9925],[-82.712,8.9225],[-82.7605,8.885],[-82.855,8.856],[-82.8665,8.8055],[-82.9145,8.7695],[-82.916,8.7415],[-82.8705,8.6985],[-82.83,8.636],[-82.822,8.567],[-82.832,8.5045],[-82.8685,8.4395],[-82.9355,8.424],[-83.046,8.318],[-82.975,8.2895],[-82.9295,8.2455],[-82.8875,8.077],[-82.9455,7.9355],[-82.3235,7.4125],[-82.1035,7.3575],[-82.016,7.304],[-81.976,7.195],[-81.8925,7.053],[-81.716,7.0945],[-81.566,7.183],[-81.292,7.3315],[-81.2065,7.311],[-81.027,7.1135],[-80.8975,7.034],[-80.3495,7.1085],[-80.2295,7.1895],[-80.17,7.219],[-79.9485,7.292],[-79.809,7.389],[-79.7635,7.5255],[-79.779,7.669],[-79.882,7.8335],[-80.1195,8.084],[-79.568,8.4275],[-79.3395,8.349],[-79.343,8.085],[-79.175,7.9525],[-78.748,7.926],[-78.63,7.775],[-78.3705,7.4045],[-78.1835,7.206],[-78.1235,7.1495],[-78.0365,7.051],[-77.889,7.2285],[-77.818,7.4725],[-77.7745,7.46],[-77.7015,7.507],[-77.7485,7.6165],[-77.742,7.6795],[-77.692,7.693],[-77.645,7.6515],[-77.63,7.5945],[-77.5835,7.5135],[-77.548,7.522],[-77.519,7.5745],[-77.491,7.567],[-77.461,7.6565],[-77.401,7.722],[-77.391,7.76],[-77.3365,7.78],[-77.364,7.825],[-77.3125,7.8975],[-77.2535,7.92],[-77.1795,7.917],[-77.164,7.9705],[-77.2135,8.0095],[-77.218,8.085],[-77.247,8.1755],[-77.3495,8.296],[-77.3555,8.387],[-77.412,8.475],[-77.4475,8.4725],[-77.448,8.5465],[-77.3705,8.661],[-77.3245,8.8105],[-77.5245,9.123],[-77.8465,9.3815],[-78.212,9.5505],[-78.571,9.797],[-79.0065,9.8355],[-79.533,9.87],[-79.9035,9.6485],[-80.025,9.5085],[-80.1925,9.3755],[-80.6045,9.2215],[-80.797,9.1125],[-80.8925,9.026],[-81.037,8.963],[-81.292,8.918],[-81.3675,8.956],[-81.4235,9.1075],[-81.535,9.1795],[-81.6855,9.202],[-81.8145,9.265],[-82.236,9.551],[-82.335,9.539],[-82.4775,9.6505],[-82.506,9.654]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/a/ab/Flag_of_Panama.svg","name:en":"Panama","wikidata":"Q804","ISO3166-1:alpha2":"PA","ISO3166-1:alpha3":"PAN","ISO3166-1:numeric":"591"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-83.046,15.033],[-82.8815,15.0905],[-82.8655,15.006],[-82.834,14.9535],[-82.5555,14.943],[-82.483,14.8735],[-82.4205,14.852],[-82.352,14.8485],[-82.2805,14.871],[-82.218,14.92],[-82.1815,14.982],[-82.1775,15.081],[-82.219,15.1725],[-82.257,15.2075],[-82.3655,15.269],[-82.4055,15.3355],[-82.516,15.4185],[-82.581,15.4455],[-82.667,15.4475],[-82.796,15.41],[-82.8625,15.463],[-82.844,15.5195],[-82.7755,15.548],[-82.7345,15.5825],[-82.6925,15.651],[-82.6825,15.6945],[-82.695,15.7915],[-82.728,15.8475],[-82.7715,15.8865],[-82.8425,15.9265],[-82.9055,15.9445],[-83.0065,15.9325],[-83.0475,15.9125],[-83.061,15.988],[-83.1525,16.155],[-83.1895,16.198],[-83.2685,16.242],[-83.3835,16.2445],[-83.4845,16.1865],[-83.5275,16.1215],[-83.539,16.011],[-83.5055,15.925],[-83.5125,15.811],[-83.483,15.7255],[-83.408,15.6545],[-83.3285,15.6295],[-83.262,15.6325],[-83.2735,15.5865],[-83.268,15.506],[-83.2295,15.433],[-83.155,15.375],[-83.1735,15.3215],[-83.28,15.4225],[-84.194,15.9845],[-84.256,16.01],[-84.94,16.171],[-85.229,16.359],[-85.6845,16.6545],[-85.7315,16.687],[-85.814,16.7135],[-85.8795,16.7155],[-86.195,16.6405],[-86.2715,16.627],[-86.73,16.485],[-86.8005,16.4235],[-87.3035,16.2145],[-87.7005,16.164],[-88.1755,15.9075],[-88.2215,15.7235],[-88.2475,15.69],[-88.3255,15.6705],[-88.3455,15.6175],[-88.5525,15.4465],[-88.605,15.413],[-88.678,15.3425],[-88.8455,15.243],[-88.974,15.136],[-89.1535,15.0685],[-89.1845,15.0025],[-89.1565,14.98],[-89.1815,14.911],[-89.228,14.8825],[-89.224,14.8345],[-89.169,14.775],[-89.1655,14.731],[-89.1315,14.7125],[-89.1575,14.67],[-89.1595,14.5745],[-89.2385,14.585],[-89.303,14.4965],[-89.355,14.47],[-89.356,14.4205],[-89.216,14.389],[-89.1685,14.366],[-89.109,14.4085],[-89.083,14.3415],[-89.0355,14.332],[-89.026,14.2725],[-88.9925,14.259],[-88.97,14.212],[-88.9025,14.2055],[-88.8665,14.1785],[-88.831,14.108],[-88.799,14.1645],[-88.7375,14.131],[-88.709,14.0465],[-88.6075,14.0055],[-88.569,13.978],[-88.5,13.967],[-88.5055,13.9085],[-88.4925,13.8625],[-88.452,13.859],[-88.417,13.8845],[-88.3175,13.89],[-88.2655,13.937],[-88.227,13.938],[-88.23,14.0],[-88.16,13.9835],[-88.0725,13.9925],[-88.063,13.9435],[-88.0125,13.8715],[-87.9625,13.8975],[-87.854,13.8945],[-87.831,13.9185],[-87.7465,13.854],[-87.7085,13.804],[-87.7545,13.691],[-87.7555,13.603],[-87.792,13.5265],[-87.722,13.506],[-87.723,13.4435],[-87.7905,13.414],[-87.799,13.3815],[-87.7435,13.349],[-87.739,13.3245],[-87.678,13.235],[-87.635,13.1975],[-87.663,13.1275],[-87.543,13.1675],[-87.4795,13.078],[-87.3945,13.004],[-87.3085,12.9875],[-87.0275,13.003],[-86.955,13.039],[-86.9255,13.083],[-86.931,13.19],[-86.9085,13.269],[-86.829,13.3135],[-86.7545,13.2675],[-86.706,13.303],[-86.7105,13.359],[-86.744,13.4065],[-86.722,13.4325],[-86.741,13.4865],[-86.7495,13.5655],[-86.7695,13.582],[-86.749,13.6395],[-86.786,13.662],[-86.7605,13.7255],[-86.7655,13.7665],[-86.723,13.787],[-86.5225,13.781],[-86.417,13.759],[-86.3325,13.769],[-86.2785,13.8755],[-86.227,13.9065],[-86.1405,14.006],[-86.1525,14.0355],[-86.076,14.07],[-86.0085,14.0555],[-86.042,13.996],[-85.9125,13.926],[-85.8455,13.9175],[-85.8265,13.859],[-85.7665,13.8495],[-85.7535,13.968],[-85.6645,14.011],[-85.626,14.0085],[-85.592,14.0425],[-85.536,14.046],[-85.4985,14.0855],[-85.404,14.123],[-85.3415,14.247],[-85.1985,14.2565],[-85.159,14.288],[-85.1985,14.3475],[-85.2055,14.3865],[-85.181,14.4285],[-85.183,14.4695],[-85.1385,14.519],[-85.147,14.5765],[-85.096,14.5525],[-85.0425,14.575],[-85.0215,14.6075],[-85.0275,14.696],[-84.987,14.74],[-84.9425,14.754],[-84.9005,14.8145],[-84.7995,14.822],[-84.752,14.7735],[-84.745,14.7155],[-84.7035,14.6665],[-84.6295,14.6745],[-84.5435,14.6545],[-84.496,14.6195],[-84.434,14.632],[-84.3755,14.6995],[-84.297,14.663],[-84.2585,14.675],[-84.2735,14.7235],[-84.2355,14.7605],[-84.1885,14.716],[-84.0955,14.7315],[-84.0395,14.7655],[-83.9715,14.7475],[-83.9285,14.7785],[-83.8895,14.765],[-83.7,14.858],[-83.5775,14.912],[-83.529,14.951],[-83.541,14.975],[-83.5015,15.011],[-83.459,14.981],[-83.385,15.0165],[-83.2355,14.9825],[-83.1415,14.998],[-83.046,15.033]]],[[[-84.1545,17.41],[-84.1445,17.3455],[-84.119,17.295],[-84.0585,17.238],[-84.0035,17.213],[-83.895,17.205],[-83.816,17.223],[-83.771,17.25],[-83.7145,17.3185],[-83.6945,17.383],[-83.712,17.493],[-83.747,17.5445],[-83.8365,17.6015],[-83.951,17.619],[-84.0155,17.6035],[-84.075,17.5685],[-84.1375,17.4895],[-84.1545,17.41]]],[[[-82.6035,15.8695],[-82.5975,15.8225],[-82.568,15.7575],[-82.488,15.689],[-82.3895,15.6675],[-82.2845,15.6955],[-82.219,15.7545],[-82.1825,15.8515],[-82.1855,15.9095],[-82.2215,15.9895],[-82.2555,16.025],[-82.3225,16.0625],[-82.4075,16.0735],[-82.4565,16.065],[-82.5395,16.0175],[-82.5815,15.962],[-82.6035,15.8695]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/82/Flag_of_Honduras.svg","name:en":"Honduras","wikidata":"Q783","ISO3166-1:alpha2":"HN","ISO3166-1:alpha3":"HND","ISO3166-1:numeric":"340"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[12.4035,43.9525],[12.4155,43.9],[12.4875,43.8965],[12.516,43.941],[12.5065,43.9915],[12.4035,43.9525]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/b1/Flag_of_San_Marino.svg","name:en":"San Marino","wikidata":"Q238","ISO3166-1:alpha2":"SM","ISO3166-1:alpha3":"SMR","ISO3166-1:numeric":"674"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-60.9865,13.5115],[-60.901,13.513],[-60.823,13.545],[-60.7765,13.588],[-60.728,13.663],[-60.687,13.762],[-60.6735,13.834],[-60.6685,13.996],[-60.68,14.084],[-60.7095,14.158],[-60.753,14.2215],[-60.892,14.2435],[-61.004,14.2725],[-61.097,14.2485],[-61.1495,14.185],[-61.258,13.99],[-61.281,13.922],[-61.2835,13.79],[-61.2705,13.736],[-61.197,13.631],[-61.1305,13.58],[-60.9865,13.5115]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9f/Flag_of_Saint_Lucia.svg","name:en":"Saint Lucia","wikidata":"Q760","ISO3166-1:alpha2":"LC","ISO3166-1:alpha3":"LCA","ISO3166-1:numeric":"662"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-10.28,8.485],[-10.322,8.506],[-10.386,8.488],[-10.3995,8.4495],[-10.478,8.3765],[-10.491,8.35],[-10.556,8.3055],[-10.5775,8.334],[-10.6425,8.3545],[-10.641,8.4745],[-10.616,8.542],[-10.5765,8.596],[-10.4925,8.6255],[-10.466,8.674],[-10.511,8.7425],[-10.52,8.7825],[-10.562,8.818],[-10.559,8.8445],[-10.5905,8.9775],[-10.574,9.0445],[-10.6675,9.0855],[-10.731,9.0815],[-10.722,9.187],[-10.667,9.1995],[-10.6635,9.3105],[-10.7085,9.332],[-10.7315,9.3795],[-10.815,9.39],[-10.8365,9.4395],[-10.881,9.5915],[-10.911,9.602],[-10.918,9.655],[-10.964,9.664],[-10.993,9.7525],[-11.039,9.7875],[-11.096,9.8575],[-11.1575,9.868],[-11.1725,9.961],[-11.208,10.0],[-11.8945,10.0],[-11.907,9.94],[-12.12,9.8725],[-12.2175,9.909],[-12.2225,9.933],[-12.4355,9.881],[-12.471,9.85],[-12.5285,9.717],[-12.5645,9.7025],[-12.5875,9.6015],[-12.6175,9.604],[-12.654,9.54],[-12.684,9.417],[-12.7555,9.392],[-12.7855,9.3035],[-12.807,9.29],[-12.8795,9.2935],[-12.907,9.262],[-12.9415,9.2905],[-12.9715,9.227],[-13.0015,9.1165],[-13.0735,9.08],[-13.082,9.0485],[-13.141,9.0565],[-13.185,9.095],[-13.2755,9.063],[-13.301,9.036],[-13.496,8.966],[-13.488,8.91],[-13.4495,8.836],[-13.444,8.636],[-13.4925,8.55],[-13.5,8.502],[-13.491,8.38],[-13.462,8.313],[-13.399,8.23],[-13.438,8.168],[-13.4515,8.11],[-13.443,8.034],[-13.3975,7.957],[-13.307,7.902],[-13.211,7.896],[-13.1955,7.831],[-13.2335,7.793],[-13.265,7.734],[-13.2755,7.668],[-13.27,7.592],[-13.245,7.52],[-13.2085,7.47],[-13.152,7.428],[-13.0265,7.37],[-12.9505,7.367],[-12.759,7.313],[-12.724,7.252],[-12.687,7.217],[-12.612,7.183],[-12.5055,7.18],[-12.4365,7.16],[-12.182,7.064],[-11.9995,6.991],[-11.792,6.884],[-11.712,6.807],[-11.608,6.755],[-11.5065,6.9285],[-11.4175,6.9415],[-11.4145,6.9735],[-11.369,7.0225],[-11.376,7.0645],[-11.334,7.077],[-11.352,7.14],[-11.2675,7.2365],[-11.182,7.265],[-11.1705,7.306],[-11.1025,7.3865],[-11.0565,7.4065],[-10.933,7.5135],[-10.9175,7.5035],[-10.843,7.55],[-10.773,7.648],[-10.6965,7.7405],[-10.6045,7.7745],[-10.605,8.0405],[-10.576,8.048],[-10.525,8.1285],[-10.463,8.1565],[-10.354,8.15],[-10.2955,8.2195],[-10.32,8.285],[-10.2885,8.3405],[-10.273,8.44],[-10.28,8.485]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/17/Flag_of_Sierra_Leone.svg","name:en":"Sierra Leone","wikidata":"Q1044","ISO3166-1:alpha2":"SL","ISO3166-1:alpha3":"SLE","ISO3166-1:numeric":"694"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-11.608,6.755],[-11.5865,6.7355],[-11.572,6.668],[-11.519,6.5825],[-11.4665,6.537],[-11.189,6.416],[-11.079,6.363],[-11.0185,6.324],[-11.0155,6.2755],[-10.9715,6.1835],[-10.912,6.1315],[-10.753,6.042],[-10.518,5.976],[-10.436,5.9185],[-10.3755,5.896],[-10.3405,5.849],[-10.25,5.791],[-10.2135,5.728],[-10.132,5.6395],[-10.0795,5.612],[-9.997,5.5445],[-9.914,5.4555],[-9.811,5.3785],[-9.727,5.284],[-9.6655,5.2465],[-9.5775,5.1385],[-9.4695,5.048],[-9.433,5.003],[-9.32,4.94],[-9.109,4.794],[-8.9955,4.763],[-8.903,4.6855],[-8.6915,4.5675],[-8.607,4.5455],[-8.5545,4.503],[-8.4275,4.4525],[-8.3275,4.3875],[-8.262,4.3665],[-8.1655,4.3545],[-8.05,4.33],[-7.985,4.296],[-7.933,4.249],[-7.7825,4.173],[-7.6175,4.1555],[-7.527,4.162],[-7.5295,4.3625],[-7.5655,4.3905],[-7.567,4.4565],[-7.5525,4.4885],[-7.5725,4.5815],[-7.548,4.612],[-7.5625,4.649],[-7.5605,4.783],[-7.5835,4.8335],[-7.592,4.908],[-7.55,4.924],[-7.5355,4.9585],[-7.557,5.0755],[-7.4835,5.1265],[-7.4705,5.1645],[-7.4825,5.2125],[-7.468,5.281],[-7.368,5.33],[-7.424,5.385],[-7.4195,5.4835],[-7.384,5.522],[-7.3975,5.546],[-7.3755,5.613],[-7.431,5.696],[-7.4305,5.8395],[-7.476,5.812],[-7.5865,5.8995],[-7.688,5.898],[-7.7845,5.979],[-7.803,6.0935],[-7.852,6.1025],[-7.836,6.2045],[-7.9035,6.278],[-8.006,6.3225],[-8.0925,6.307],[-8.1665,6.2795],[-8.334,6.373],[-8.385,6.386],[-8.4055,6.447],[-8.4565,6.471],[-8.482,6.434],[-8.5715,6.523],[-8.5695,6.5575],[-8.534,6.6],[-8.419,6.676],[-8.3105,6.855],[-8.3335,6.8915],[-8.326,6.972],[-8.2935,7.0255],[-8.303,7.1355],[-8.288,7.1875],[-8.3315,7.203],[-8.365,7.2405],[-8.36,7.278],[-8.396,7.3285],[-8.3975,7.397],[-8.423,7.528],[-8.471,7.557],[-8.559,7.626],[-8.554,7.696],[-8.6765,7.6955],[-8.7065,7.6445],[-8.723,7.5575],[-8.7095,7.5135],[-8.81,7.4045],[-8.8535,7.347],[-8.8375,7.2765],[-8.872,7.257],[-8.93,7.2885],[-8.9705,7.253],[-9.026,7.2465],[-9.072,7.202],[-9.203,7.3185],[-9.2025,7.386],[-9.286,7.377],[-9.2955,7.4275],[-9.3605,7.4335],[-9.3845,7.394],[-9.468,7.43],[-9.4205,7.484],[-9.3785,7.5885],[-9.359,7.613],[-9.3745,7.6915],[-9.349,7.7465],[-9.3785,7.809],[-9.432,7.8595],[-9.4375,7.988],[-9.4055,8.0495],[-9.441,8.084],[-9.494,8.1755],[-9.512,8.2415],[-9.489,8.2655],[-9.507,8.3295],[-9.497,8.3735],[-9.5525,8.382],[-9.5675,8.4095],[-9.6575,8.405],[-9.62,8.453],[-9.6635,8.5015],[-9.734,8.472],[-9.725,8.5255],[-9.7755,8.55],[-9.806,8.5015],[-9.9235,8.496],[-9.9895,8.4455],[-10.038,8.4285],[-10.043,8.489],[-10.12,8.5345],[-10.2265,8.4805],[-10.28,8.485],[-10.273,8.44],[-10.2885,8.3405],[-10.32,8.285],[-10.2955,8.2195],[-10.354,8.15],[-10.463,8.1565],[-10.525,8.1285],[-10.576,8.048],[-10.605,8.0405],[-10.6045,7.7745],[-10.6965,7.7405],[-10.773,7.648],[-10.843,7.55],[-10.9175,7.5035],[-10.933,7.5135],[-11.0565,7.4065],[-11.1025,7.3865],[-11.1705,7.306],[-11.182,7.265],[-11.2675,7.2365],[-11.352,7.14],[-11.334,7.077],[-11.376,7.0645],[-11.369,7.0225],[-11.4145,6.9735],[-11.4175,6.9415],[-11.5065,6.9285],[-11.608,6.755]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/b8/Flag_of_Liberia.svg","name:en":"Liberia","wikidata":"Q1014","ISO3166-1:alpha2":"LR","ISO3166-1:alpha3":"LBR","ISO3166-1:numeric":"430"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-16.976,13.061],[-16.951,12.981],[-16.9955,12.779],[-16.994,12.671],[-16.973,12.577],[-16.994,12.513],[-16.991,12.423],[-16.951,12.309],[-16.8945,12.2375],[-16.6895,12.359],[-16.609,12.3475],[-16.545,12.3605],[-16.5145,12.3505],[-16.3755,12.376],[-16.203,12.4615],[-16.1625,12.4495],[-16.039,12.4725],[-15.9535,12.443],[-15.89,12.4505],[-15.683,12.425],[-15.429,12.537],[-15.3375,12.6135],[-15.184,12.682],[-15.1225,12.686],[-14.913,12.6785],[-14.3235,12.6765],[-13.708,12.6755],[-13.4835,12.674],[-13.345,12.6585],[-13.332,12.645],[-13.2185,12.6515],[-13.1385,12.645],[-13.0515,12.6255],[-13.0465,12.5755],[-13.0615,12.485],[-12.9445,12.4755],[-12.942,12.5375],[-12.895,12.554],[-12.845,12.495],[-12.752,12.467],[-12.765,12.4335],[-12.6265,12.436],[-12.565,12.3665],[-12.486,12.4045],[-12.403,12.385],[-12.3565,12.315],[-12.302,12.3505],[-12.1775,12.3605],[-12.118,12.4135],[-12.0595,12.433],[-12.0105,12.4015],[-11.968,12.4055],[-11.9,12.449],[-11.8445,12.3925],[-11.805,12.4035],[-11.77,12.382],[-11.686,12.405],[-11.668,12.426],[-11.476,12.4525],[-11.377,12.4125],[-11.352,12.4675],[-11.3585,12.508],[-11.4105,12.537],[-11.4035,12.5875],[-11.4235,12.664],[-11.4235,12.729],[-11.383,12.743],[-11.3805,12.8035],[-11.4105,12.8485],[-11.3975,12.93],[-11.346,12.9435],[-11.355,12.976],[-11.396,13.0055],[-11.4355,13.0625],[-11.5185,13.157],[-11.5415,13.2325],[-11.525,13.2595],[-11.591,13.314],[-11.596,13.365],[-11.6275,13.3945],[-11.742,13.39],[-11.7665,13.342],[-11.8285,13.309],[-11.8875,13.394],[-11.868,13.4585],[-11.9535,13.522],[-12.0315,13.6165],[-12.04,13.6665],[-12.072,13.721],[-12.026,13.743],[-11.949,13.8115],[-11.933,13.9245],[-12.009,13.986],[-12.0145,14.0285],[-11.9825,14.09],[-11.989,14.195],[-12.024,14.2835],[-12.092,14.3045],[-12.1025,14.375],[-12.2005,14.4065],[-12.19,14.443],[-12.22,14.49],[-12.2165,14.55],[-12.189,14.5545],[-12.146,14.6525],[-12.2145,14.7055],[-12.2405,14.7645],[-12.277,14.771],[-12.3275,14.8265],[-12.362,14.826],[-12.4545,14.895],[-12.4625,14.984],[-12.4925,15.0145],[-12.5725,15.0425],[-12.612,15.0855],[-12.6655,15.1095],[-12.687,15.09],[-12.7945,15.1595],[-12.809,15.2165],[-12.8495,15.199],[-12.852,15.317],[-12.9235,15.337],[-12.9445,15.358],[-12.9665,15.501],[-13.061,15.479],[-13.105,15.5015],[-13.088,15.582],[-13.1865,15.6265],[-13.215,15.609],[-13.25,15.651],[-13.2075,15.6935],[-13.2935,15.7695],[-13.289,15.7885],[-13.3135,15.9165],[-13.3475,15.9415],[-13.378,16.014],[-13.3745,16.0525],[-13.4435,16.0835],[-13.497,16.078],[-13.524,16.1245],[-13.581,16.132],[-13.6755,16.0955],[-13.72,16.1445],[-13.798,16.143],[-13.843,16.107],[-13.877,16.152],[-13.8675,16.1825],[-13.917,16.2025],[-14.0405,16.3715],[-14.219,16.543],[-14.3335,16.5695],[-14.3275,16.6325],[-14.4095,16.634],[-14.5,16.614],[-14.5495,16.639],[-14.641,16.6175],[-14.652,16.6495],[-14.7535,16.6285],[-14.809,16.6505],[-14.895,16.632],[-14.9455,16.635],[-14.952,16.676],[-14.99,16.6905],[-15.0055,16.6405],[-15.0545,16.6285],[-15.076,16.6705],[-15.1155,16.634],[-15.085,16.6045],[-15.12,16.578],[-15.164,16.5895],[-15.2265,16.553],[-15.301,16.5745],[-15.4065,16.535],[-15.4725,16.5895],[-15.5085,16.567],[-15.5145,16.521],[-15.5445,16.5095],[-15.62,16.525],[-15.626,16.4935],[-15.7045,16.4705],[-15.8115,16.507],[-15.863,16.4945],[-15.9155,16.511],[-15.9645,16.483],[-16.0085,16.4995],[-16.057,16.479],[-16.1035,16.5255],[-16.15,16.5485],[-16.18,16.519],[-16.2605,16.5245],[-16.3045,16.462],[-16.3305,16.3575],[-16.368,16.307],[-16.354,16.27],[-16.399,16.2125],[-16.4545,16.182],[-16.4515,16.0875],[-16.5085,16.0665],[-16.741,16.0805],[-16.79,15.827],[-17.123,15.3795],[-17.3575,15.095],[-17.6975,14.9585],[-17.776,14.8105],[-17.786,14.686],[-17.716,14.5525],[-17.576,14.4685],[-17.3835,14.559],[-17.2535,14.3465],[-17.144,14.195],[-17.0255,14.0495],[-17.0305,13.8035],[-16.959,13.645],[-16.858,13.603],[-16.817,13.587],[-16.147,13.5925],[-15.6055,13.5915],[-15.4775,13.584],[-15.452,13.674],[-15.412,13.743],[-15.3645,13.7815],[-15.3075,13.791],[-15.265,13.741],[-15.2055,13.7545],[-15.1265,13.8065],[-15.067,13.825],[-14.986,13.794],[-14.938,13.8055],[-14.883,13.793],[-14.8185,13.745],[-14.792,13.6535],[-14.7185,13.612],[-14.664,13.6455],[-14.6115,13.6575],[-14.5355,13.6505],[-14.4855,13.599],[-14.472,13.535],[-14.338,13.454],[-14.241,13.4785],[-14.218,13.504],[-14.0705,13.563],[-14.0305,13.556],[-13.9735,13.583],[-13.886,13.5455],[-13.7995,13.4335],[-13.801,13.389],[-13.867,13.323],[-13.9485,13.3235],[-13.987,13.3045],[-14.114,13.2865],[-14.129,13.2605],[-14.1935,13.2295],[-14.2665,13.247],[-14.359,13.2295],[-14.4385,13.271],[-14.4595,13.3],[-14.517,13.3055],[-14.578,13.3585],[-14.6725,13.3445],[-14.7275,13.3715],[-14.7605,13.4175],[-14.814,13.418],[-14.85,13.4445],[-14.8995,13.4475],[-14.999,13.4915],[-15.051,13.535],[-15.09,13.6005],[-15.1415,13.583],[-15.1975,13.5295],[-15.229,13.419],[-15.2995,13.3665],[-15.3795,13.356],[-15.4945,13.395],[-15.5575,13.3595],[-15.6295,13.354],[-15.683,13.371],[-15.7335,13.35],[-15.805,13.3405],[-15.8035,13.167],[-16.2335,13.165],[-16.679,13.1685],[-16.735,13.1125],[-16.7485,13.061],[-16.976,13.061]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fd/Flag_of_Senegal.svg","name:en":"Senegal","wikidata":"Q1041","ISO3166-1:alpha2":"SN","ISO3166-1:alpha3":"SEN","ISO3166-1:numeric":"686"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-12.2405,14.7645],[-12.119,14.7545],[-12.0555,14.721],[-12.025,14.7635],[-11.9375,14.7805],[-11.9355,14.8125],[-11.8395,14.858],[-11.789,15.03],[-11.8445,15.096],[-11.812,15.1315],[-11.836,15.1845],[-11.7995,15.2325],[-11.806,15.278],[-11.767,15.336],[-11.7435,15.461],[-11.7045,15.492],[-11.71,15.533],[-11.64,15.521],[-11.581,15.55],[-11.5605,15.5835],[-11.517,15.5935],[-11.4925,15.6415],[-11.411,15.637],[-11.3395,15.526],[-11.2515,15.4195],[-11.2045,15.399],[-10.993,15.232],[-10.954,15.16],[-10.882,15.12],[-10.867,15.1985],[-10.833,15.2155],[-10.829,15.2705],[-10.8,15.307],[-10.732,15.3435],[-10.7155,15.375],[-10.7285,15.4275],[-10.705,15.4435],[-10.639,15.42],[-10.519,15.451],[-10.3325,15.4345],[-10.3205,15.443],[-10.1675,15.402],[-10.1375,15.3765],[-10.065,15.3615],[-9.9225,15.4],[-9.799,15.389],[-9.781,15.414],[-9.722,15.4075],[-9.685,15.427],[-9.401,15.439],[-9.447,15.6055],[-9.331,15.703],[-9.3095,15.6935],[-9.3315,15.5655],[-9.3315,15.5015],[-9.0,15.5005],[-8.509,15.5005],[-8.0,15.5],[-7.429,15.5],[-6.731,15.5],[-6.189,15.5],[-5.5,15.5],[-5.3885,16.057],[-5.3335,16.3335],[-5.609,16.5],[-5.6615,17.0005],[-5.705,17.3975],[-5.7715,18.0],[-5.8265,18.4875],[-5.9075,19.187],[-5.943,19.511],[-6.0005,20.041],[-6.0595,20.569],[-6.1045,20.969],[-6.178,21.618],[-6.2175,21.9615],[-6.2805,22.5],[-6.3345,22.9635],[-6.3585,23.201],[-6.4055,23.568],[-6.4565,23.972],[-6.4845,24.222],[-6.5485,24.354],[-6.4995,24.356],[-6.572,25.0],[-6.0,25.0005],[-5.553,25.001],[-4.8335,25.0],[-5.3905,25.344],[-6.0345,25.736],[-6.434,25.9805],[-6.833,26.2235],[-7.266,26.483],[-7.5785,26.67],[-8.2315,27.057],[-8.668,27.315],[-8.667,26.766],[-8.667,26.3335],[-8.667,26.0005],[-9.067,25.9995],[-9.4335,26.0],[-9.9335,25.9995],[-10.367,25.9995],[-10.881,26.0],[-11.2935,25.999],[-11.6725,25.999],[-12.0005,26.0],[-12.0,25.7515],[-12.0005,25.349],[-12.0005,25.3485],[-12.001,24.766],[-12.0,24.366],[-12.001,23.8335],[-12.0005,23.4545],[-12.156,23.4165],[-12.366,23.3185],[-12.5715,23.2915],[-12.9985,23.0245],[-13.1055,22.893],[-13.1505,22.7575],[-13.084,22.5365],[-13.0755,22.4575],[-13.0515,22.086],[-12.9995,21.3365],[-13.5415,21.341],[-14.2855,21.342],[-14.687,21.3385],[-15.0405,21.337],[-15.6105,21.338],[-15.992,21.339],[-16.568,21.339],[-16.9485,21.334],[-16.9935,21.138],[-17.032,21.058],[-17.068,20.8855],[-17.01,20.5625],[-16.932,20.5695],[-16.8445,20.542],[-16.733,20.445],[-16.604,20.3485],[-16.529,20.2775],[-16.487,20.195],[-16.508,20.087],[-16.5705,19.9715],[-16.631,19.906],[-16.668,19.8145],[-16.659,19.6295],[-16.663,19.5605],[-16.6845,19.5095],[-16.7715,19.467],[-16.7935,19.408],[-16.7785,19.3525],[-16.706,19.188],[-16.6125,19.064],[-16.4515,18.868],[-16.335,18.624],[-16.2665,18.4315],[-16.2455,18.2345],[-16.2335,18.0065],[-16.243,17.788],[-16.267,17.612],[-16.298,17.4805],[-16.362,17.34],[-16.637,16.706],[-16.6505,16.6775],[-16.6735,16.509],[-16.7085,16.397],[-16.7285,16.29],[-16.7295,16.1665],[-16.741,16.0805],[-16.5085,16.0665],[-16.4515,16.0875],[-16.4545,16.182],[-16.399,16.2125],[-16.354,16.27],[-16.368,16.307],[-16.3305,16.3575],[-16.3045,16.462],[-16.2605,16.5245],[-16.18,16.519],[-16.15,16.5485],[-16.1035,16.5255],[-16.057,16.479],[-16.0085,16.4995],[-15.9645,16.483],[-15.9155,16.511],[-15.863,16.4945],[-15.8115,16.507],[-15.7045,16.4705],[-15.626,16.4935],[-15.62,16.525],[-15.5445,16.5095],[-15.5145,16.521],[-15.5085,16.567],[-15.4725,16.5895],[-15.4065,16.535],[-15.301,16.5745],[-15.2265,16.553],[-15.164,16.5895],[-15.12,16.578],[-15.085,16.6045],[-15.1155,16.634],[-15.076,16.6705],[-15.0545,16.6285],[-15.0055,16.6405],[-14.99,16.6905],[-14.952,16.676],[-14.9455,16.635],[-14.895,16.632],[-14.809,16.6505],[-14.7535,16.6285],[-14.652,16.6495],[-14.641,16.6175],[-14.5495,16.639],[-14.5,16.614],[-14.4095,16.634],[-14.3275,16.6325],[-14.3335,16.5695],[-14.219,16.543],[-14.0405,16.3715],[-13.917,16.2025],[-13.8675,16.1825],[-13.877,16.152],[-13.843,16.107],[-13.798,16.143],[-13.72,16.1445],[-13.6755,16.0955],[-13.581,16.132],[-13.524,16.1245],[-13.497,16.078],[-13.4435,16.0835],[-13.3745,16.0525],[-13.378,16.014],[-13.3475,15.9415],[-13.3135,15.9165],[-13.289,15.7885],[-13.2935,15.7695],[-13.2075,15.6935],[-13.25,15.651],[-13.215,15.609],[-13.1865,15.6265],[-13.088,15.582],[-13.105,15.5015],[-13.061,15.479],[-12.9665,15.501],[-12.9445,15.358],[-12.9235,15.337],[-12.852,15.317],[-12.8495,15.199],[-12.809,15.2165],[-12.7945,15.1595],[-12.687,15.09],[-12.6655,15.1095],[-12.612,15.0855],[-12.5725,15.0425],[-12.4925,15.0145],[-12.4625,14.984],[-12.4545,14.895],[-12.362,14.826],[-12.3275,14.8265],[-12.277,14.771],[-12.2405,14.7645]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/43/Flag_of_Mauritania.svg","name:en":"Mauritania","wikidata":"Q1025","ISO3166-1:alpha2":"MR","ISO3166-1:alpha3":"MRT","ISO3166-1:numeric":"478"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-69.7365,12.263],[-69.687,12.32],[-69.6615,12.408],[-69.691,12.542],[-69.7225,12.6],[-69.814,12.692],[-69.962,12.802],[-70.0585,12.823],[-70.12,12.813],[-70.1815,12.781],[-70.251,12.69],[-70.2635,12.64],[-70.268,12.518],[-70.2455,12.447],[-70.164,12.35],[-70.1405,12.365],[-69.7365,12.263]]],[[[-69.3675,12.381],[-69.3605,12.281],[-69.345,12.217],[-69.3155,12.165],[-69.2395,12.072],[-69.186,12.025],[-69.055,11.935],[-68.9055,11.861],[-68.7855,11.834],[-68.6835,11.781],[-68.602,11.781],[-68.5215,11.816],[-68.468,11.871],[-68.4405,11.932],[-68.364,11.859],[-68.3225,11.838],[-68.2265,11.826],[-68.156,11.844],[-68.0935,11.885],[-68.0445,11.952],[-67.9945,12.092],[-67.991,12.206],[-68.0165,12.315],[-68.0595,12.369],[-68.1035,12.399],[-68.2,12.426],[-68.287,12.49],[-68.3665,12.511],[-68.4455,12.504],[-68.5035,12.478],[-68.554,12.437],[-68.604,12.363],[-68.6255,12.249],[-68.698,12.313],[-68.787,12.364],[-68.857,12.376],[-68.8895,12.444],[-68.944,12.508],[-69.0695,12.576],[-69.175,12.591],[-69.25,12.569],[-69.3235,12.506],[-69.3595,12.435],[-69.3675,12.381]]],[[[-62.7865,17.616],[-62.847,17.674],[-63.0195,17.736],[-63.0655,17.783],[-63.0545,17.806],[-62.9695,17.9865],[-62.9495,18.017],[-63.063,18.064],[-63.139,18.053],[-63.268,17.8965],[-63.278,17.847],[-63.367,17.813],[-63.415,17.772],[-63.457,17.7],[-63.4675,17.654],[-63.4625,17.572],[-63.446,17.529],[-63.396,17.468],[-63.324,17.428],[-63.262,17.415],[-63.1935,17.418],[-63.1515,17.352],[-63.051,17.279],[-62.7865,17.616]]],[[[3.08,51.551],[3.295,51.449],[3.3855,51.334],[3.3585,51.315],[3.3945,51.2655],[3.4485,51.2415],[3.5275,51.246],[3.5155,51.287],[3.591,51.3045],[3.7955,51.256],[3.7915,51.214],[3.8865,51.2],[4.0115,51.244],[4.064,51.2475],[4.1665,51.293],[4.242,51.354],[4.3915,51.408],[4.4425,51.4685],[4.538,51.4825],[4.5355,51.423],[4.6665,51.4445],[4.774,51.505],[4.8405,51.4785],[4.83,51.421],[4.9285,51.396],[5.079,51.4715],[5.1045,51.4315],[5.071,51.3935],[5.1315,51.347],[5.242,51.305],[5.226,51.2685],[5.4175,51.2625],[5.4875,51.2995],[5.558,51.2625],[5.5605,51.2225],[5.658,51.1845],[5.767,51.1835],[5.847,51.1415],[5.8225,51.0925],[5.758,51.0335],[5.756,50.9575],[5.7145,50.9085],[5.6515,50.875],[5.654,50.82],[5.694,50.8115],[5.695,50.755],[5.7765,50.782],[5.8075,50.756],[5.8865,50.77],[6.021,50.7545],[6.019,50.8465],[6.074,50.8465],[6.0925,50.9175],[6.018,50.9345],[6.0175,50.9835],[5.955,50.9885],[5.938,51.035],[6.0915,51.1345],[6.0725,51.2425],[6.1695,51.3295],[6.2265,51.3605],[6.2055,51.3995],[6.2235,51.475],[6.212,51.5135],[6.0915,51.606],[6.1095,51.647],[6.0365,51.673],[6.029,51.726],[5.945,51.8235],[6.0555,51.8525],[6.1665,51.8405],[6.2795,51.874],[6.407,51.829],[6.4645,51.855],[6.675,51.916],[6.722,51.896],[6.8285,51.964],[6.8265,51.9935],[6.688,52.04],[6.751,52.085],[6.7605,52.119],[6.8555,52.1205],[6.9815,52.2215],[7.0615,52.2345],[7.0265,52.292],[7.072,52.352],[6.994,52.4655],[6.9415,52.4355],[6.8545,52.4595],[6.7745,52.4595],[6.6975,52.4865],[6.7265,52.563],[6.715,52.626],[6.742,52.6455],[6.897,52.6515],[6.94,52.638],[7.055,52.6445],[7.0715,52.8105],[7.1815,52.9415],[7.213,53.011],[7.203,53.1135],[7.179,53.1385],[7.2175,53.198],[7.1915,53.315],[7.0525,53.3065],[6.9025,53.3535],[6.8825,53.4475],[6.699,53.4935],[6.5515,53.5825],[6.4125,53.6045],[6.346,53.7245],[6.3325,53.744],[6.205,53.707],[6.099,53.7245],[5.9935,53.7215],[5.824,53.6665],[5.6335,53.685],[5.4685,53.6705],[5.353,53.63],[5.0785,53.5855],[4.965,53.553],[4.826,53.491],[4.7585,53.433],[4.623,53.382],[4.5405,53.3225],[4.4945,53.231],[4.419,53.15],[4.3375,53.028],[4.332,52.935],[4.364,52.8755],[4.3085,52.7605],[4.2805,52.5915],[4.2145,52.512],[4.209,52.4345],[4.0955,52.2925],[3.934,52.1835],[3.772,52.1365],[3.7035,52.09],[3.638,51.9805],[3.521,51.917],[3.422,51.8835],[3.3065,51.823],[3.2235,51.7325],[3.2135,51.689],[3.1215,51.625],[3.08,51.551]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/20/Flag_of_the_Netherlands.svg","name:en":"Netherlands","wikidata":"Q29999","ISO3166-1:alpha2":"NL","ISO3166-1:alpha3":"NLD","ISO3166-1:numeric":"528"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-6.0565,35.9595],[-6.117,35.9135],[-6.179,35.808],[-6.2335,35.6415],[-6.4485,35.151],[-6.512,34.9745],[-6.581,34.8425],[-6.7875,34.5045],[-6.873,34.388],[-7.002,34.176],[-7.1005,34.09],[-7.167,34.044],[-7.3505,33.9725],[-7.4965,33.8995],[-7.756,33.786],[-8.3975,33.548],[-8.629,33.447],[-8.7105,33.389],[-8.822,33.2625],[-8.8835,33.176],[-8.9305,33.129],[-9.239,32.8665],[-9.379,32.7555],[-9.4915,32.6395],[-9.523,32.5555],[-9.5245,32.364],[-9.5515,32.1515],[-9.6345,32.077],[-9.843,31.8665],[-10.001,31.5855],[-10.058,31.4555],[-10.0725,31.384],[-10.0715,31.1075],[-10.0895,30.8845],[-10.1095,30.771],[-10.12,30.6515],[-10.1095,30.5905],[-10.058,30.481],[-9.9465,30.448],[-9.8605,30.381],[-9.816,30.312],[-9.818,30.2845],[-9.9785,30.007],[-10.0245,29.9475],[-10.187,29.786],[-10.36,29.52],[-10.5055,29.3705],[-10.6105,29.25],[-10.672,29.196],[-10.7675,29.1335],[-11.0395,29.0045],[-11.196,28.9205],[-11.2835,28.864],[-11.416,28.724],[-11.491,28.6655],[-11.619,28.491],[-11.6715,28.466],[-11.885,28.406],[-12.1035,28.322],[-12.2055,28.29],[-12.49,28.211],[-12.6155,28.194],[-12.801,28.189],[-13.008,28.146],[-13.0665,28.1005],[-13.216,27.907],[-13.306,27.8625],[-13.358,27.803],[-13.4335,27.627],[-13.5805,27.4035],[-13.6685,27.2395],[-13.715,27.1185],[-13.751,26.9585],[-13.8175,26.8545],[-13.882,26.8035],[-14.0305,26.7075],[-14.1465,26.6815],[-14.332,26.6095],[-14.467,26.4705],[-14.5685,26.412],[-14.6285,26.3575],[-14.6765,26.266],[-14.742,26.0725],[-14.834,25.882],[-14.91,25.6915],[-14.976,25.55],[-15.0555,25.3425],[-15.0745,25.267],[-15.085,25.153],[-15.0735,25.061],[-15.0485,24.9945],[-15.075,24.8465],[-15.0985,24.778],[-15.2215,24.704],[-15.3225,24.6585],[-15.424,24.5595],[-15.51,24.4495],[-15.678,24.3115],[-15.7945,24.1985],[-15.9205,24.1045],[-16.056,23.982],[-16.176,23.8365],[-16.2215,23.7725],[-16.2365,23.716],[-16.2505,23.541],[-16.2775,23.4205],[-16.307,23.357],[-16.397,23.2105],[-16.468,23.0745],[-16.5275,22.935],[-16.616,22.6905],[-16.6915,22.577],[-16.7505,22.5395],[-16.8275,22.467],[-16.9355,22.385],[-16.973,22.346],[-17.046,22.222],[-17.104,22.081],[-17.141,22.014],[-17.1965,21.875],[-17.2105,21.8075],[-17.221,21.5445],[-17.255,21.3525],[-17.029,21.3415],[-16.9915,21.364],[-16.8715,21.363],[-16.8325,21.3785],[-16.6595,21.392],[-16.4585,21.3895],[-16.2755,21.347],[-16.1385,21.3515],[-15.992,21.339],[-15.6105,21.338],[-15.0405,21.337],[-14.964,21.36],[-14.815,21.3725],[-14.776,21.4065],[-14.713,21.4335],[-14.605,21.5505],[-14.542,21.591],[-14.495,21.6765],[-14.482,21.747],[-14.5045,22.0085],[-14.4925,22.0415],[-14.446,22.0615],[-14.394,22.132],[-14.2785,22.243],[-14.16,22.418],[-14.1155,22.5765],[-14.116,22.7855],[-14.0745,22.9175],[-14.0395,22.9955],[-14.002,23.016],[-14.023,23.1005],[-14.008,23.173],[-13.953,23.29],[-13.9315,23.378],[-13.825,23.6375],[-13.825,23.6635],[-13.773,23.7585],[-13.7165,23.7815],[-13.689,23.8335],[-13.5775,23.8625],[-13.5305,23.886],[-13.471,23.875],[-13.395,23.9275],[-13.274,23.967],[-13.224,23.964],[-13.115,24.0095],[-13.0615,23.9835],[-13.021,24.03],[-13.0095,24.1765],[-12.971,24.257],[-12.9635,24.3415],[-12.9155,24.4295],[-12.8615,24.457],[-12.7965,24.548],[-12.703,24.5915],[-12.6605,24.67],[-12.602,24.697],[-12.5455,24.769],[-12.5435,24.802],[-12.454,24.858],[-12.388,24.847],[-12.3565,24.884],[-12.325,24.963],[-12.2725,24.9815],[-12.253,25.0405],[-12.119,25.1875],[-12.0495,25.2865],[-12.0315,25.353],[-12.0005,25.3485],[-12.0005,25.349],[-12.0,25.7515],[-12.0005,26.0],[-11.6725,25.999],[-11.636,26.063],[-11.627,26.117],[-11.5645,26.215],[-11.546,26.3495],[-11.4825,26.415],[-11.487,26.4525],[-11.464,26.515],[-11.4345,26.538],[-11.378,26.636],[-11.273,26.7075],[-11.272,26.735],[-11.3395,26.8005],[-11.343,26.841],[-11.2535,26.878],[-11.1485,26.9],[-10.9195,26.9195],[-10.8465,26.8985],[-10.7445,26.908],[-10.683,26.8945],[-10.583,26.9035],[-10.5145,26.8895],[-10.4145,26.8395],[-10.359,26.843],[-10.2575,26.8165],[-10.236,26.783],[-10.132,26.776],[-10.086,26.785],[-9.988,26.745],[-9.9135,26.7325],[-9.846,26.7475],[-9.792,26.7405],[-9.692,26.8015],[-9.6325,26.8785],[-9.4695,26.961],[-9.4045,26.955],[-9.3505,26.977],[-9.2875,26.9815],[-9.141,26.966],[-9.094,26.9705],[-9.047,27.014],[-8.805,26.9975],[-8.7665,26.987],[-8.7325,27.0535],[-8.737,27.1395],[-8.774,27.194],[-8.78,27.258],[-8.762,27.4225],[-8.779,27.547],[-8.8115,27.6665],[-8.668,27.6665],[-8.669,28.241],[-8.6685,28.667],[-8.5735,28.7475],[-8.458,28.792],[-8.3515,28.8855],[-8.202,28.9845],[-8.04,29.08],[-7.958,29.1215],[-7.8735,29.1935],[-7.822,29.211],[-7.773,29.255],[-7.6345,29.301],[-7.6135,29.3655],[-7.4885,29.36],[-7.345,29.392],[-7.257,29.474],[-7.15,29.5225],[-7.0225,29.4935],[-6.9085,29.4955],[-6.834,29.468],[-6.777,29.462],[-6.721,29.5055],[-6.6185,29.53],[-6.5275,29.5215],[-6.468,29.56],[-6.2895,29.5705],[-6.2475,29.5655],[-6.1655,29.5825],[-6.07,29.562],[-5.9365,29.5935],[-5.8765,29.5905],[-5.8135,29.6075],[-5.7275,29.591],[-5.7505,29.53],[-5.686,29.541],[-5.651,29.5045],[-5.5655,29.4765],[-5.445,29.6335],[-5.3175,29.789],[-5.3235,29.815],[-5.289,29.881],[-5.2395,29.933],[-5.124,30.003],[-4.9365,30.1405],[-4.6075,30.2825],[-4.488,30.372],[-4.427,30.4395],[-4.3295,30.5215],[-4.1485,30.585],[-4.001,30.593],[-3.8865,30.6105],[-3.817,30.5905],[-3.768,30.626],[-3.6445,30.6905],[-3.6295,30.7445],[-3.6515,30.8425],[-3.5565,30.932],[-3.5365,31.0135],[-3.606,31.0765],[-3.674,31.097],[-3.681,31.165],[-3.778,31.124],[-3.7595,31.176],[-3.7905,31.236],[-3.759,31.2635],[-3.7645,31.334],[-3.661,31.378],[-3.6645,31.6335],[-3.252,31.7135],[-2.8205,31.7935],[-2.844,31.832],[-2.8415,31.8805],[-2.931,32.035],[-2.8735,32.1115],[-2.6,32.113],[-2.5425,32.145],[-2.2775,32.1685],[-2.18,32.142],[-2.1325,32.1465],[-2.015,32.182],[-1.9685,32.1575],[-1.9035,32.1645],[-1.749,32.121],[-1.5765,32.092],[-1.509,32.104],[-1.41,32.0875],[-1.2215,32.08],[-1.154,32.11],[-1.183,32.1635],[-1.241,32.2055],[-1.235,32.2895],[-1.2485,32.3205],[-1.188,32.407],[-1.1285,32.41],[-1.063,32.453],[-1.0005,32.5195],[-1.2715,32.6955],[-1.399,32.7625],[-1.544,32.958],[-1.486,32.9755],[-1.462,33.042],[-1.576,33.149],[-1.6065,33.213],[-1.6705,33.284],[-1.6635,33.378],[-1.6215,33.448],[-1.62,33.49],[-1.588,33.529],[-1.5995,33.6155],[-1.6465,33.676],[-1.7325,33.701],[-1.721,33.7405],[-1.6765,33.759],[-1.672,33.806],[-1.6985,33.8695],[-1.646,34.1025],[-1.692,34.194],[-1.712,34.2615],[-1.7055,34.307],[-1.782,34.3915],[-1.687,34.4905],[-1.7415,34.508],[-1.839,34.628],[-1.771,34.7235],[-1.7395,34.7435],[-1.8885,34.8065],[-1.8935,34.8415],[-1.9745,34.8875],[-1.973,34.9355],[-2.035,34.9245],[-2.1305,34.997],[-2.1705,35.0095],[-2.211,35.0585],[-2.2065,35.337],[-2.508,35.426],[-2.605,35.44],[-2.696,35.4895],[-2.752,35.5545],[-2.816,35.5945],[-2.9135,35.6285],[-2.9775,35.6375],[-3.0735,35.6245],[-3.1775,35.563],[-3.302,35.439],[-3.63,35.484],[-3.9715,35.45],[-4.298,35.391],[-4.5825,35.4145],[-4.843,35.5525],[-5.019,35.75],[-5.019,35.886],[-5.037,35.9615],[-5.112,36.047],[-5.213,36.0475],[-5.3,36.033],[-5.6215,35.9405],[-5.6665,35.9355],[-5.8855,35.965],[-6.0565,35.9595]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/2c/Flag_of_Morocco.svg","name:en":"Morocco","wikidata":"Q1028","ISO3166-1:alpha2":"MA","ISO3166-1:alpha3":"MAR","ISO3166-1:numeric":"504"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-31.5575,39.4455],[-31.5445,39.3625],[-31.469,39.234],[-31.353,39.1635],[-31.2705,39.1475],[-31.075,39.17],[-30.977,39.2145],[-30.931,39.2535],[-30.868,39.365],[-30.8575,39.421],[-30.8545,39.7225],[-30.8785,39.8095],[-30.9525,39.882],[-31.027,39.9145],[-31.0945,39.926],[-31.1965,39.9175],[-31.2995,39.8705],[-31.3505,39.8175],[-31.5045,39.5875],[-31.543,39.509],[-31.5575,39.4455]]],[[[-29.0955,38.593],[-29.08,38.528],[-29.0325,38.4645],[-28.9495,38.3895],[-28.8785,38.344],[-28.572,38.2105],[-28.4565,38.185],[-28.2045,38.1805],[-28.045,38.1975],[-27.929,38.2235],[-27.6295,38.366],[-27.549,38.4275],[-27.508,38.5165],[-27.388,38.4645],[-27.189,38.418],[-27.0945,38.409],[-26.982,38.436],[-26.8845,38.4935],[-26.84,38.533],[-26.769,38.642],[-26.76,38.6945],[-26.7825,38.792],[-26.8125,38.8395],[-26.898,38.911],[-26.9615,38.9465],[-27.1375,38.9925],[-27.2455,39.0055],[-27.3975,38.993],[-27.4995,38.9605],[-27.612,38.8705],[-27.6375,38.8215],[-27.6415,38.718],[-27.878,38.8105],[-27.805,38.8335],[-27.7105,38.9085],[-27.6785,38.9835],[-27.699,39.1065],[-27.7475,39.1845],[-27.8385,39.2525],[-27.9215,39.288],[-28.0195,39.307],[-28.0875,39.303],[-28.2015,39.265],[-28.2725,39.208],[-28.331,39.1075],[-28.33,39.0285],[-28.2955,38.959],[-28.4085,38.9485],[-28.806,38.8355],[-28.9985,38.756],[-29.055,38.707],[-29.0955,38.593]]],[[[-26.097,37.829],[-26.0595,37.721],[-25.745,37.314],[-25.3735,36.821],[-25.3295,36.7845],[-25.228,36.7435],[-25.064,36.7255],[-24.9555,36.732],[-24.851,36.7755],[-24.806,36.8185],[-24.5625,37.1695],[-24.5315,37.2415],[-24.5515,37.3565],[-24.8405,37.8045],[-24.9115,37.9215],[-24.985,37.994],[-25.0425,38.0315],[-25.1485,38.0595],[-25.767,38.1085],[-25.8385,38.106],[-25.9455,38.069],[-26.0225,37.9955],[-26.0875,37.8815],[-26.097,37.829]]],[[[-17.5045,32.8065],[-17.4895,32.742],[-17.4235,32.638],[-17.329,32.561],[-17.208,32.4915],[-16.5745,32.2215],[-16.508,32.2025],[-16.43,32.202],[-16.317,32.244],[-16.2735,32.282],[-16.238,32.341],[-16.227,32.393],[-16.24,32.469],[-16.3295,32.6825],[-16.415,32.7935],[-16.2995,32.8035],[-16.1475,32.8795],[-16.066,32.9525],[-16.0395,33.015],[-16.0405,33.135],[-16.0745,33.229],[-16.1255,33.2795],[-16.1785,33.308],[-16.2545,33.326],[-16.3175,33.325],[-16.443,33.293],[-16.505,33.267],[-16.5805,33.195],[-16.643,33.0735],[-16.6405,32.9865],[-16.626,32.949],[-16.7975,33.0175],[-16.8565,33.0335],[-17.1365,33.076],[-17.248,33.071],[-17.3385,33.0285],[-17.424,32.965],[-17.476,32.909],[-17.5045,32.8065]]],[[[-16.2815,30.0175],[-16.2645,29.953],[-16.223,29.8955],[-16.161,29.853],[-16.0865,29.831],[-15.972,29.8345],[-15.896,29.862],[-15.814,29.941],[-15.7555,29.9575],[-15.693,29.9955],[-15.6345,30.078],[-15.624,30.1575],[-15.638,30.215],[-15.6875,30.2875],[-15.7895,30.347],[-15.874,30.3615],[-15.945,30.3525],[-16.009,30.327],[-16.089,30.2515],[-16.1805,30.2175],[-16.252,30.1465],[-16.276,30.0875],[-16.2815,30.0175]]],[[[-9.1565,41.8725],[-9.1565,41.7215],[-9.1285,41.6335],[-9.0815,41.5405],[-9.071,41.415],[-9.04,40.673],[-9.17,40.2755],[-9.571,39.83],[-9.7595,39.619],[-9.827,39.526],[-9.835,39.4735],[-9.814,39.3915],[-9.7785,38.7785],[-9.755,38.675],[-9.713,38.598],[-9.4565,38.312],[-9.1665,37.9],[-9.228,37.379],[-9.265,37.0635],[-9.2555,36.9865],[-9.2075,36.8575],[-9.142,36.7705],[-9.0745,36.7325],[-8.949,36.713],[-8.352,36.741],[-7.8845,36.7635],[-7.757,36.799],[-7.459,36.9645],[-7.3865,36.9745],[-7.397,37.1575],[-7.422,37.275],[-7.441,37.3075],[-7.4405,37.391],[-7.4905,37.5185],[-7.5225,37.553],[-7.445,37.664],[-7.419,37.7435],[-7.3215,37.8185],[-7.255,37.9235],[-7.2455,37.992],[-7.1285,38.0015],[-7.1085,38.041],[-7.0445,38.014],[-7.007,38.021],[-6.969,38.1605],[-7.0285,38.1825],[-7.097,38.179],[-7.143,38.2615],[-7.318,38.44],[-7.3035,38.543],[-7.264,38.588],[-7.2725,38.6385],[-7.26,38.723],[-7.203,38.752],[-7.1255,38.8155],[-7.0965,38.816],[-7.033,38.879],[-7.052,38.907],[-6.951,39.0235],[-6.981,39.088],[-7.028,39.1155],[-7.144,39.1085],[-7.132,39.1645],[-7.2215,39.1935],[-7.248,39.2535],[-7.2315,39.2785],[-7.3105,39.341],[-7.323,39.382],[-7.2945,39.457],[-7.377,39.489],[-7.5,39.591],[-7.534,39.667],[-7.331,39.641],[-7.2515,39.667],[-7.1515,39.653],[-7.0155,39.6705],[-6.977,39.772],[-6.987,39.81],[-6.9035,39.8705],[-6.8855,39.94],[-6.881,40.0415],[-6.954,40.1155],[-7.019,40.1425],[-7.009,40.2275],[-6.9515,40.2575],[-6.868,40.2645],[-6.864,40.296],[-6.7905,40.334],[-6.781,40.364],[-6.8385,40.4145],[-6.8495,40.4515],[-6.801,40.5505],[-6.8455,40.566],[-6.799,40.6555],[-6.8305,40.743],[-6.8035,40.846],[-6.86,40.951],[-6.9315,41.017],[-6.809,41.0365],[-6.754,41.104],[-6.768,41.135],[-6.7015,41.1805],[-6.6485,41.2475],[-6.5805,41.239],[-6.469,41.3015],[-6.4155,41.348],[-6.3155,41.39],[-6.3055,41.4495],[-6.189,41.575],[-6.2545,41.633],[-6.3545,41.6765],[-6.4525,41.681],[-6.5125,41.661],[-6.5485,41.6855],[-6.5645,41.7515],[-6.5175,41.875],[-6.571,41.8835],[-6.546,41.931],[-6.5995,41.9485],[-6.7,41.9335],[-6.7505,41.9435],[-7.077,41.952],[-7.1415,41.991],[-7.1865,41.9695],[-7.1735,41.919],[-7.1965,41.88],[-7.3155,41.8425],[-7.39,41.8425],[-7.4525,41.865],[-7.574,41.8295],[-7.704,41.9075],[-7.7335,41.8925],[-7.9215,41.882],[-7.9885,41.8675],[-8.0125,41.8335],[-8.094,41.808],[-8.165,41.818],[-8.162,41.861],[-8.217,41.913],[-8.1305,42.0035],[-8.086,42.0165],[-8.1185,42.0825],[-8.186,42.0725],[-8.1955,42.148],[-8.2535,42.1365],[-8.332,42.084],[-8.429,42.0725],[-8.518,42.0795],[-8.547,42.054],[-8.637,42.0475],[-8.6615,42.0035],[-8.746,41.9645],[-8.871,41.864],[-9.1565,41.8725]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/5/5c/Flag_of_Portugal.svg","name:en":"Portugal","wikidata":"Q45","ISO3166-1:alpha2":"PT","ISO3166-1:alpha3":"PRT","ISO3166-1:numeric":"620"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-2.5605,49.22],[-2.5265,49.0585],[-2.5265,48.927],[-2.243,48.871],[-2.0845,48.871],[-1.9845,48.8825],[-1.9845,48.9405],[-1.944,48.9635],[-1.8605,49.065],[-1.8345,49.1825],[-1.8345,49.25],[-1.893,49.3155],[-1.994,49.3635],[-2.099,49.4595],[-2.1335,49.4075],[-2.295,49.326],[-2.48,49.2645],[-2.5605,49.22]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/1c/Flag_of_Jersey.svg","name:en":"Jersey","wikidata":"Q785","ISO3166-1:alpha2":"JE","ISO3166-1:alpha3":"JEY","ISO3166-1:numeric":"832"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[13.9325,36.0715],[13.948,35.9705],[13.998,35.9],[14.085,35.8485],[14.129,35.7675],[14.244,35.677],[14.387,35.622],[14.5145,35.603],[14.5965,35.6105],[14.6995,35.65],[14.7485,35.685],[14.8085,35.775],[14.825,35.8885],[14.7795,35.9865],[14.6435,36.0925],[14.551,36.14],[14.489,36.2025],[14.4225,36.241],[14.333,36.273],[14.19,36.2835],[14.0735,36.2565],[13.996,36.208],[13.9405,36.1235],[13.9325,36.0715]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/73/Flag_of_Malta.svg","name:en":"Malta","wikidata":"Q233","ISO3166-1:alpha2":"MT","ISO3166-1:alpha3":"MLT","ISO3166-1:numeric":"470"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-6.7445,55.415],[-6.869,55.45],[-6.8885,55.502],[-6.965,55.572],[-7.0905,55.62],[-7.1995,55.635],[-7.3375,55.628],[-7.4335,55.603],[-7.6575,55.514],[-7.706,55.477],[-7.972,55.426],[-8.1305,55.465],[-8.3095,55.472],[-8.425,55.448],[-8.5455,55.383],[-8.588,55.331],[-8.6025,55.262],[-8.6495,55.226],[-8.826,55.145],[-8.898,55.068],[-8.9225,54.976],[-8.907,54.921],[-9.097,54.807],[-9.1665,54.721],[-9.1765,54.633],[-9.133,54.563],[-9.0255,54.497],[-9.116,54.491],[-9.251,54.521],[-9.4465,54.519],[-9.5745,54.526],[-9.6445,54.55],[-9.8225,54.566],[-9.9575,54.541],[-10.03,54.509],[-10.2495,54.462],[-10.357,54.411],[-10.4295,54.318],[-10.5165,54.232],[-10.622,54.162],[-10.661,54.05],[-10.5925,53.904],[-10.5035,53.774],[-10.5925,53.709],[-10.6315,53.647],[-10.634,53.571],[-10.564,53.448],[-10.5545,53.336],[-10.478,53.261],[-10.3315,53.207],[-10.2825,53.143],[-10.1755,53.084],[-10.138,53.039],[-10.04,52.979],[-9.8565,52.928],[-9.8445,52.884],[-9.935,52.811],[-10.1135,52.734],[-10.2175,52.665],[-10.261,52.596],[-10.2495,52.498],[-10.541,52.402],[-10.7045,52.32],[-10.7925,52.286],[-10.917,52.204],[-10.9705,52.15],[-11.0065,52.066],[-11.0055,51.98],[-10.9605,51.915],[-10.8425,51.848],[-10.866,51.788],[-10.843,51.694],[-10.783,51.634],[-10.723,51.602],[-10.6215,51.574],[-10.5665,51.479],[-10.411,51.398],[-10.243,51.37],[-10.121,51.382],[-10.037,51.303],[-9.9145,51.259],[-9.7755,51.248],[-9.691,51.253],[-9.575,51.225],[-9.413,51.232],[-9.294,51.271],[-9.1925,51.268],[-9.0505,51.299],[-8.9895,51.331],[-8.9185,51.331],[-8.7445,51.37],[-8.6215,51.378],[-8.5345,51.403],[-8.3855,51.426],[-8.288,51.474],[-8.2345,51.527],[-8.08,51.593],[-7.868,51.638],[-7.663,51.719],[-7.6045,51.752],[-7.378,51.838],[-7.274,51.932],[-7.0285,51.932],[-6.8935,51.924],[-6.7985,51.939],[-6.6275,51.909],[-6.5305,51.917],[-6.3805,51.973],[-6.2605,51.983],[-6.109,52.012],[-6.0,52.048],[-5.8945,52.144],[-5.887,52.246],[-5.934,52.314],[-6.008,52.363],[-5.9145,52.448],[-5.8645,52.56],[-5.875,52.614],[-5.811,52.724],[-5.7625,52.766],[-5.686,52.895],[-5.666,52.973],[-5.702,53.057],[-5.7125,53.151],[-5.749,53.231],[-5.7085,53.389],[-5.6715,53.439],[-5.6605,53.517],[-5.6695,53.631],[-5.6935,53.679],[-5.764,53.74],[-5.8775,53.784],[-5.888,53.844],[-5.826,53.87],[-5.951,53.9315],[-6.1135,54.033],[-6.2805,54.111],[-6.368,54.1115],[-6.3675,54.07],[-6.48,54.067],[-6.623,54.039],[-6.661,54.0625],[-6.6615,54.1225],[-6.6315,54.147],[-6.664,54.1925],[-6.746,54.189],[-6.8025,54.215],[-6.8775,54.347],[-6.982,54.4095],[-7.0535,54.412],[-7.104,54.3555],[-7.1525,54.3355],[-7.1735,54.286],[-7.147,54.2255],[-7.248,54.2045],[-7.31,54.1675],[-7.347,54.1175],[-7.442,54.154],[-7.4905,54.123],[-7.566,54.1265],[-7.702,54.208],[-7.862,54.2185],[-7.8885,54.3005],[-7.9465,54.3045],[-7.9865,54.344],[-8.056,54.3655],[-8.1605,54.4405],[-8.042,54.506],[-8.006,54.546],[-7.8505,54.533],[-7.7715,54.6215],[-7.9135,54.6765],[-7.855,54.727],[-7.746,54.7045],[-7.6365,54.7515],[-7.5785,54.742],[-7.549,54.7895],[-7.484,54.824],[-7.442,54.8755],[-7.4455,54.9365],[-7.405,54.942],[-7.3915,55.0225],[-7.345,55.051],[-7.218,55.0795],[-6.942,55.2045],[-6.8215,55.2165],[-6.7195,55.28],[-6.7445,55.415]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/45/Flag_of_Ireland.svg","name:en":"Ireland","wikidata":"Q27","ISO3166-1:alpha2":"IE","ISO3166-1:alpha3":"IRL","ISO3166-1:numeric":"372"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[27.0115,-29.649],[27.0965,-29.73],[27.223,-29.997],[27.2965,-30.056],[27.327,-30.148],[27.394,-30.1425],[27.3655,-30.219],[27.3765,-30.3255],[27.4645,-30.314],[27.475,-30.3565],[27.534,-30.3845],[27.6015,-30.4465],[27.6215,-30.5045],[27.693,-30.545],[27.7415,-30.604],[27.7815,-30.6175],[27.918,-30.6025],[27.94,-30.644],[28.004,-30.659],[28.123,-30.6635],[28.114,-30.599],[28.1505,-30.5845],[28.1775,-30.5225],[28.1555,-30.472],[28.1795,-30.439],[28.229,-30.43],[28.271,-30.371],[28.253,-30.3235],[28.2155,-30.307],[28.2365,-30.26],[28.3055,-30.255],[28.3405,-30.2345],[28.365,-30.1685],[28.4,-30.1435],[28.452,-30.158],[28.5685,-30.123],[28.6675,-30.1405],[28.69,-30.1245],[28.859,-30.088],[28.879,-30.07],[28.9855,-30.0315],[29.006,-30.0045],[29.109,-29.937],[29.1695,-29.9165],[29.16,-29.883],[29.1025,-29.85],[29.1315,-29.8255],[29.145,-29.7165],[29.1835,-29.668],[29.236,-29.64],[29.279,-29.6445],[29.285,-29.5865],[29.3195,-29.5815],[29.297,-29.5225],[29.3295,-29.47],[29.4035,-29.444],[29.4555,-29.341],[29.437,-29.2645],[29.408,-29.212],[29.3565,-29.2015],[29.3555,-29.127],[29.308,-29.0765],[29.259,-29.0905],[29.2155,-29.0265],[29.1515,-28.9875],[29.075,-28.919],[28.975,-28.893],[28.939,-28.86],[28.944,-28.7825],[28.905,-28.759],[28.808,-28.771],[28.795,-28.6995],[28.7045,-28.6715],[28.7035,-28.619],[28.648,-28.5715],[28.613,-28.5965],[28.507,-28.618],[28.4715,-28.6055],[28.414,-28.6275],[28.357,-28.6925],[28.2795,-28.714],[28.233,-28.695],[28.166,-28.713],[28.073,-28.8155],[27.9905,-28.883],[27.948,-28.8525],[27.896,-28.9085],[27.8325,-28.906],[27.7295,-28.947],[27.7155,-29.007],[27.6825,-29.024],[27.652,-29.1005],[27.617,-29.153],[27.515,-29.224],[27.548,-29.2475],[27.5115,-29.283],[27.466,-29.294],[27.4365,-29.334],[27.4195,-29.4105],[27.392,-29.4155],[27.363,-29.476],[27.206,-29.559],[27.12,-29.5775],[27.0595,-29.635],[27.0115,-29.649]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/4a/Flag_of_Lesotho.svg","name:en":"Lesotho","wikidata":"Q1013","ISO3166-1:alpha2":"LS","ISO3166-1:alpha3":"LSO","ISO3166-1:numeric":"426"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-25.0135,65.5045],[-24.9885,65.438],[-24.4825,64.793],[-23.716,63.616],[-23.653,63.556],[-23.5495,63.5085],[-23.417,63.4785],[-23.196,63.473],[-22.9865,63.5055],[-20.774,63.102],[-20.5305,63.0885],[-19.0875,63.1855],[-18.704,63.185],[-18.578,63.193],[-18.0645,63.259],[-17.71,63.345],[-17.591,63.399],[-17.3175,63.5465],[-16.581,63.5875],[-16.384,63.62],[-15.935,63.7525],[-15.695,63.8555],[-14.77,64.054],[-14.2055,64.222],[-13.793,64.393],[-13.6425,64.469],[-13.441,64.4005],[-13.178,64.39],[-12.9635,64.4355],[-12.8775,64.479],[-12.817,64.5405],[-12.822,64.64],[-12.8885,64.7005],[-13.0445,64.7605],[-13.2055,64.794],[-13.083,64.877],[-13.032,64.9545],[-13.0135,65.0825],[-13.031,65.19],[-13.1235,65.525],[-13.157,65.5785],[-14.063,66.4485],[-14.224,66.5295],[-14.38,66.56],[-15.89,66.7265],[-16.015,66.7335],[-16.2975,66.7285],[-16.7325,66.685],[-16.879,66.6465],[-17.376,66.4565],[-17.4975,66.476],[-17.481,66.559],[-17.519,66.6195],[-17.6305,66.6925],[-17.7305,66.729],[-17.896,66.7595],[-18.078,66.7645],[-18.3295,66.7235],[-18.4485,66.671],[-18.5135,66.605],[-18.528,66.5385],[-18.458,66.4425],[-18.317,66.377],[-18.8605,66.3845],[-20.119,66.337],[-22.1605,66.629],[-22.296,66.6545],[-22.456,66.6655],[-22.597,66.657],[-22.977,66.6655],[-23.164,66.641],[-23.3815,66.592],[-23.5395,66.535],[-24.1865,66.1665],[-24.5265,65.8975],[-24.9485,65.6015],[-25.0135,65.5045]]],[[[-19.1995,67.151],[-19.177,67.093],[-19.0955,67.029],[-18.8895,66.965],[-18.63,66.949],[-18.4475,66.97],[-18.33,67.002],[-18.203,67.071],[-18.162,67.135],[-18.212,67.239],[-18.3155,67.295],[-18.503,67.341],[-18.7335,67.352],[-18.995,67.312],[-19.111,67.264],[-19.1795,67.207],[-19.1995,67.151]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/ce/Flag_of_Iceland.svg","name:en":"Iceland","wikidata":"Q189","ISO3166-1:alpha2":"IS","ISO3166-1:alpha3":"ISL","ISO3166-1:numeric":"352"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-179.059,-31.3545],[-179.0485,-31.414],[-178.9805,-31.505],[-178.884,-31.55],[-178.801,-31.556],[-178.7045,-31.5285],[-178.639,-31.4785],[-178.6095,-31.437],[-178.5895,-31.3675],[-178.6095,-31.2725],[-178.6795,-31.1955],[-178.7535,-31.162],[-178.884,-31.1595],[-178.9735,-31.199],[-179.0425,-31.2805],[-179.059,-31.3545]]],[[[-178.802,-30.5355],[-178.7945,-30.5885],[-178.754,-30.6595],[-178.6445,-30.73],[-178.551,-30.7455],[-178.434,-30.716],[-178.3785,-30.6755],[-178.334,-30.6075],[-178.3225,-30.526],[-178.358,-30.4325],[-178.2775,-30.397],[-178.2285,-30.356],[-178.191,-30.2935],[-178.182,-30.208],[-178.1955,-30.161],[-178.238,-30.1],[-178.28,-30.068],[-178.3715,-30.03],[-178.452,-30.025],[-178.564,-30.0595],[-178.6415,-30.132],[-178.6675,-30.195],[-178.67,-30.2515],[-178.635,-30.341],[-178.6965,-30.3655],[-178.7675,-30.4295],[-178.802,-30.5355]]],[[[-178.211,-29.245],[-178.1715,-29.3705],[-178.123,-29.436],[-178.0695,-29.472],[-177.9855,-29.4955],[-177.897,-29.498],[-177.84,-29.4855],[-177.7595,-29.447],[-177.696,-29.4025],[-177.6445,-29.337],[-177.626,-29.279],[-177.631,-29.2045],[-177.6755,-29.121],[-177.7735,-29.049],[-177.874,-29.0305],[-178.027,-29.0425],[-178.103,-29.072],[-178.1855,-29.1505],[-178.211,-29.245]]],[[[-177.2445,-43.9045],[-177.233,-43.9605],[-177.1915,-44.021],[-177.1025,-44.078],[-177.029,-44.098],[-176.946,-44.1025],[-176.9105,-44.1775],[-176.852,-44.252],[-176.7815,-44.296],[-176.6675,-44.3255],[-176.636,-44.3875],[-176.539,-44.5045],[-176.444,-44.5735],[-176.3675,-44.6135],[-176.213,-44.634],[-176.0715,-44.5955],[-175.9835,-44.5205],[-175.834,-44.5385],[-175.7195,-44.511],[-175.6235,-44.442],[-175.5935,-44.3895],[-175.5875,-44.308],[-175.6115,-44.2515],[-175.6795,-44.186],[-175.737,-44.1535],[-175.6615,-44.1275],[-175.58,-44.0645],[-175.5425,-43.982],[-175.5495,-43.9145],[-175.6165,-43.8255],[-175.6865,-43.7865],[-175.77,-43.7655],[-175.915,-43.7695],[-175.93,-43.6685],[-175.957,-43.6285],[-176.0765,-43.548],[-176.18,-43.526],[-176.3395,-43.5335],[-176.4825,-43.516],[-176.5385,-43.4975],[-176.59,-43.4295],[-176.6555,-43.3885],[-176.8185,-43.3265],[-176.9295,-43.311],[-177.063,-43.3375],[-177.1345,-43.3805],[-177.193,-43.464],[-177.186,-43.576],[-177.0975,-43.6675],[-177.2175,-43.817],[-177.2445,-43.9045]]],[[[165.5535,-50.802],[165.5985,-50.9295],[165.6945,-51.024],[165.804,-51.08],[165.982,-51.1205],[166.145,-51.1255],[166.2735,-51.0975],[166.4,-51.042],[166.518,-50.945],[166.561,-50.837],[166.532,-50.7445],[166.6435,-50.656],[166.6725,-50.5985],[166.6655,-50.5125],[166.6,-50.401],[166.542,-50.35],[166.424,-50.3005],[166.2625,-50.287],[166.0145,-50.323],[165.841,-50.381],[165.691,-50.49],[165.635,-50.5755],[165.636,-50.643],[165.576,-50.714],[165.5535,-50.802]]],[[[166.1385,-45.907],[166.167,-46.0415],[166.2035,-46.1205],[166.2875,-46.2185],[166.363,-46.2655],[166.4515,-46.3465],[166.565,-46.396],[166.6705,-46.4185],[166.5815,-46.514],[166.576,-46.611],[166.624,-46.6865],[166.672,-46.7225],[166.784,-46.768],[166.8745,-46.7815],[167.019,-46.765],[167.097,-46.7315],[167.171,-46.6625],[167.195,-46.602],[167.1815,-46.5065],[167.1545,-46.465],[167.2925,-46.459],[167.5335,-46.513],[167.541,-46.5305],[167.4045,-46.598],[167.3235,-46.673],[167.3005,-46.761],[167.3655,-46.9345],[167.2105,-46.9865],[167.1435,-47.043],[167.05,-47.146],[167.0285,-47.1995],[167.039,-47.284],[167.099,-47.3585],[167.201,-47.4225],[167.3105,-47.4625],[167.431,-47.484],[167.531,-47.4895],[167.5375,-47.579],[167.5715,-47.6305],[167.6335,-47.6785],[167.731,-47.7145],[167.8025,-47.7235],[167.918,-47.713],[168.0065,-47.682],[168.0895,-47.621],[168.1305,-47.509],[168.193,-47.431],[168.1955,-47.328],[168.3265,-47.3065],[168.464,-47.245],[168.533,-47.1865],[168.562,-47.13],[168.544,-47.015],[168.7345,-46.95],[168.838,-46.869],[169.0405,-46.876],[169.249,-46.8615],[169.342,-46.8395],[169.442,-46.83],[169.603,-46.778],[169.745,-46.7455],[170.048,-46.5815],[170.0955,-46.527],[170.116,-46.4535],[170.3835,-46.2925],[170.4385,-46.2375],[170.482,-46.141],[170.7805,-46.091],[170.9135,-46.038],[170.9845,-45.987],[171.0345,-45.8925],[171.0375,-45.7895],[170.988,-45.676],[171.0075,-45.636],[171.0835,-45.57],[171.1345,-45.466],[171.152,-45.36],[171.1395,-45.31],[171.2515,-45.1855],[171.324,-45.1265],[171.4075,-45.0145],[171.4465,-44.9085],[171.456,-44.814],[171.4405,-44.6345],[171.519,-44.52],[171.5445,-44.426],[171.616,-44.376],[171.7345,-44.315],[172.0585,-44.173],[172.2565,-44.0995],[172.401,-44.0575],[172.623,-44.032],[172.6775,-44.06],[172.825,-44.096],[172.9275,-44.103],[173.0335,-44.092],[173.131,-44.067],[173.2765,-43.994],[173.3485,-43.9265],[173.39,-43.8465],[173.407,-43.78],[173.4035,-43.715],[173.338,-43.587],[173.242,-43.5085],[173.167,-43.47],[172.989,-43.4245],[172.9845,-43.362],[173.0065,-43.296],[173.084,-43.2635],[173.207,-43.2405],[173.296,-43.1855],[173.3235,-43.1535],[173.4225,-43.1065],[173.555,-42.9815],[173.716,-42.6965],[173.7705,-42.63],[173.894,-42.58],[173.978,-42.4925],[173.995,-42.4115],[174.17,-42.2675],[174.207,-42.1875],[174.208,-42.1465],[174.3825,-41.9915],[174.4475,-41.914],[174.516,-41.862],[174.562,-41.7845],[174.5645,-41.7075],[174.5065,-41.603],[174.42,-41.5435],[174.395,-41.4805],[174.4295,-41.4575],[174.5365,-41.5265],[174.735,-41.5875],[174.813,-41.625],[174.9825,-41.6785],[175.0195,-41.731],[175.114,-41.791],[175.269,-41.815],[175.3815,-41.8055],[175.5545,-41.7515],[175.67,-41.6825],[175.741,-41.6565],[175.84,-41.584],[175.982,-41.5365],[176.039,-41.5055],[176.1095,-41.4195],[176.2435,-41.325],[176.3065,-41.2575],[176.3625,-41.0965],[176.4745,-40.9805],[176.522,-40.857],[176.6175,-40.7525],[176.6915,-40.6855],[176.76,-40.6635],[176.8275,-40.619],[176.864,-40.574],[176.918,-40.3995],[176.9755,-40.3655],[177.0885,-40.2485],[177.1305,-40.186],[177.1625,-40.0555],[177.203,-39.986],[177.2815,-39.897],[177.288,-39.7895],[177.3495,-39.6925],[177.3545,-39.613],[177.2955,-39.3095],[177.431,-39.266],[177.59,-39.2545],[177.6225,-39.3805],[177.7125,-39.469],[177.81,-39.503],[177.94,-39.501],[178.0805,-39.443],[178.137,-39.4075],[178.1895,-39.3405],[178.202,-39.2405],[178.2575,-39.1475],[178.247,-39.036],[178.1665,-38.9465],[178.1735,-38.8945],[178.357,-38.785],[178.412,-38.728],[178.501,-38.662],[178.5435,-38.591],[178.555,-38.5355],[178.592,-38.4795],[178.6315,-38.279],[178.6165,-38.2245],[178.634,-38.0705],[178.63,-38.03],[178.6545,-37.961],[178.704,-37.8775],[178.8125,-37.77],[178.8355,-37.6705],[178.8065,-37.5905],[178.757,-37.5395],[178.628,-37.4725],[178.5235,-37.4385],[178.4815,-37.403],[178.401,-37.367],[178.2405,-37.334],[177.9245,-37.338],[177.831,-37.3725],[177.7025,-37.476],[177.6205,-37.4945],[177.5445,-37.539],[177.463,-37.619],[177.349,-37.766],[177.3115,-37.7895],[177.2235,-37.7835],[177.1905,-37.7365],[177.265,-37.7265],[177.3515,-37.6875],[177.4115,-37.6295],[177.4475,-37.534],[177.4335,-37.4485],[177.389,-37.386],[177.2685,-37.307],[177.139,-37.2755],[177.06,-37.2845],[176.977,-37.318],[176.9275,-37.358],[176.8835,-37.432],[176.8765,-37.4955],[176.9055,-37.5745],[176.9505,-37.6225],[176.8085,-37.623],[176.7525,-37.519],[176.6995,-37.4615],[176.5765,-37.38],[176.5145,-37.353],[176.5285,-37.244],[176.5125,-37.197],[176.46,-37.132],[176.411,-37.0985],[176.3135,-37.067],[176.3455,-37.0095],[176.351,-36.9015],[176.3045,-36.8195],[176.2315,-36.7605],[176.1665,-36.731],[176.1935,-36.663],[176.192,-36.583],[176.1375,-36.47],[176.033,-36.4025],[175.972,-36.3055],[175.906,-36.2595],[175.779,-36.228],[175.755,-36.1855],[175.763,-36.1045],[175.7115,-35.9985],[175.639,-35.9475],[175.5545,-35.863],[175.48,-35.8325],[175.3785,-35.8255],[175.319,-35.768],[175.2295,-35.7215],[175.155,-35.702],[175.0225,-35.7],[174.932,-35.7245],[174.8855,-35.702],[174.9415,-35.648],[174.99,-35.5195],[174.9745,-35.3835],[174.9005,-35.295],[174.8045,-35.2515],[174.718,-35.244],[174.6215,-35.2675],[174.5855,-35.2105],[174.584,-35.1435],[174.562,-35.079],[174.5065,-35.016],[174.4265,-34.9755],[174.232,-34.956],[174.148,-34.8395],[174.0915,-34.8015],[173.9955,-34.772],[173.8755,-34.772],[173.8285,-34.7575],[173.695,-34.742],[173.5725,-34.6355],[173.5155,-34.599],[173.4485,-34.5775],[173.2455,-34.5665],[173.279,-34.49],[173.298,-34.401],[173.2795,-34.335],[173.1985,-34.25],[173.117,-34.209],[172.979,-34.1955],[172.8965,-34.216],[172.7585,-34.231],[172.7005,-34.2205],[172.6145,-34.227],[172.5155,-34.2725],[172.4365,-34.3505],[172.3995,-34.419],[172.402,-34.5275],[172.4565,-34.609],[172.581,-34.6975],[172.7405,-34.846],[172.8915,-35.0225],[172.8505,-35.0665],[172.814,-35.1445],[172.827,-35.2665],[172.8605,-35.316],[172.934,-35.3855],[173.027,-35.507],[173.133,-35.6135],[173.1585,-35.6595],[173.2415,-35.76],[173.2895,-35.7985],[173.359,-35.884],[173.4085,-35.9245],[173.5585,-36.0865],[173.7195,-36.272],[173.7745,-36.351],[173.7975,-36.445],[173.8395,-36.5105],[173.9675,-36.6105],[174.059,-36.71],[174.17,-36.867],[174.2155,-36.998],[174.2335,-37.083],[174.271,-37.159],[174.339,-37.2145],[174.4475,-37.41],[174.4915,-37.618],[174.562,-37.728],[174.532,-37.7735],[174.481,-37.7835],[174.396,-37.824],[174.3415,-37.881],[174.3145,-37.957],[174.324,-38.028],[174.3795,-38.1065],[174.4265,-38.1385],[174.4515,-38.2475],[174.4135,-38.2855],[174.3775,-38.3775],[174.3755,-38.582],[174.349,-38.732],[174.283,-38.782],[174.193,-38.782],[174.1105,-38.7985],[174.0005,-38.845],[173.886,-38.8715],[173.791,-38.9405],[173.6545,-39.017],[173.5715,-39.0885],[173.505,-39.217],[173.4925,-39.304],[173.533,-39.4555],[173.5735,-39.5195],[173.7395,-39.674],[173.8875,-39.74],[174.0005,-39.768],[174.1175,-39.7855],[174.2855,-39.91],[174.417,-39.9845],[174.6495,-40.0605],[174.7705,-40.067],[174.86,-40.127],[174.9465,-40.2285],[174.9665,-40.368],[174.9415,-40.501],[174.894,-40.6225],[174.809,-40.6405],[174.7165,-40.696],[174.643,-40.7715],[174.612,-40.826],[174.6035,-40.896],[174.474,-40.815],[174.373,-40.7855],[174.3145,-40.7305],[174.2375,-40.5705],[174.164,-40.505],[174.086,-40.472],[173.9785,-40.4615],[173.8575,-40.4925],[173.717,-40.5645],[173.5975,-40.656],[173.5255,-40.7775],[173.5005,-40.863],[173.456,-40.8795],[173.289,-40.749],[173.34,-40.621],[173.331,-40.533],[173.275,-40.4525],[173.148,-40.368],[173.0,-40.321],[172.837,-40.3015],[172.6135,-40.3045],[172.494,-40.342],[172.281,-40.4715],[172.1345,-40.582],[172.0355,-40.625],[171.9115,-40.7365],[171.852,-40.825],[171.826,-40.925],[171.8425,-41.025],[171.8385,-41.1595],[171.8235,-41.2805],[171.764,-41.3315],[171.6805,-41.4625],[171.598,-41.527],[171.4295,-41.5235],[171.349,-41.543],[171.281,-41.581],[171.2175,-41.6545],[171.182,-41.746],[171.186,-41.812],[171.145,-41.914],[171.1165,-41.953],[171.059,-42.085],[171.046,-42.1965],[170.9715,-42.293],[170.9205,-42.424],[170.8575,-42.5055],[170.73,-42.6025],[170.6465,-42.7025],[170.562,-42.765],[170.4625,-42.8165],[170.3695,-42.834],[170.2715,-42.868],[170.2035,-42.9115],[170.0625,-42.9695],[169.975,-43.067],[169.844,-43.155],[169.8015,-43.1965],[169.68,-43.233],[169.6015,-43.2795],[169.5,-43.3865],[169.2205,-43.475],[169.0045,-43.581],[168.923,-43.6425],[168.839,-43.657],[168.6645,-43.722],[168.6205,-43.7545],[168.555,-43.7615],[168.422,-43.801],[168.3515,-43.802],[168.2355,-43.8285],[168.169,-43.863],[168.031,-44.0105],[167.8635,-44.1075],[167.8165,-44.147],[167.7385,-44.2415],[167.6285,-44.33],[167.528,-44.4505],[167.403,-44.5175],[167.312,-44.598],[167.194,-44.657],[167.0545,-44.741],[166.881,-44.8915],[166.7825,-44.989],[166.725,-45.0285],[166.6335,-45.117],[166.51,-45.276],[166.414,-45.439],[166.323,-45.4935],[166.193,-45.6255],[166.1525,-45.7095],[166.1385,-45.907]]],[[[166.192,-48.066],[166.207,-48.129],[166.262,-48.1945],[166.3245,-48.232],[166.4115,-48.2585],[166.495,-48.2655],[166.672,-48.245],[166.819,-48.1995],[166.879,-48.155],[166.922,-48.0855],[166.928,-48.0405],[166.903,-47.964],[166.846,-47.893],[166.7155,-47.824],[166.5895,-47.809],[166.466,-47.831],[166.3455,-47.887],[166.2525,-47.944],[166.2165,-47.986],[166.192,-48.066]]],[[[168.6555,-52.5555],[168.6695,-52.615],[168.734,-52.6865],[168.83,-52.7425],[169.003,-52.8065],[169.1055,-52.821],[169.223,-52.813],[169.3805,-52.7715],[169.528,-52.6795],[169.584,-52.604],[169.5875,-52.504],[169.538,-52.3975],[169.482,-52.339],[169.4085,-52.2995],[169.2825,-52.27],[169.0915,-52.269],[168.9595,-52.296],[168.7555,-52.412],[168.687,-52.4725],[168.6555,-52.5555]]],[[[171.7885,-34.184],[171.8025,-34.251],[171.852,-34.321],[171.9295,-34.37],[172.02,-34.3895],[172.0875,-34.3855],[172.2545,-34.3485],[172.3295,-34.303],[172.382,-34.2425],[172.41,-34.1225],[172.3875,-34.0475],[172.308,-33.9675],[172.2045,-33.932],[172.1145,-33.934],[171.932,-33.9975],[171.873,-34.0305],[171.8195,-34.086],[171.7885,-34.184]]],[[[178.4075,-49.6745],[178.4225,-49.7485],[178.4765,-49.8265],[178.537,-49.8665],[178.6645,-49.9095],[178.812,-49.9165],[178.9105,-49.897],[179.0375,-49.8325],[179.1115,-49.736],[179.1315,-49.6515],[179.095,-49.548],[178.9925,-49.474],[178.875,-49.4415],[178.7915,-49.438],[178.6675,-49.461],[178.5105,-49.5245],[178.4355,-49.5905],[178.4075,-49.6745]]],[[[178.7065,-47.7525],[178.741,-47.847],[178.7785,-47.8845],[178.869,-47.9335],[178.951,-47.956],[179.0695,-47.968],[179.1665,-47.955],[179.2655,-47.913],[179.318,-47.8705],[179.355,-47.8125],[179.3515,-47.7045],[179.307,-47.644],[179.2425,-47.599],[179.086,-47.5505],[179.0035,-47.546],[178.877,-47.5715],[178.781,-47.6215],[178.735,-47.669],[178.7065,-47.7525]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/3e/Flag_of_New_Zealand.svg","name:en":"New Zealand","wikidata":"Q664","ISO3166-1:alpha2":"NZ","ISO3166-1:alpha3":"NZL","ISO3166-1:numeric":"554"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[19.999,-24.764],[19.999,-24.434],[19.999,-23.863],[19.999,-23.309],[19.999,-22.281],[19.999,-22.0],[20.58,-22.0005],[20.9985,-22.0005],[20.999,-21.746],[20.999,-21.006],[20.999,-20.5435],[20.999,-19.866],[20.999,-19.166],[20.999,-18.3175],[21.453,-18.3175],[22.048,-18.205],[22.5,-18.1175],[23.0985,-18.001],[23.297,-18.001],[23.324,-18.082],[23.379,-18.135],[23.4105,-18.2035],[23.5205,-18.2695],[23.558,-18.336],[23.5725,-18.482],[23.616,-18.5025],[23.6565,-18.4785],[23.6825,-18.436],[23.722,-18.433],[23.7895,-18.382],[23.806,-18.3415],[23.8965,-18.263],[23.925,-18.2565],[23.9315,-18.2045],[24.091,-18.1205],[24.149,-18.075],[24.1925,-18.02],[24.294,-18.0305],[24.372,-17.949],[24.4265,-17.9535],[24.482,-18.0105],[24.5055,-18.06],[24.579,-18.07],[24.6185,-17.996],[24.6655,-17.95],[24.6965,-17.9445],[24.732,-17.8925],[24.864,-17.8455],[24.9105,-17.8105],[24.954,-17.8005],[25.0075,-17.8385],[25.052,-17.808],[25.0835,-17.837],[25.144,-17.808],[25.1625,-17.7785],[25.262,-17.7905],[25.194,-17.7695],[25.0895,-17.663],[25.032,-17.5865],[24.998,-17.592],[24.975,-17.5535],[24.9075,-17.5545],[24.8835,-17.529],[24.829,-17.52],[24.8025,-17.5365],[24.7085,-17.497],[24.6275,-17.511],[24.559,-17.539],[24.453,-17.48],[24.3815,-17.4715],[24.327,-17.492],[24.2485,-17.474],[23.927,-17.539],[23.434,-17.638],[22.9965,-17.724],[22.5,-17.821],[21.423,-18.026],[21.268,-17.967],[21.21,-17.929],[21.1145,-17.9355],[21.095,-17.9495],[20.977,-17.958],[20.9055,-17.995],[20.8665,-18.0295],[20.8025,-18.0375],[20.749,-18.0035],[20.719,-18.013],[20.6455,-17.977],[20.56,-17.9845],[20.4765,-17.952],[20.4385,-17.9065],[20.368,-17.8965],[20.3325,-17.8665],[20.2755,-17.865],[20.233,-17.8845],[20.1745,-17.877],[20.116,-17.9095],[20.057,-17.8855],[20.028,-17.9055],[19.912,-17.862],[19.863,-17.8835],[19.8065,-17.8685],[19.7425,-17.9075],[19.6735,-17.8495],[19.5665,-17.874],[19.508,-17.8605],[19.4085,-17.8675],[19.331,-17.852],[19.2715,-17.8205],[19.231,-17.828],[19.138,-17.8075],[19.118,-17.8365],[19.0345,-17.835],[18.8945,-17.816],[18.834,-17.772],[18.7905,-17.7665],[18.624,-17.636],[18.598,-17.5835],[18.5685,-17.5845],[18.5335,-17.5205],[18.4215,-17.3895],[17.6265,-17.3895],[16.8945,-17.39],[16.107,-17.3905],[15.578,-17.3905],[15.2015,-17.391],[14.637,-17.3915],[14.2225,-17.3915],[14.2125,-17.41],[14.101,-17.439],[14.017,-17.415],[13.9555,-17.433],[13.9165,-17.378],[13.8685,-17.343],[13.68,-17.247],[13.5415,-17.147],[13.5225,-17.0905],[13.4645,-17.013],[13.352,-16.974],[13.2725,-16.9985],[13.17,-16.964],[13.0905,-16.98],[12.9865,-16.982],[12.9155,-17.021],[12.8725,-17.0645],[12.6965,-17.169],[12.6175,-17.207],[12.5945,-17.235],[12.4505,-17.2545],[12.4235,-17.2075],[12.2835,-17.242],[12.25,-17.2405],[12.184,-17.1965],[12.162,-17.1625],[12.102,-17.161],[12.079,-17.142],[11.9325,-17.19],[11.7985,-17.268],[11.7495,-17.25],[11.542,-17.25],[11.5465,-17.306],[11.5305,-17.4595],[11.528,-17.5895],[11.5605,-17.802],[11.592,-17.9275],[11.5975,-18.0075],[11.6315,-18.1435],[11.6665,-18.23],[11.7475,-18.3225],[11.794,-18.4325],[11.8015,-18.491],[11.838,-18.5745],[11.9035,-18.657],[12.1525,-18.8605],[12.2635,-19.0055],[12.2815,-19.0735],[12.3135,-19.1265],[12.396,-19.2085],[12.43,-19.283],[12.4925,-19.389],[12.505,-19.4485],[12.541,-19.5255],[12.656,-19.7225],[12.6825,-19.7875],[12.739,-19.8765],[12.762,-19.938],[12.83,-20.0625],[12.864,-20.16],[12.9125,-20.2265],[12.997,-20.304],[13.0325,-20.3875],[13.0445,-20.5105],[13.137,-20.687],[13.166,-20.824],[13.2025,-20.9225],[13.2355,-20.9845],[13.321,-21.0895],[13.399,-21.2475],[13.492,-21.351],[13.536,-21.4275],[13.619,-21.527],[13.6535,-21.584],[13.682,-21.6645],[13.7375,-21.7505],[13.737,-21.7985],[13.76,-21.866],[13.806,-21.922],[13.883,-21.984],[14.0785,-22.211],[14.1005,-22.2625],[14.2035,-22.3855],[14.2665,-22.555],[14.2895,-22.5965],[14.3055,-22.7165],[14.2555,-22.7665],[14.197,-22.905],[14.186,-23.016],[14.2185,-23.16],[14.2595,-23.268],[14.216,-23.385],[14.231,-23.4975],[14.2875,-23.6405],[14.28,-23.7515],[14.2875,-23.8305],[14.244,-23.9235],[14.2335,-23.997],[14.253,-24.146],[14.301,-24.2975],[14.3875,-24.4815],[14.34,-24.539],[14.314,-24.6045],[14.316,-24.6825],[14.343,-24.744],[14.395,-24.7975],[14.4635,-24.831],[14.5615,-24.838],[14.5775,-24.948],[14.637,-25.0805],[14.5805,-25.2945],[14.59,-25.4],[14.639,-25.5545],[14.624,-25.606],[14.6075,-25.749],[14.6225,-25.822],[14.684,-25.9195],[14.7075,-26.0145],[14.73,-26.0525],[14.709,-26.1535],[14.7235,-26.231],[14.7145,-26.312],[14.741,-26.413],[14.788,-26.4855],[14.887,-26.546],[14.8555,-26.632],[14.8575,-26.7095],[14.8765,-26.7895],[14.9825,-27.089],[15.013,-27.14],[15.0455,-27.271],[15.0935,-27.412],[15.1795,-27.5465],[15.238,-27.6075],[15.3015,-27.73],[15.3225,-27.7995],[15.371,-27.878],[15.4455,-27.951],[15.476,-28.0395],[15.5475,-28.132],[15.696,-28.2745],[15.897,-28.4315],[16.066,-28.5905],[16.159,-28.6855],[16.238,-28.7455],[16.3335,-28.8005],[16.453,-28.633],[16.48,-28.5655],[16.5925,-28.5355],[16.621,-28.5],[16.6885,-28.4705],[16.734,-28.4945],[16.777,-28.44],[16.7695,-28.396],[16.808,-28.361],[16.755,-28.302],[16.803,-28.222],[16.857,-28.207],[16.888,-28.173],[16.8845,-28.09],[17.0825,-28.033],[17.1445,-28.0955],[17.1965,-28.128],[17.184,-28.209],[17.215,-28.248],[17.334,-28.226],[17.3825,-28.296],[17.376,-28.321],[17.414,-28.376],[17.3965,-28.4275],[17.3555,-28.437],[17.333,-28.475],[17.3655,-28.509],[17.389,-28.571],[17.4295,-28.57],[17.43,-28.7215],[17.4935,-28.693],[17.599,-28.6945],[17.619,-28.765],[17.736,-28.7565],[17.9255,-28.769],[17.972,-28.7915],[18.046,-28.8745],[18.079,-28.87],[18.194,-28.9155],[18.2635,-28.8795],[18.351,-28.8805],[18.4235,-28.901],[18.533,-28.868],[18.7285,-28.8315],[18.9355,-28.857],[18.9835,-28.879],[19.006,-28.9245],[19.1185,-28.9695],[19.1965,-28.947],[19.23,-28.9125],[19.285,-28.9085],[19.24,-28.806],[19.27,-28.742],[19.4055,-28.7365],[19.4895,-28.682],[19.507,-28.598],[19.5485,-28.541],[19.622,-28.511],[19.671,-28.5295],[19.73,-28.488],[19.813,-28.5085],[19.847,-28.46],[19.999,-28.428],[19.999,-27.8885],[19.999,-27.547],[20.0,-26.9125],[19.999,-26.6185],[19.9995,-26.1425],[19.999,-25.7805],[19.999,-25.2205],[19.999,-24.764]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/00/Flag_of_Namibia.svg","name:en":"Namibia","wikidata":"Q1030","ISO3166-1:alpha2":"NA","ISO3166-1:alpha3":"NAM","ISO3166-1:numeric":"516"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[114.095,10.97],[114.12,10.952],[114.1635,10.8865],[114.2185,10.86],[114.2795,10.8515],[114.281,10.827],[114.2355,10.7665],[114.2155,10.694],[114.227,10.612],[114.2845,10.531],[114.4985,10.493],[114.573,10.543],[114.6155,10.6145],[114.6255,10.697],[114.605,10.7685],[114.5595,10.8275],[114.4955,10.867],[114.4215,10.8805],[114.4865,11.006],[114.491,11.0685],[114.474,11.134],[114.4355,11.1905],[114.333,11.2495],[114.2015,11.124],[114.095,10.97]]],[[[115.5995,10.716],[115.6435,10.6055],[115.6915,10.562],[115.7755,10.53],[115.8675,10.537],[115.9625,10.598],[116.0065,10.681],[116.029,10.816],[116.0215,10.8705],[115.9695,10.9595],[115.914,10.9995],[115.8345,11.0205],[115.7805,11.017],[115.6945,10.978],[115.6395,10.9145],[115.62,10.864],[115.5995,10.716]]],[[[118.8375,4.4525],[119.1115,4.2635],[119.2095,4.219],[119.2465,4.216],[119.347,4.2405],[119.955,4.616],[120.786,5.024],[122.077,5.8975],[122.316,6.229],[124.18,5.9645],[124.219,5.937],[125.2055,5.231],[125.298,5.1785],[125.4345,5.166],[125.4905,5.178],[125.577,5.2195],[125.6245,5.257],[126.336,6.127],[126.393,6.222],[126.785,7.195],[126.8075,7.293],[126.799,7.5325],[126.771,7.761],[126.68,8.256],[126.5475,8.9195],[126.3675,9.8735],[126.164,10.814],[125.76,12.173],[125.7075,12.2895],[125.669,12.345],[125.5445,12.4895],[125.375,12.669],[125.237,12.794],[124.461,14.2155],[124.381,14.284],[123.107,14.633],[122.4615,16.318],[122.6355,16.737],[122.7225,17.009],[122.7375,17.127],[122.6025,17.995],[122.5485,18.338],[122.397,19.781],[122.3755,19.9835],[122.168,21.1635],[122.1075,21.2625],[122.0315,21.305],[121.947,21.322],[121.8945,21.3155],[121.7965,21.274],[121.75,21.2125],[121.738,21.17],[121.5775,20.7765],[121.325,20.0865],[121.0355,19.2735],[120.3765,18.5835],[120.3555,18.5365],[120.1475,17.739],[120.133,17.5915],[119.833,16.9405],[119.5925,16.402],[119.5795,16.3635],[119.5445,16.1725],[119.542,15.937],[119.5755,15.7905],[119.812,13.8835],[119.662,12.407],[119.0675,11.2985],[118.452,10.1085],[117.477,9.1845],[117.3235,9.03],[117.192,8.87],[117.077,8.708],[116.719,8.1185],[116.7005,8.036],[116.688,7.9015],[116.719,7.7675],[116.8105,7.68],[116.8375,7.6665],[117.0,7.6665],[117.425,7.4125],[117.967,6.867],[117.9665,6.2835],[118.3335,6.0],[118.833,6.0],[119.5835,5.2665],[119.3335,5.025],[119.0,4.7],[119.0,4.443],[118.8375,4.4525]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/99/Flag_of_the_Philippines.svg","name:en":"Philippines","wikidata":"Q928","ISO3166-1:alpha2":"PH","ISO3166-1:alpha3":"PHL","ISO3166-1:numeric":"608"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[31.975,-25.9525],[32.003,-25.997],[32.0875,-26.0085],[32.1055,-26.1615],[32.0745,-26.3055],[32.0775,-26.4055],[32.135,-26.527],[32.1345,-26.8405],[32.1865,-26.863],[32.2205,-26.833],[32.33,-26.8635],[32.692,-26.868],[32.902,-26.858],[33.1025,-26.921],[33.117,-26.881],[33.1275,-26.692],[33.1205,-26.649],[33.1525,-26.449],[33.1475,-26.4115],[33.183,-26.0885],[33.2055,-26.0345],[33.492,-25.4355],[33.6955,-25.3585],[33.7895,-25.314],[33.9495,-25.2565],[34.062,-25.2245],[34.298,-25.136],[34.3655,-25.12],[34.6355,-25.0195],[34.8965,-24.934],[35.1185,-24.8385],[35.2135,-24.7865],[35.299,-24.728],[35.391,-24.625],[35.4145,-24.572],[35.5805,-24.3775],[35.6515,-24.28],[35.7055,-24.1755],[35.718,-24.1245],[35.7205,-24.0245],[35.7625,-23.9315],[35.7715,-23.842],[35.7495,-23.7425],[35.7225,-23.6855],[35.6485,-23.621],[35.6405,-23.542],[35.701,-23.37],[35.709,-23.324],[35.7115,-23.1765],[35.7655,-23.095],[35.811,-22.993],[35.82,-22.917],[35.793,-22.8275],[35.73,-22.7615],[35.721,-22.595],[35.7555,-22.44],[35.7595,-22.324],[35.7475,-22.238],[35.761,-22.1125],[35.741,-22.0505],[35.6865,-21.9835],[35.606,-21.936],[35.646,-21.8895],[35.676,-21.8215],[35.7095,-21.669],[35.7115,-21.5595],[35.6975,-21.472],[35.674,-21.424],[35.62,-21.361],[35.5685,-21.3305],[35.4545,-21.303],[35.3875,-21.3105],[35.3325,-21.3345],[35.32,-21.203],[35.328,-21.1655],[35.315,-21.067],[35.3425,-20.944],[35.333,-20.8715],[35.2645,-20.7665],[35.23,-20.737],[35.1985,-20.637],[35.129,-20.5395],[35.063,-20.474],[35.0105,-20.443],[34.9315,-20.4185],[34.918,-20.351],[34.9805,-20.2155],[34.989,-20.0985],[34.971,-20.035],[35.097,-19.969],[35.177,-19.9025],[35.2535,-19.8575],[35.4705,-19.6755],[35.563,-19.588],[35.691,-19.433],[35.9075,-19.2035],[36.0515,-19.1315],[36.252,-19.089],[36.3545,-19.072],[36.468,-18.999],[36.5535,-18.907],[36.5875,-18.8485],[36.599,-18.7805],[36.6745,-18.6865],[36.713,-18.622],[36.8225,-18.5185],[36.9765,-18.363],[37.045,-18.308],[37.082,-18.2395],[37.1485,-18.1645],[37.1705,-18.1155],[37.3015,-17.952],[37.389,-17.8825],[37.5225,-17.805],[37.9355,-17.588],[38.06,-17.5345],[38.1085,-17.5275],[38.9015,-17.449],[38.985,-17.423],[39.2165,-17.292],[39.6555,-16.979],[39.8665,-16.806],[40.093,-16.609],[40.226,-16.4615],[40.75,-15.5855],[40.9645,-15.1705],[40.9935,-15.0995],[41.041,-14.861],[41.0545,-14.469],[41.05,-14.419],[40.998,-14.188],[40.956,-14.1035],[40.897,-14.049],[40.822,-13.9215],[40.792,-13.824],[40.774,-13.6745],[40.7955,-13.63],[40.806,-13.557],[40.775,-13.451],[40.789,-13.3715],[40.7675,-13.254],[40.7605,-13.104],[40.8025,-13.008],[40.8405,-12.7685],[40.849,-12.4495],[40.84,-11.8315],[40.856,-11.7145],[40.9335,-11.0365],[40.9305,-10.973],[40.893,-10.7945],[40.8395,-10.6215],[40.735,-10.4665],[40.6895,-10.364],[40.647,-10.325],[40.493,-10.4145],[40.419,-10.483],[40.371,-10.5505],[40.298,-10.566],[40.2155,-10.6105],[40.176,-10.668],[40.1345,-10.69],[39.999,-10.807],[39.876,-10.846],[39.7595,-10.9155],[39.595,-10.9515],[39.522,-10.9815],[39.438,-11.0485],[39.356,-11.083],[39.274,-11.1575],[39.2365,-11.173],[39.123,-11.1435],[39.0785,-11.159],[38.883,-11.172],[38.8495,-11.2085],[38.7675,-11.25],[38.7495,-11.274],[38.6635,-11.273],[38.611,-11.307],[38.5775,-11.3475],[38.5365,-11.3655],[38.489,-11.4165],[38.3705,-11.3745],[38.316,-11.3265],[38.2825,-11.3275],[38.265,-11.2875],[38.1115,-11.2605],[38.064,-11.2775],[37.9705,-11.2725],[37.944,-11.2585],[37.8305,-11.311],[37.8275,-11.3725],[37.7775,-11.4575],[37.7855,-11.505],[37.753,-11.547],[37.6555,-11.5795],[37.581,-11.626],[37.502,-11.614],[37.4455,-11.6365],[37.415,-11.6855],[37.333,-11.678],[37.301,-11.641],[37.2255,-11.6545],[37.1575,-11.6315],[37.08,-11.625],[37.044,-11.593],[36.9865,-11.58],[36.9105,-11.6055],[36.8365,-11.584],[36.794,-11.6085],[36.767,-11.649],[36.6225,-11.737],[36.5735,-11.6985],[36.5195,-11.7615],[36.517,-11.6975],[36.494,-11.6815],[36.3835,-11.6805],[36.3545,-11.7135],[36.2875,-11.7205],[36.27,-11.6905],[36.221,-11.708],[36.183,-11.671],[36.1885,-11.598],[36.132,-11.5555],[36.128,-11.518],[36.063,-11.5405],[36.042,-11.4945],[36.0085,-11.487],[35.95,-11.4315],[35.9135,-11.438],[35.8465,-11.412],[35.8115,-11.4195],[35.766,-11.471],[35.7195,-11.475],[35.708,-11.509],[35.6505,-11.5505],[35.561,-11.566],[35.4155,-11.572],[35.2315,-11.5685],[34.959,-11.5735],[34.637,-11.5735],[34.6435,-11.656],[34.6345,-11.739],[34.591,-11.856],[34.484,-11.9725],[34.378,-12.1715],[34.4175,-12.2655],[34.43,-12.401],[34.4555,-12.571],[34.5205,-12.6825],[34.535,-12.7365],[34.5305,-12.88],[34.576,-13.232],[34.5615,-13.348],[34.607,-13.483],[34.867,-13.483],[34.927,-13.5465],[35.0895,-13.6995],[35.2275,-13.881],[35.487,-14.1705],[35.5205,-14.259],[35.872,-14.6755],[35.872,-14.8955],[35.9185,-14.8955],[35.79,-15.17],[35.853,-15.419],[35.8065,-15.992],[35.8175,-16.0275],[35.704,-16.1025],[35.6615,-16.1],[35.6125,-16.1245],[35.5025,-16.1555],[35.4815,-16.1235],[35.4055,-16.125],[35.3135,-16.2075],[35.2785,-16.326],[35.303,-16.3455],[35.263,-16.399],[35.262,-16.475],[35.213,-16.5],[35.146,-16.557],[35.167,-16.6195],[35.2755,-16.706],[35.3145,-16.831],[35.2635,-16.939],[35.2965,-16.9645],[35.2955,-17.128],[35.0975,-17.1295],[35.0885,-17.0785],[35.0565,-17.0495],[35.063,-16.994],[35.1355,-16.9715],[35.1675,-16.9215],[35.1575,-16.84],[35.113,-16.818],[35.0635,-16.8355],[34.9905,-16.7895],[34.9845,-16.749],[34.919,-16.746],[34.9095,-16.6935],[34.8585,-16.676],[34.8435,-16.6085],[34.7875,-16.5855],[34.775,-16.544],[34.7215,-16.497],[34.6895,-16.505],[34.6525,-16.453],[34.594,-16.4175],[34.5755,-16.3265],[34.52,-16.2805],[34.489,-16.2955],[34.4195,-16.25],[34.3975,-16.1855],[34.4295,-16.0445],[34.389,-16.027],[34.355,-15.9695],[34.259,-15.906],[34.2505,-15.8425],[34.2995,-15.761],[34.3645,-15.7425],[34.388,-15.6925],[34.437,-15.6595],[34.455,-15.611],[34.4255,-15.476],[34.521,-15.3845],[34.519,-15.3535],[34.5985,-15.287],[34.6035,-15.238],[34.578,-15.2005],[34.573,-15.0675],[34.618,-15.015],[34.58,-14.908],[34.5835,-14.809],[34.5205,-14.687],[34.551,-14.649],[34.5155,-14.5545],[34.4755,-14.5325],[34.391,-14.395],[34.301,-14.3985],[34.239,-14.425],[34.085,-14.4585],[34.087,-14.4895],[33.9155,-14.4775],[33.799,-14.5515],[33.7405,-14.501],[33.7165,-14.5015],[33.708,-14.578],[33.6795,-14.616],[33.634,-14.587],[33.6075,-14.4915],[33.566,-14.4815],[33.545,-14.4345],[33.512,-14.4235],[33.4495,-14.365],[33.445,-14.328],[33.3815,-14.2305],[33.3415,-14.2165],[33.2975,-14.146],[33.322,-14.0845],[33.3,-14.0315],[33.2165,-13.9995],[32.6105,-14.2145],[32.4515,-14.2825],[32.2415,-14.3235],[32.0565,-14.3885],[31.9295,-14.42],[31.8265,-14.473],[31.6835,-14.5065],[31.491,-14.612],[31.19,-14.6795],[31.079,-14.7175],[30.799,-14.7835],[30.5175,-14.888],[30.365,-14.9595],[30.2165,-14.9975],[30.234,-15.0355],[30.213,-15.0835],[30.257,-15.164],[30.283,-15.2645],[30.361,-15.3295],[30.383,-15.387],[30.377,-15.437],[30.4015,-15.4765],[30.3665,-15.5405],[30.421,-15.621],[30.425,-15.628],[30.4245,-16.0],[30.9225,-16.0],[30.982,-16.059],[31.0325,-16.0205],[31.1405,-15.9835],[31.322,-16.0255],[31.412,-16.144],[31.5645,-16.186],[31.6925,-16.197],[31.771,-16.2365],[31.8305,-16.3085],[31.894,-16.3365],[31.9135,-16.413],[32.0375,-16.4425],[32.2855,-16.4345],[32.412,-16.4695],[32.6255,-16.5595],[32.7125,-16.603],[32.7025,-16.677],[32.761,-16.7055],[32.838,-16.6965],[32.888,-16.719],[32.9215,-16.6975],[32.9785,-16.707],[32.9665,-16.7725],[32.914,-16.889],[32.8385,-16.932],[32.938,-17.0685],[32.9865,-17.1795],[33.0005,-17.314],[33.0495,-17.3355],[33.0065,-17.402],[33.0065,-17.4355],[32.9635,-17.4945],[32.9975,-17.5685],[33.042,-17.5875],[33.046,-17.637],[33.0155,-17.6885],[33.006,-17.7425],[33.0185,-17.786],[32.9725,-17.8005],[32.951,-17.882],[32.9735,-17.9385],[32.941,-18.011],[33.003,-18.201],[33.0215,-18.2285],[33.0255,-18.3105],[33.0675,-18.3485],[33.0185,-18.421],[33.0205,-18.4975],[32.9085,-18.5],[32.8845,-18.5225],[32.886,-18.579],[32.954,-18.69],[32.928,-18.7665],[32.8775,-18.797],[32.827,-18.7775],[32.734,-18.818],[32.7045,-18.857],[32.7305,-18.9225],[32.699,-18.939],[32.7135,-19.025],[32.8335,-19.0165],[32.8775,-19.098],[32.849,-19.2335],[32.85,-19.2935],[32.7765,-19.3615],[32.784,-19.47],[32.8455,-19.4835],[32.8585,-19.5465],[32.857,-19.6185],[32.8345,-19.6735],[32.892,-19.684],[32.9755,-19.6515],[32.9585,-19.723],[33.063,-19.777],[33.0245,-19.966],[33.029,-20.035],[32.943,-20.0425],[32.9235,-20.1265],[32.8615,-20.1365],[32.8755,-20.1875],[32.864,-20.287],[32.808,-20.339],[32.668,-20.556],[32.567,-20.561],[32.514,-20.5985],[32.4835,-20.657],[32.4945,-20.701],[32.498,-20.8075],[32.517,-20.9145],[32.481,-20.9925],[32.388,-21.0985],[32.3665,-21.1365],[32.4,-21.163],[32.3995,-21.2065],[32.476,-21.3115],[32.4055,-21.316],[32.2775,-21.459],[31.858,-21.8905],[31.3765,-22.3815],[31.3075,-22.424],[31.4755,-22.9255],[31.5635,-23.198],[31.5505,-23.4105],[31.561,-23.482],[31.6625,-23.586],[31.69,-23.6255],[31.6975,-23.722],[31.768,-23.885],[31.8805,-23.9525],[31.908,-24.182],[31.987,-24.302],[32.009,-24.4555],[32.009,-24.5755],[31.998,-24.6895],[32.0335,-25.1325],[32.016,-25.3775],[31.9795,-25.457],[31.996,-25.5265],[32.006,-25.645],[31.9785,-25.6935],[31.9305,-25.841],[31.975,-25.9525]],[[34.5725,-12.014],[34.605,-11.9745],[34.64,-11.982],[34.6625,-12.041],[34.6195,-12.085],[34.5845,-12.069],[34.5725,-12.014]],[[34.675,-12.0845],[34.706,-12.008],[34.742,-11.9995],[34.794,-12.0425],[34.7825,-12.0815],[34.7065,-12.1365],[34.675,-12.0845]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d0/Flag_of_Mozambique.svg","name:en":"Mozambique","wikidata":"Q1029","ISO3166-1:alpha2":"MZ","ISO3166-1:alpha3":"MOZ","ISO3166-1:numeric":"508"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[30.473,-1.0575],[30.359,-1.0605],[30.307,-1.145],[30.2925,-1.1885],[30.214,-1.269],[30.1695,-1.2745],[30.17,-1.345],[30.014,-1.416],[29.997,-1.446],[29.916,-1.4825],[29.8855,-1.4235],[29.882,-1.357],[29.823,-1.309],[29.777,-1.3665],[29.7115,-1.345],[29.6765,-1.3835],[29.591,-1.3875],[29.526,-1.4115],[29.4505,-1.506],[29.362,-1.509],[29.3105,-1.5995],[29.2685,-1.6295],[29.245,-1.698],[29.156,-1.82],[29.136,-1.861],[29.129,-1.9415],[29.156,-2.0175],[29.1735,-2.129],[29.1155,-2.256],[29.092,-2.2765],[28.9985,-2.2975],[28.948,-2.3765],[28.909,-2.3725],[28.8665,-2.4365],[28.892,-2.476],[28.8625,-2.5325],[28.891,-2.5575],[28.918,-2.681],[29.004,-2.7045],[29.0405,-2.7435],[29.055,-2.7125],[29.0625,-2.6015],[29.1495,-2.5915],[29.2225,-2.632],[29.264,-2.6185],[29.328,-2.653],[29.3505,-2.7005],[29.334,-2.747],[29.3845,-2.821],[29.4385,-2.7975],[29.487,-2.8025],[29.543,-2.8295],[29.6585,-2.7875],[29.708,-2.8195],[29.7455,-2.807],[29.7695,-2.767],[29.8115,-2.771],[29.887,-2.7495],[29.918,-2.7025],[29.9355,-2.642],[29.9205,-2.5565],[29.9675,-2.446],[29.944,-2.373],[29.9595,-2.3275],[30.0465,-2.361],[30.061,-2.39],[30.126,-2.437],[30.185,-2.427],[30.232,-2.3505],[30.2925,-2.3735],[30.3735,-2.3525],[30.4085,-2.31],[30.459,-2.3185],[30.4715,-2.367],[30.5435,-2.413],[30.61,-2.3995],[30.657,-2.4135],[30.717,-2.3555],[30.7765,-2.389],[30.8525,-2.321],[30.8435,-2.2055],[30.899,-2.0775],[30.808,-1.938],[30.832,-1.8145],[30.8215,-1.693],[30.84,-1.651],[30.745,-1.5235],[30.739,-1.454],[30.6825,-1.397],[30.6295,-1.385],[30.6125,-1.35],[30.5685,-1.337],[30.5515,-1.252],[30.5095,-1.172],[30.471,-1.157],[30.453,-1.081],[30.473,-1.0575]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/17/Flag_of_Rwanda.svg","name:en":"Rwanda","wikidata":"Q1037","ISO3166-1:alpha2":"RW","ISO3166-1:alpha3":"RWA","ISO3166-1:numeric":"646"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-63.051,17.279],[-63.0075,17.22],[-62.8975,17.132],[-62.8405,17.106],[-62.81,17.016],[-62.754,16.951],[-62.645,16.899],[-62.5535,16.8995],[-62.3305,17.147],[-62.3425,17.23],[-62.402,17.33],[-62.5135,17.419],[-62.6035,17.528],[-62.7245,17.6],[-62.7865,17.616],[-63.051,17.279]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fe/Flag_of_Saint_Kitts_and_Nevis.svg","name:en":"Saint Kitts and Nevis","wikidata":"Q763","ISO3166-1:alpha2":"KN","ISO3166-1:alpha3":"KNA","ISO3166-1:numeric":"659"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[166.71,-0.534],[166.728,-0.62],[166.784,-0.697],[166.847,-0.737],[166.956,-0.752],[167.026,-0.732],[167.0805,-0.697],[167.1355,-0.621],[167.157,-0.551],[167.148,-0.449],[167.1045,-0.375],[167.039,-0.325],[166.9675,-0.304],[166.894,-0.307],[166.811,-0.345],[166.7635,-0.39],[166.723,-0.458],[166.71,-0.534]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/30/Flag_of_Nauru.svg","name:en":"Nauru","wikidata":"Q697","ISO3166-1:alpha2":"NR","ISO3166-1:alpha3":"NRU","ISO3166-1:numeric":"520"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[42.968,-22.084],[42.9715,-22.118],[43.04,-22.4805],[43.1395,-22.864],[43.1545,-22.9025],[43.4175,-23.415],[43.448,-24.3395],[43.4705,-24.423],[43.8365,-25.09],[44.037,-25.35],[44.1305,-25.418],[45.048,-25.7685],[45.133,-25.784],[45.517,-25.784],[45.592,-25.772],[46.7935,-25.3715],[47.203,-25.1775],[47.2565,-25.141],[47.3005,-25.083],[47.4165,-24.8495],[47.432,-24.8075],[47.433,-24.733],[47.4935,-24.5935],[47.525,-24.458],[47.5815,-24.3305],[47.6195,-24.2155],[47.6705,-24.138],[47.802,-23.8415],[47.816,-23.737],[47.86,-23.5235],[47.921,-23.381],[47.964,-23.225],[47.9805,-23.123],[48.016,-23.031],[48.0665,-22.7975],[48.076,-22.6845],[48.105,-22.529],[48.1625,-22.3625],[48.237,-22.183],[48.3305,-21.9745],[48.4285,-21.716],[48.522,-21.4405],[48.558,-21.284],[48.644,-21.0625],[48.713,-20.743],[48.808,-20.456],[48.882,-20.2845],[48.9875,-20.0705],[49.0325,-19.9465],[49.028,-19.8555],[49.071,-19.6665],[49.187,-19.3935],[49.197,-19.3065],[49.228,-19.209],[49.29,-19.075],[49.3865,-18.8265],[49.423,-18.718],[49.4955,-18.5465],[49.5335,-18.4825],[49.5495,-18.38],[49.573,-18.3095],[49.616,-18.2545],[49.669,-18.0895],[49.669,-18.0135],[49.653,-17.965],[49.721,-17.7715],[50.22,-16.7875],[50.2405,-16.683],[50.2305,-16.6365],[50.1155,-16.3045],[50.3415,-16.1705],[50.4195,-16.087],[50.529,-15.864],[50.6455,-15.531],[50.6555,-15.4825],[50.672,-15.2825],[50.6675,-15.2215],[50.216,-13.3055],[50.0455,-12.743],[50.0185,-12.687],[49.4185,-11.821],[49.3445,-11.756],[49.242,-11.733],[49.15,-11.759],[48.55,-12.092],[48.4955,-12.136],[48.467,-12.178],[48.446,-12.2735],[48.4595,-12.671],[47.687,-13.4425],[47.6345,-13.5335],[47.399,-14.425],[46.3585,-15.3405],[44.3875,-16.0085],[43.91,-16.14],[43.8445,-16.1705],[43.8,-16.213],[43.7615,-16.3005],[43.56,-17.467],[43.5575,-17.496],[43.54,-18.3125],[43.5465,-18.367],[43.6085,-18.5835],[43.6595,-18.6495],[44.0145,-18.952],[44.009,-19.0875],[44.1475,-19.785],[43.931,-20.3745],[43.5985,-20.692],[43.5585,-20.7445],[43.0745,-21.6605],[43.0575,-21.7045],[42.9735,-22.0375],[42.968,-22.084]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/bc/Flag_of_Madagascar.svg","name:en":"Madagascar","wikidata":"Q1019","ISO3166-1:alpha2":"MG","ISO3166-1:alpha3":"MDG","ISO3166-1:numeric":"450"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[46.5525,29.1005],[46.841,29.068],[47.465,29.0],[47.5845,28.8355],[47.598,28.7565],[47.59,28.716],[47.6055,28.655],[47.709,28.543],[47.7045,28.5245],[48.4305,28.5345],[48.5895,28.639],[48.6695,28.6725],[48.6885,28.697],[49.0035,28.799],[48.9795,28.9095],[48.932,28.965],[48.833,29.013],[48.7125,29.0105],[48.7235,29.0755],[48.7075,29.146],[48.662,29.21],[48.612,29.244],[48.6635,29.326],[48.671,29.3965],[48.623,29.5025],[48.5395,29.5715],[48.5815,29.638],[48.5235,29.707],[48.464,29.757],[48.3835,29.8045],[48.318,29.881],[48.1515,30.017],[48.065,30.021],[48.023,29.9785],[47.7075,30.1035],[47.371,30.1035],[47.2875,30.0585],[47.1895,30.0285],[47.1395,29.987],[47.0815,29.8515],[47.007,29.722],[46.998,29.6635],[46.9045,29.525],[46.867,29.437],[46.7845,29.3555],[46.7675,29.322],[46.6605,29.196],[46.5525,29.1005]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/a/aa/Flag_of_Kuwait.svg","name:en":"Kuwait","wikidata":"Q817","ISO3166-1:alpha2":"KW","ISO3166-1:alpha3":"KWT","ISO3166-1:numeric":"414"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[56.3825,-10.347],[56.391,-10.407],[56.4425,-10.507],[56.5305,-10.62],[56.5995,-10.671],[56.6835,-10.69],[56.763,-10.677],[56.8195,-10.647],[56.8715,-10.593],[56.904,-10.515],[56.899,-10.417],[56.872,-10.358],[56.797,-10.286],[56.765,-10.229],[56.701,-10.169],[56.6275,-10.141],[56.552,-10.141],[56.452,-10.189],[56.4045,-10.251],[56.3825,-10.347]]],[[[57.095,-20.466],[57.118,-20.557],[57.1925,-20.635],[57.335,-20.697],[57.395,-20.711],[57.5525,-20.724],[57.642,-20.706],[57.7385,-20.672],[57.8165,-20.63],[57.917,-20.548],[57.9855,-20.458],[58.023,-20.29],[58.021,-20.208],[58.004,-20.15],[57.9325,-20.008],[57.9825,-19.949],[58.0175,-19.857],[58.016,-19.773],[57.963,-19.679],[57.917,-19.644],[57.851,-19.62],[57.7835,-19.617],[57.669,-19.662],[57.5825,-19.675],[57.525,-19.706],[57.4595,-19.784],[57.4345,-19.834],[57.381,-19.882],[57.3115,-20.009],[57.248,-20.062],[57.188,-20.155],[57.161,-20.222],[57.15,-20.315],[57.1,-20.42],[57.095,-20.466]]],[[[59.2615,-16.8165],[59.2725,-16.8895],[59.3075,-16.9515],[59.361,-16.999],[59.428,-17.0275],[59.556,-17.0275],[59.659,-16.989],[59.732,-16.938],[59.8215,-16.839],[59.883,-16.7525],[59.917,-16.64],[59.9235,-16.477],[59.8915,-16.387],[59.8455,-16.3345],[59.803,-16.2565],[59.7975,-16.1915],[59.7495,-16.1045],[59.695,-16.062],[59.629,-16.039],[59.556,-16.0395],[59.465,-16.0765],[59.416,-16.1255],[59.385,-16.1945],[59.389,-16.299],[59.412,-16.3465],[59.3265,-16.438],[59.299,-16.503],[59.296,-16.573],[59.309,-16.698],[59.279,-16.745],[59.2615,-16.8165]]],[[[63.086,-19.722],[63.0955,-19.784],[63.142,-19.863],[63.2495,-19.945],[63.3105,-19.968],[63.431,-19.985],[63.5365,-19.96],[63.595,-19.915],[63.6825,-19.822],[63.713,-19.741],[63.7005,-19.617],[63.651,-19.543],[63.6075,-19.511],[63.5325,-19.48],[63.444,-19.467],[63.2,-19.526],[63.157,-19.557],[63.1115,-19.62],[63.086,-19.722]]]]},"properties":{"flag":"https://commons.wikimedia.org/wiki/File:Flag_of_Mauritius.svg","name:en":"Mauritius","wikidata":"Q1027","ISO3166-1:alpha2":"MU","ISO3166-1:alpha3":"MUS","ISO3166-1:numeric":"480"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[45.999,-9.401],[46.0055,-9.4565],[46.0455,-9.569],[46.0825,-9.6225],[46.345,-9.894],[46.3985,-9.9335],[46.4615,-9.954],[46.547,-9.953],[46.6135,-9.9275],[46.695,-9.842],[46.7245,-9.7665],[46.73,-9.7095],[46.7235,-9.3715],[46.6945,-9.294],[46.642,-9.2365],[46.568,-9.1975],[46.476,-9.1715],[46.392,-9.1635],[46.2075,-9.17],[46.123,-9.195],[46.066,-9.2355],[46.0055,-9.3385],[45.999,-9.401]]],[[[47.3005,-9.72],[47.31,-9.788],[47.3345,-9.8405],[47.5555,-10.2025],[47.6065,-10.2585],[47.6735,-10.2955],[47.753,-10.306],[47.8255,-10.29],[47.9175,-10.219],[47.9635,-10.108],[47.956,-10.018],[47.857,-9.6625],[47.8265,-9.5995],[47.7565,-9.5265],[47.6815,-9.4825],[47.597,-9.456],[47.5225,-9.462],[47.452,-9.496],[47.3865,-9.546],[47.338,-9.598],[47.316,-9.6435],[47.3005,-9.72]]],[[[50.519,-9.3165],[50.5305,-9.403],[50.604,-9.623],[50.6475,-9.7255],[50.8305,-10.2325],[50.8715,-10.339],[50.915,-10.3985],[50.987,-10.448],[51.0555,-10.465],[51.159,-10.445],[51.276,-10.377],[51.3245,-10.325],[51.359,-10.2675],[51.391,-10.184],[51.3965,-10.115],[51.332,-9.362],[51.2815,-9.2105],[51.1805,-9.07],[51.1215,-9.0255],[51.055,-9.005],[50.952,-9.018],[50.6515,-9.139],[50.6075,-9.162],[50.5545,-9.2155],[50.519,-9.3165]]],[[[52.5185,-7.038],[52.536,-7.2235],[52.5665,-7.286],[52.616,-7.334],[52.679,-7.363],[52.7545,-7.372],[52.879,-7.328],[52.925,-7.287],[52.9665,-7.2165],[52.9795,-7.1455],[52.966,-6.9855],[52.9445,-6.9195],[52.8735,-6.8435],[52.7525,-6.7955],[52.6805,-6.7975],[52.6135,-6.825],[52.5605,-6.874],[52.5285,-6.939],[52.5185,-7.038]]],[[[52.631,-6.099],[52.646,-6.167],[52.6835,-6.2255],[52.739,-6.268],[52.8465,-6.2895],[52.8685,-6.343],[52.913,-6.3935],[52.971,-6.4265],[53.046,-6.439],[53.125,-6.422],[53.179,-6.386],[53.237,-6.3665],[53.292,-6.3235],[53.3325,-6.2605],[53.3485,-6.1935],[53.3425,-6.122],[53.3115,-6.057],[53.217,-5.965],[53.3125,-5.987],[53.421,-5.953],[53.4725,-5.9065],[53.504,-5.848],[53.5435,-5.8805],[53.6085,-5.9055],[53.7235,-5.895],[53.811,-5.8485],[53.863,-5.7955],[53.898,-5.698],[53.892,-5.629],[53.863,-5.566],[53.807,-5.511],[53.704,-5.4745],[53.625,-5.4845],[53.559,-5.52],[53.5705,-5.4445],[53.546,-5.3455],[53.4795,-5.2635],[53.5235,-5.214],[53.5485,-5.1605],[53.5625,-5.069],[53.5535,-5.022],[53.5785,-4.974],[53.593,-4.884],[53.564,-4.775],[53.519,-4.7215],[53.452,-4.6835],[53.3835,-4.6715],[53.315,-4.684],[53.252,-4.7215],[53.2105,-4.7705],[53.1745,-4.8605],[53.1755,-4.9665],[53.1195,-5.0495],[53.1095,-5.1515],[53.1325,-5.217],[53.164,-5.259],[53.124,-5.3025],[53.093,-5.3735],[53.089,-5.443],[53.1015,-5.4915],[53.069,-5.526],[53.035,-5.597],[53.0215,-5.684],[52.9535,-5.6975],[52.894,-5.734],[52.8505,-5.7885],[52.826,-5.8885],[52.772,-5.897],[52.713,-5.927],[52.6695,-5.9705],[52.64,-6.0295],[52.631,-6.099]]],[[[55.0015,-3.725],[55.0225,-3.815],[55.0595,-3.867],[55.1055,-3.902],[55.1625,-3.923],[55.2555,-3.92],[55.322,-3.889],[55.3605,-3.854],[55.3975,-3.792],[55.41,-3.738],[55.4,-3.654],[55.338,-3.561],[55.299,-3.535],[55.226,-3.513],[55.1705,-3.515],[55.1025,-3.54],[55.057,-3.576],[55.013,-3.649],[55.0015,-3.725]]],[[[55.0055,-4.4875],[55.035,-4.5905],[55.0765,-4.6465],[55.2115,-4.7975],[55.3665,-4.9465],[55.4445,-4.9935],[55.5235,-5.008],[55.586,-5.0005],[55.6745,-4.9525],[55.845,-4.793],[55.9315,-4.803],[56.0185,-4.781],[56.1085,-4.717],[56.151,-4.6205],[56.1495,-4.55],[56.0965,-4.449],[56.124,-4.3615],[56.117,-4.283],[56.0705,-4.1995],[56.005,-4.1365],[55.9355,-4.0935],[55.7375,-4.0215],[55.8115,-3.957],[55.8525,-3.9005],[55.8725,-3.8315],[55.8685,-3.7545],[55.8165,-3.659],[55.7135,-3.602],[55.6325,-3.6005],[55.5255,-3.652],[55.4845,-3.704],[55.459,-3.7995],[55.472,-3.8805],[55.507,-3.943],[55.5945,-4.0205],[55.561,-4.0355],[55.293,-4.1895],[55.2395,-4.1815],[55.171,-4.194],[55.111,-4.229],[55.051,-4.3145],[55.016,-4.4125],[55.0055,-4.4875]]],[[[55.148,-5.851],[55.1795,-5.9505],[55.2355,-6.016],[55.2935,-6.0595],[55.3995,-6.08],[55.4745,-6.0605],[55.529,-6.024],[55.569,-5.972],[55.5895,-5.9095],[55.588,-5.805],[55.5595,-5.734],[55.4915,-5.6525],[55.4295,-5.6205],[55.328,-5.6165],[55.264,-5.643],[55.19,-5.7195],[55.1575,-5.7815],[55.148,-5.851]]],[[[56.035,-7.2065],[56.0505,-7.276],[56.1155,-7.3595],[56.179,-7.3915],[56.28,-7.3955],[56.3765,-7.349],[56.4715,-7.227],[56.498,-7.1285],[56.485,-7.0235],[56.4515,-6.9655],[56.401,-6.9215],[56.3385,-6.8965],[56.2715,-6.8935],[56.1955,-6.9175],[56.136,-6.9635],[56.1005,-7.0185],[56.045,-7.136],[56.035,-7.2065]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fc/Flag_of_Seychelles.svg","name:en":"Seychelles","wikidata":"Q1042","ISO3166-1:alpha2":"SC","ISO3166-1:alpha3":"SYC","ISO3166-1:numeric":"690"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[101.0395,2.8305],[100.6605,3.003],[100.329,3.3005],[99.919,3.669],[99.6225,3.935],[99.595,3.9525],[99.1885,4.1705],[98.873,4.339],[98.4185,4.5825],[98.07,5.018],[98.0465,5.043],[97.681,5.366],[97.602,5.4195],[97.502,5.451],[96.8565,5.4745],[95.9735,5.6875],[95.4605,6.0525],[95.248,6.2285],[95.188,6.2625],[95.1125,6.2745],[95.0065,6.2445],[94.9325,6.1635],[94.7925,5.881],[94.7715,5.797],[94.789,5.712],[95.017,5.1985],[95.1625,4.836],[95.1845,2.98],[95.208,2.886],[95.265,2.81],[95.7835,2.381],[96.7205,1.4585],[96.868,1.313],[96.8815,1.193],[96.9085,1.112],[97.187,0.6395],[97.6635,-0.1695],[97.691,-0.208],[98.2365,-1.0335],[98.4335,-1.332],[98.706,-1.782],[98.74,-1.824],[98.9355,-2.0075],[99.8445,-2.9645],[100.1765,-3.4245],[100.331,-3.5845],[100.879,-4.1515],[101.927,-5.4825],[102.1085,-5.6545],[102.224,-5.715],[102.388,-5.71],[103.507,-5.5135],[103.8335,-5.4565],[104.2855,-6.022],[104.6515,-6.455],[105.085,-6.968],[105.1535,-7.023],[105.4445,-7.2005],[106.051,-7.4565],[106.3555,-7.5825],[107.8015,-7.9375],[108.2725,-8.015],[108.437,-8.0205],[108.8295,-7.989],[109.401,-7.9775],[109.902,-8.133],[110.3725,-8.2895],[110.6385,-8.3875],[110.8815,-8.441],[111.4725,-8.5665],[111.6955,-8.605],[113.2645,-8.706],[114.4925,-8.9835],[115.0935,-9.047],[115.1915,-9.05],[115.57,-9.0205],[115.959,-9.1165],[116.9755,-9.306],[117.0865,-9.3015],[118.371,-9.106],[118.757,-9.6735],[118.912,-9.8365],[118.951,-9.8675],[119.9925,-10.5045],[120.1025,-10.538],[120.401,-10.52],[121.1905,-11.0135],[121.2875,-11.033],[121.3545,-11.0195],[121.838,-10.84],[122.8055,-11.1965],[122.89,-11.2085],[122.972,-11.186],[123.3205,-11.004],[124.4975,-10.344],[124.604,-10.2815],[124.8905,-10.0265],[125.1595,-9.755],[125.2565,-9.575],[125.24,-9.5645],[125.087,-9.463],[125.0795,-9.3925],[125.0065,-9.295],[124.9825,-9.2395],[124.983,-9.191],[125.014,-9.171],[125.0745,-9.1765],[125.098,-9.2005],[125.176,-9.1725],[125.1715,-9.126],[125.1855,-9.0265],[125.1135,-8.9655],[125.0895,-9.011],[124.9845,-9.066],[124.941,-9.047],[124.9345,-8.995],[124.9525,-8.9665],[124.936,-8.7415],[125.098,-8.519],[125.24,-8.444],[125.3215,-8.2915],[125.34,-8.2325],[125.6725,-8.0895],[125.696,-8.101],[125.8275,-8.251],[126.4595,-8.176],[126.7625,-8.232],[126.7995,-8.2285],[127.189,-8.2235],[127.397,-8.2785],[127.5335,-8.419],[127.6195,-8.436],[128.4885,-8.549],[128.534,-8.5495],[129.822,-8.424],[130.749,-8.5475],[130.8565,-8.5405],[130.985,-8.4815],[131.232,-8.339],[131.429,-8.2045],[131.8615,-7.8095],[131.898,-7.7655],[132.1485,-7.3485],[132.8945,-6.285],[134.0905,-7.121],[134.3985,-7.2755],[134.539,-7.3025],[134.6085,-7.283],[134.7865,-7.1985],[134.869,-7.1095],[134.975,-6.9035],[135.031,-6.696],[135.1095,-6.3705],[135.115,-6.3195],[135.107,-5.979],[135.0415,-5.679],[135.2285,-5.037],[136.1485,-4.872],[136.6765,-5.0885],[137.5745,-5.538],[137.882,-6.0775],[137.9075,-6.1125],[138.1695,-6.402],[137.6605,-7.753],[137.495,-8.1625],[137.4595,-8.4185],[137.5065,-8.577],[137.6595,-8.6435],[138.908,-8.634],[138.967,-8.6245],[139.4115,-8.4855],[140.7165,-9.247],[140.7785,-9.3255],[140.8665,-9.3835],[140.915,-9.3045],[141.0195,-9.1355],[141.0195,-8.4525],[141.0195,-8.0455],[141.0195,-7.415],[141.02,-6.8925],[140.9405,-6.892],[140.871,-6.7945],[140.9005,-6.7565],[140.847,-6.7215],[140.886,-6.605],[140.956,-6.491],[140.943,-6.3735],[140.9605,-6.3335],[141.0,-6.3225],[141.0,-5.2945],[141.001,-4.987],[141.0,-4.5345],[141.0,-3.9315],[141.0045,-3.238],[141.001,-2.9005],[141.003,-2.6815],[141.0,-2.3875],[140.663,-2.2465],[140.244,-2.143],[138.808,-1.3965],[138.7455,-1.3765],[137.97,-1.2605],[136.4715,-0.8855],[135.9705,-0.535],[135.9485,-0.522],[135.4095,-0.2395],[135.1785,0.0895],[134.505,1.0475],[134.4465,1.102],[134.3255,1.137],[134.2425,1.132],[134.1685,1.0935],[133.522,0.5605],[132.9785,0.113],[132.651,-0.1575],[132.4305,-0.143],[132.2715,-0.14],[131.814,0.5955],[131.45,1.18],[131.3745,1.2525],[131.3105,1.2815],[131.24,1.2875],[131.172,1.2685],[131.1155,1.227],[130.656,0.746],[129.64,0.8685],[129.275,0.9125],[128.935,1.628],[128.8985,2.4365],[128.8725,2.5265],[128.7475,2.7445],[128.719,2.7825],[128.418,2.9015],[127.8055,3.4465],[127.093,3.8355],[127.353,4.5595],[127.3625,4.6655],[127.341,4.8055],[127.3065,4.885],[126.7555,5.679],[126.697,5.7355],[126.62,5.7625],[126.532,5.76],[126.4615,5.726],[125.37,4.905],[125.3345,4.8775],[125.269,4.7915],[125.2345,4.7205],[125.118,4.256],[124.9625,2.789],[124.554,1.8575],[124.2285,1.348],[123.1165,1.2485],[122.4605,1.3355],[122.2955,1.3645],[121.4725,1.511],[120.9085,1.5765],[120.822,1.5675],[120.691,1.51],[120.2205,1.234],[119.668,1.5835],[119.175,1.9315],[118.823,2.3515],[118.765,2.5325],[118.265,4.097],[117.9585,4.1905],[117.899,4.166],[117.642,4.1655],[117.5625,4.179],[117.5295,4.1615],[117.4415,4.1935],[117.426,4.233],[117.342,4.291],[117.289,4.316],[117.2425,4.374],[117.1965,4.336],[117.1515,4.354],[117.1055,4.3335],[117.044,4.3465],[116.9735,4.345],[116.899,4.367],[116.84,4.327],[116.782,4.3575],[116.6195,4.336],[116.612,4.3765],[116.536,4.3755],[116.5355,4.323],[116.5025,4.3245],[116.441,4.2885],[116.4325,4.3255],[116.3485,4.3915],[116.2595,4.364],[116.1805,4.3825],[116.162,4.342],[116.1195,4.3385],[116.079,4.2765],[116.039,4.295],[116.006,4.348],[115.929,4.355],[115.878,4.391],[115.864,4.3225],[115.8295,4.241],[115.766,4.2505],[115.731,4.1945],[115.7075,4.1995],[115.6755,4.127],[115.6775,4.0905],[115.65,3.9885],[115.616,3.938],[115.581,3.9415],[115.582,3.888],[115.6185,3.8425],[115.5795,3.7475],[115.5765,3.6095],[115.6145,3.5445],[115.6345,3.4555],[115.574,3.422],[115.538,3.362],[115.516,3.261],[115.525,3.188],[115.564,3.171],[115.514,3.093],[115.5195,3.0585],[115.4835,3.0195],[115.429,3.017],[115.3975,2.979],[115.335,2.9735],[115.2855,3.0495],[115.249,2.9665],[115.2095,2.9555],[115.15,2.909],[115.151,2.872],[115.114,2.8335],[115.1425,2.8025],[115.141,2.7445],[115.093,2.694],[115.1165,2.647],[115.088,2.603],[115.128,2.5825],[115.172,2.6055],[115.2375,2.506],[115.1935,2.4715],[115.1395,2.4775],[115.095,2.411],[115.047,2.408],[115.0215,2.368],[114.9505,2.3515],[114.9485,2.3115],[114.906,2.257],[114.864,2.272],[114.7995,2.2495],[114.78,2.1445],[114.8105,2.1005],[114.792,2.06],[114.8065,2.0245],[114.8565,2.0455],[114.8805,2.0175],[114.8485,1.972],[114.8785,1.9145],[114.815,1.889],[114.786,1.8485],[114.7435,1.8695],[114.6945,1.8105],[114.718,1.728],[114.711,1.6715],[114.649,1.5895],[114.615,1.575],[114.611,1.501],[114.586,1.4465],[114.5265,1.442],[114.416,1.511],[114.2495,1.451],[114.213,1.4105],[114.1425,1.466],[113.9745,1.45],[113.8675,1.3875],[113.8145,1.3665],[113.8295,1.3345],[113.8035,1.3005],[113.7035,1.2655],[113.6645,1.2245],[113.5965,1.264],[113.5745,1.3065],[113.5375,1.3225],[113.4275,1.286],[113.37,1.3235],[113.356,1.357],[113.241,1.3925],[113.183,1.377],[113.1335,1.4015],[113.097,1.443],[113.015,1.405],[112.986,1.4525],[113.0315,1.4915],[113.064,1.5525],[112.9895,1.579],[112.9335,1.5675],[112.8845,1.5865],[112.812,1.5405],[112.7745,1.564],[112.677,1.5545],[112.6575,1.5715],[112.544,1.574],[112.501,1.5835],[112.445,1.5395],[112.2875,1.4735],[112.212,1.4495],[112.2325,1.379],[112.2025,1.314],[112.181,1.306],[112.143,1.135],[112.057,1.1355],[111.9355,1.122],[111.887,1.0785],[111.8595,1.004],[111.818,0.9865],[111.772,1.016],[111.7175,1.007],[111.6675,1.043],[111.596,1.009],[111.551,0.9565],[111.4885,1.0355],[111.445,1.014],[111.385,1.015],[111.2315,1.086],[111.1385,1.0505],[111.0695,1.0495],[110.998,1.0295],[110.907,1.029],[110.8855,0.978],[110.762,0.9235],[110.6975,0.884],[110.5955,0.8575],[110.4905,0.8765],[110.4055,0.953],[110.3965,0.9975],[110.2985,0.9955],[110.2795,1.0525],[110.2105,1.1185],[110.2125,1.151],[110.165,1.196],[110.097,1.199],[110.064,1.263],[109.9795,1.299],[109.9635,1.403],[109.9305,1.425],[109.8375,1.422],[109.838,1.4755],[109.8015,1.4695],[109.7805,1.509],[109.662,1.6175],[109.6675,1.733],[109.6855,1.782],[109.623,1.8035],[109.5865,1.792],[109.576,1.844],[109.5515,1.859],[109.54,1.9265],[109.56,1.971],[109.6205,1.983],[109.645,2.0815],[109.7125,2.3145],[109.328,2.765],[109.0875,3.131],[108.596,4.103],[108.2005,4.8855],[108.1115,4.974],[107.9875,4.992],[107.9265,4.975],[107.8835,4.947],[107.7625,4.846],[107.5785,4.6585],[107.5525,4.625],[107.2955,4.218],[106.1865,3.633],[105.8805,3.5175],[105.842,3.498],[105.4745,3.2595],[105.4105,3.1925],[105.2065,2.8415],[105.1795,2.7485],[105.2,2.6535],[105.3685,2.3105],[104.924,1.3555],[104.6345,1.417],[104.5735,1.4005],[104.5215,1.357],[104.4725,1.2955],[104.416,1.256],[104.376,1.252],[104.3155,1.2835],[104.189,1.2725],[104.125,1.276],[104.0335,1.2695],[103.805,1.1715],[103.7405,1.1305],[103.6585,1.185],[103.5665,1.1955],[103.433,1.2365],[103.065,1.325],[102.5835,1.6865],[102.2235,1.92],[101.775,2.2565],[101.2015,2.6915],[101.0395,2.8305]],[[124.273,-9.419],[124.218,-9.3665],[124.133,-9.4255],[124.1005,-9.41],[124.0415,-9.34],[124.048,-9.231],[124.155,-9.178],[124.3295,-9.117],[124.433,-9.113],[124.475,-9.1655],[124.462,-9.2995],[124.404,-9.34],[124.3515,-9.426],[124.3415,-9.4825],[124.2775,-9.5025],[124.273,-9.419]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9f/Flag_of_Indonesia.svg","name:en":"Indonesia","wikidata":"Q252","ISO3166-1:alpha2":"ID","ISO3166-1:alpha3":"IDN","ISO3166-1:numeric":"360"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[7.533,43.537],[7.4525,43.742],[7.4185,43.725],[7.5,43.5165],[7.533,43.537]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/ea/Flag_of_Monaco.svg","name:en":"Monaco","wikidata":"Q235","ISO3166-1:alpha2":"MC","ISO3166-1:alpha3":"MCO","ISO3166-1:numeric":"492"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-173.009,-13.5235],[-172.9945,-13.5905],[-172.937,-13.681],[-172.827,-13.7735],[-172.7355,-13.8935],[-172.6595,-13.9635],[-172.606,-13.99],[-172.5315,-14.0035],[-172.3525,-13.9795],[-172.2705,-14.003],[-172.206,-14.04],[-172.038,-14.1675],[-171.9775,-14.1965],[-171.8875,-14.205],[-171.785,-14.244],[-171.6865,-14.2485],[-171.519,-14.2455],[-171.458,-14.271],[-171.3975,-14.277],[-171.327,-14.262],[-171.265,-14.2225],[-171.22,-14.163],[-171.1945,-14.043],[-171.208,-13.937],[-171.2445,-13.879],[-171.319,-13.814],[-171.4875,-13.701],[-171.534,-13.6815],[-171.6285,-13.6635],[-171.7545,-13.6065],[-171.819,-13.5925],[-171.9785,-13.61],[-172.0395,-13.4685],[-172.1505,-13.3285],[-172.2475,-13.2605],[-172.3435,-13.238],[-172.525,-13.265],[-172.6875,-13.301],[-172.763,-13.289],[-172.815,-13.294],[-172.898,-13.3255],[-172.947,-13.3685],[-173.0,-13.4555],[-173.009,-13.5235]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/31/Flag_of_Samoa.svg","name:en":"Samoa","wikidata":"Q683","ISO3166-1:alpha2":"WS","ISO3166-1:alpha3":"WSM","ISO3166-1:numeric":"882"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[72.3555,6.2185],[72.3695,6.066],[72.4585,5.086],[72.499,4.6385],[72.5355,4.2395],[72.503,4.0335],[72.497,3.786],[72.499,3.5975],[72.5345,3.3725],[72.59,2.9835],[72.6235,2.7635],[72.6665,2.54],[72.686,2.3965],[72.716,1.884],[72.7475,1.34],[72.7905,0.626],[72.7865,0.487],[72.804,0.373],[72.8365,-0.0645],[72.877,-0.6075],[72.8965,-0.681],[72.956,-0.795],[72.998,-0.842],[73.061,-0.8855],[73.1285,-0.906],[73.214,-0.9015],[73.286,-0.868],[73.387,-0.7705],[73.612,-0.415],[73.6395,-0.346],[73.7065,-0.004],[73.7605,0.2355],[73.788,0.431],[73.787,1.3855],[73.786,2.1205],[73.8545,2.6485],[73.935,3.191],[73.97,3.467],[73.95,3.782],[73.9225,4.4675],[73.879,5.0915],[73.8585,5.382],[73.821,5.4855],[73.653,6.119],[73.52,6.621],[73.412,7.0215],[73.3865,7.074],[73.2965,7.1515],[72.987,7.2925],[72.897,7.3105],[72.792,7.29],[72.7265,7.2645],[72.645,7.202],[72.6025,7.121],[72.5115,6.8075],[72.3625,6.2925],[72.3555,6.2185]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/0f/Flag_of_Maldives.svg","name:en":"Maldives","wikidata":"Q826","ISO3166-1:alpha2":"MV","ISO3166-1:alpha3":"MDV","ISO3166-1:numeric":"462"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[140.915,-9.3045],[140.9985,-9.387],[141.0755,-9.426],[141.1175,-9.4345],[141.241,-9.426],[141.343,-9.3835],[141.3945,-9.3515],[141.4465,-9.381],[141.593,-9.41],[141.662,-9.41],[141.7805,-9.3875],[141.8115,-9.396],[141.9825,-9.4025],[142.0595,-9.457],[142.0595,-9.261],[142.1435,-9.196],[142.2365,-9.1915],[142.275,-9.23],[142.346,-9.266],[142.4955,-9.366],[142.559,-9.3745],[142.5925,-9.3555],[142.8065,-9.3225],[142.8435,-9.349],[142.851,-9.3915],[142.851,-9.4695],[142.94,-9.3985],[143.039,-9.3415],[143.0845,-9.3025],[143.1985,-9.344],[143.2555,-9.348],[143.324,-9.334],[143.392,-9.2955],[143.442,-9.234],[143.5245,-9.2065],[143.5635,-9.1815],[143.6205,-9.1115],[143.6455,-9.048],[143.6525,-8.9795],[143.6365,-8.9015],[143.6985,-8.888],[143.757,-8.854],[143.797,-8.813],[143.8315,-8.7405],[143.9015,-8.717],[143.9715,-8.6585],[144.0035,-8.605],[144.0205,-8.538],[144.0095,-8.445],[143.981,-8.385],[143.8935,-8.291],[143.9085,-8.241],[143.989,-8.2035],[144.067,-8.135],[144.106,-8.073],[144.123,-7.997],[144.265,-8.0015],[144.3565,-7.9665],[144.489,-8.0055],[144.5915,-7.9895],[144.6475,-7.956],[144.697,-7.8935],[144.7615,-7.9475],[144.849,-7.9805],[144.899,-8.015],[144.99,-8.0425],[145.104,-8.0425],[145.163,-8.061],[145.239,-8.06],[145.307,-8.1215],[145.394,-8.15],[145.5745,-8.141],[145.669,-8.1575],[145.7315,-8.2075],[145.792,-8.2315],[145.9125,-8.2485],[145.9545,-8.311],[146.0235,-8.3825],[146.066,-8.406],[146.0885,-8.499],[146.175,-8.653],[146.24,-8.732],[146.305,-8.7755],[146.3165,-8.8375],[146.3745,-9.0085],[146.4235,-9.1075],[146.512,-9.19],[146.59,-9.2255],[146.6695,-9.2415],[146.6985,-9.3445],[146.682,-9.3945],[146.6845,-9.4715],[146.717,-9.544],[146.782,-9.6025],[146.8735,-9.6285],[146.911,-9.67],[146.982,-9.71],[147.0485,-9.724],[147.132,-9.713],[147.2045,-9.737],[147.253,-9.807],[147.305,-9.8605],[147.321,-9.938],[147.3565,-9.997],[147.4605,-10.113],[147.5295,-10.1645],[147.591,-10.2545],[147.6515,-10.291],[147.7245,-10.305],[147.826,-10.3005],[147.9,-10.35],[147.9685,-10.3635],[148.0695,-10.4245],[148.1385,-10.4305],[148.2175,-10.4075],[148.277,-10.371],[148.341,-10.4],[148.411,-10.4085],[148.571,-10.3895],[148.723,-10.436],[148.7655,-10.4785],[148.8245,-10.5085],[148.886,-10.518],[148.958,-10.506],[149.057,-10.468],[149.0955,-10.4435],[149.1635,-10.466],[149.201,-10.5305],[149.2385,-10.567],[149.3065,-10.601],[149.3815,-10.6075],[149.5,-10.5915],[149.5705,-10.607],[149.632,-10.6],[149.675,-10.664],[149.7785,-10.7455],[150.0395,-10.8595],[150.092,-10.872],[150.138,-10.9055],[150.2215,-10.928],[150.307,-10.9645],[150.3745,-10.972],[150.702,-11.107],[151.0365,-11.281],[151.1755,-11.3405],[151.2565,-11.357],[151.6545,-11.355],[151.7845,-11.375],[151.9295,-11.379],[151.9885,-11.397],[152.087,-11.457],[152.15,-11.463],[152.2275,-11.4995],[152.2925,-11.507],[152.379,-11.492],[153.213,-11.699],[153.247,-11.727],[153.373,-11.7725],[153.4115,-11.809],[153.485,-11.845],[153.597,-11.8515],[153.82,-11.821],[154.441,-11.647],[154.5265,-11.6075],[154.59,-11.5215],[154.6035,-11.4545],[154.592,-11.381],[154.529,-11.291],[154.3425,-11.1395],[154.2865,-11.1065],[153.116,-10.638],[153.046,-10.559],[152.7535,-9.7185],[153.721,-9.502],[153.7865,-9.479],[153.865,-9.4065],[153.896,-9.3335],[153.9005,-9.2915],[153.8815,-9.2035],[153.8045,-9.1065],[153.7465,-9.072],[153.673,-9.057],[153.537,-9.0655],[152.8605,-8.8175],[152.826,-8.808],[152.495,-8.749],[151.988,-8.6085],[151.764,-8.515],[151.1865,-8.212],[150.9465,-8.1525],[150.819,-8.116],[150.7575,-8.126],[150.5725,-8.126],[150.413,-8.1425],[150.233,-8.144],[150.1445,-8.104],[150.07,-8.105],[150.001,-8.1325],[149.2525,-8.738],[148.317,-7.9485],[148.1325,-6.873],[148.61,-6.607],[149.0125,-6.382],[149.0685,-6.3845],[149.129,-6.3685],[149.2525,-6.2925],[149.305,-6.278],[149.31,-6.389],[149.3425,-6.449],[149.393,-6.4945],[149.456,-6.521],[149.5245,-6.5245],[149.6135,-6.492],[149.6795,-6.493],[149.7995,-6.521],[149.9,-6.523],[150.13,-6.506],[150.191,-6.478],[150.2935,-6.5035],[150.3475,-6.5015],[150.4675,-6.4655],[150.5415,-6.46],[150.64,-6.4015],[150.674,-6.35],[150.72,-6.3325],[150.832,-6.3195],[150.888,-6.293],[150.9275,-6.256],[150.9955,-6.234],[151.05,-6.2305],[151.1365,-6.194],[151.1805,-6.1615],[151.24,-6.1505],[151.304,-6.119],[151.3765,-6.0455],[151.4925,-5.9715],[151.57,-5.8765],[151.617,-5.8515],[151.6885,-5.769],[151.81,-5.7885],[151.881,-5.777],[152.071,-5.6855],[152.1135,-5.6485],[152.193,-5.618],[152.879,-5.177],[153.021,-5.086],[153.9125,-5.232],[154.334,-5.3015],[154.3395,-5.353],[154.372,-5.423],[154.399,-5.5315],[154.4045,-5.609],[154.3795,-5.6545],[154.366,-5.7225],[154.377,-5.7925],[154.427,-5.872],[154.534,-5.9315],[154.574,-6.046],[154.6785,-6.169],[154.767,-6.2225],[154.789,-6.2875],[154.858,-6.361],[154.9765,-6.613],[155.0025,-6.6535],[155.2975,-7.0175],[155.336,-7.0975],[155.594,-6.926],[155.6935,-6.926],[155.923,-6.847],[156.0395,-6.6585],[156.036,-6.55],[156.174,-6.445],[156.163,-6.381],[156.136,-6.321],[156.101,-6.281],[156.0435,-6.246],[156.016,-6.188],[155.98,-6.1485],[155.866,-6.078],[155.7845,-6.0125],[155.6845,-5.9495],[155.42,-5.061],[155.4745,-5.059],[155.54,-5.034],[155.622,-4.96],[155.673,-4.847],[155.664,-4.7135],[155.603,-4.5955],[155.531,-4.516],[155.4405,-4.4685],[155.317,-4.4565],[155.2465,-4.4765],[154.866,-3.197],[154.853,-3.1635],[154.7805,-3.084],[154.5465,-2.936],[154.4885,-2.9115],[154.394,-2.911],[153.912,-3.026],[153.387,-3.151],[152.794,-2.62],[152.711,-2.5755],[152.082,-2.412],[150.7875,-1.482],[150.728,-1.453],[150.662,-1.445],[150.0905,-1.472],[149.861,-1.2995],[149.8215,-1.2455],[149.7475,-1.188],[149.64,-1.129],[149.571,-1.115],[149.4675,-1.131],[148.5305,-1.5465],[147.998,-1.782],[147.886,-1.801],[147.658,-1.8565],[147.3705,-1.763],[147.2425,-1.7555],[147.1315,-1.74],[147.0395,-1.738],[146.8745,-1.7055],[146.667,-1.7055],[145.707,-0.733],[145.646,-0.6905],[145.3835,-0.574],[145.2875,-0.5575],[145.224,-0.573],[144.4825,-0.8935],[144.4035,-0.875],[144.3415,-0.8825],[143.5225,-1.1165],[142.983,-1.266],[142.9015,-1.32],[142.8625,-1.3825],[142.8345,-1.51],[142.781,-1.511],[142.714,-1.536],[142.6595,-1.583],[142.6255,-1.647],[142.6155,-1.7185],[142.6415,-1.8285],[142.698,-1.9115],[142.7515,-1.9445],[142.6425,-2.515],[142.3705,-2.917],[142.3395,-2.923],[142.255,-2.898],[142.187,-2.8455],[142.0865,-2.7865],[142.0215,-2.765],[141.954,-2.7625],[141.8985,-2.7285],[141.8385,-2.7105],[141.52,-2.5515],[141.4575,-2.535],[141.3665,-2.4755],[141.2405,-2.434],[141.169,-2.4285],[141.0,-2.3875],[141.003,-2.6815],[141.001,-2.9005],[141.0045,-3.238],[141.0,-3.9315],[141.0,-4.5345],[141.001,-4.987],[141.0,-5.2945],[141.0,-6.3225],[140.9605,-6.3335],[140.943,-6.3735],[140.956,-6.491],[140.886,-6.605],[140.847,-6.7215],[140.9005,-6.7565],[140.871,-6.7945],[140.9405,-6.892],[141.02,-6.8925],[141.0195,-7.415],[141.0195,-8.0455],[141.0195,-8.4525],[141.0195,-9.1355],[140.915,-9.3045]]],[[[156.7155,-4.7585],[156.7205,-4.8005],[156.7585,-4.893],[156.839,-4.9695],[156.935,-4.997],[157.031,-4.9965],[157.113,-4.9685],[157.2015,-4.893],[157.234,-4.8205],[157.235,-4.715],[157.197,-4.621],[157.1445,-4.5635],[157.0405,-4.5045],[156.9965,-4.495],[156.8965,-4.498],[156.802,-4.5455],[156.745,-4.628],[156.7225,-4.689],[156.7155,-4.7585]]],[[[159.1195,-4.557],[159.1505,-4.653],[159.2095,-4.7295],[159.27,-4.771],[159.412,-4.8045],[159.5205,-4.7925],[159.607,-4.744],[159.6565,-4.694],[159.692,-4.6015],[159.686,-4.5235],[159.647,-4.4225],[159.6025,-4.3645],[159.498,-4.2735],[159.388,-4.244],[159.3135,-4.26],[159.2115,-4.33],[159.1615,-4.3835],[159.1345,-4.445],[159.1195,-4.557]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/e3/Flag_of_Papua_New_Guinea.svg","name:en":"Papua New Guinea","wikidata":"Q691","ISO3166-1:alpha2":"PG","ISO3166-1:alpha3":"PNG","ISO3166-1:numeric":"598"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[155.336,-7.0975],[155.3255,-7.1045],[155.319,-7.393],[155.347,-7.508],[155.418,-7.6035],[156.3845,-8.457],[156.4265,-8.4855],[157.242,-8.8985],[157.3365,-8.933],[157.5825,-8.9915],[157.7765,-9.012],[158.6735,-9.4405],[159.6,-9.8825],[159.724,-9.973],[159.7895,-9.994],[159.988,-10.0225],[160.841,-10.4665],[161.4025,-10.7585],[161.513,-10.84],[161.675,-10.937],[161.811,-10.9845],[162.4115,-11.1005],[162.4905,-11.1],[162.5415,-11.0865],[162.603,-11.049],[162.647,-10.9925],[162.668,-10.9245],[162.6775,-10.8225],[162.661,-10.7535],[162.3615,-10.067],[162.1775,-9.644],[162.1325,-9.5745],[161.1805,-8.5125],[160.817,-7.784],[160.778,-7.73],[160.7235,-7.692],[160.6325,-7.668],[160.377,-7.65],[159.048,-7.558],[158.9305,-7.4385],[158.7965,-7.3325],[158.7045,-7.284],[158.1505,-7.1185],[157.932,-7.0155],[156.74,-6.454],[156.6,-6.4025],[156.5005,-6.381],[156.434,-6.3795],[156.2195,-6.41],[156.174,-6.445],[156.036,-6.55],[156.0395,-6.6585],[155.923,-6.847],[155.6935,-6.926],[155.594,-6.926],[155.336,-7.0975]]],[[[158.981,-5.374],[158.9905,-5.4455],[159.0275,-5.5165],[159.0725,-5.5665],[159.1805,-5.652],[159.2455,-5.6755],[159.538,-5.726],[159.6875,-5.7265],[159.759,-5.7085],[159.8195,-5.6655],[159.8825,-5.5955],[159.911,-5.5335],[159.9165,-5.4655],[159.908,-5.368],[159.892,-5.3035],[159.8555,-5.2485],[159.532,-4.8985],[159.4765,-4.856],[159.357,-4.816],[159.2655,-4.8155],[159.143,-4.875],[159.091,-4.9365],[159.0395,-5.0305],[159.0165,-5.098],[158.9825,-5.329],[158.981,-5.374]]],[[[159.098,-6.2285],[159.123,-6.336],[159.175,-6.4065],[159.208,-6.4325],[159.31,-6.4735],[159.381,-6.4735],[159.463,-6.4405],[159.551,-6.359],[159.581,-6.2965],[159.594,-6.203],[159.581,-6.1245],[159.538,-6.039],[159.4915,-5.9965],[159.4305,-5.969],[159.356,-5.96],[159.285,-5.9755],[159.1985,-6.0225],[159.139,-6.085],[159.1045,-6.166],[159.098,-6.2285]]],[[[159.5455,-11.2525],[159.5525,-11.323],[159.855,-12.372],[159.8805,-12.427],[160.1215,-12.7865],[160.388,-13.1235],[160.498,-13.216],[160.5695,-13.2405],[160.645,-13.2375],[160.714,-13.2085],[160.8065,-13.1215],[160.836,-13.0655],[160.8465,-13.0035],[160.8555,-11.855],[160.8445,-11.7885],[160.7885,-11.7045],[160.6045,-11.5435],[159.923,-11.1155],[159.8515,-11.084],[159.735,-11.068],[159.665,-11.085],[159.6055,-11.125],[159.564,-11.1835],[159.5455,-11.2525]]],[[[162.6635,-8.4175],[162.688,-8.5225],[162.731,-8.577],[162.79,-8.613],[162.871,-8.628],[163.0245,-8.604],[163.0785,-8.5695],[163.132,-8.498],[163.152,-8.3885],[163.134,-8.3205],[163.095,-8.265],[163.045,-8.227],[162.947,-8.1835],[162.863,-8.1735],[162.7915,-8.192],[162.7495,-8.2175],[162.697,-8.278],[162.6675,-8.3525],[162.6635,-8.4175]]],[[[165.492,-10.109],[165.544,-10.7975],[165.553,-10.8735],[165.5775,-10.934],[165.62,-10.984],[166.7005,-11.8855],[166.7895,-11.9325],[166.8695,-11.9485],[166.954,-11.946],[167.0675,-11.9155],[167.163,-11.858],[167.2095,-11.8],[167.231,-11.729],[167.223,-11.5855],[167.204,-11.5145],[166.561,-10.1725],[166.523,-10.1185],[166.442,-10.036],[166.377,-9.9915],[166.1265,-9.884],[166.0685,-9.8685],[165.7375,-9.832],[165.66,-9.838],[165.582,-9.877],[165.516,-9.964],[165.492,-10.109]]],[[[166.858,-9.7725],[166.892,-9.868],[166.9485,-9.947],[167.027,-10.0305],[167.082,-10.072],[167.2195,-10.129],[167.288,-10.1265],[167.352,-10.1015],[167.404,-10.0565],[167.4375,-9.997],[167.4465,-9.8845],[167.425,-9.8225],[167.3845,-9.7705],[167.2235,-9.6255],[167.1705,-9.5855],[167.103,-9.5595],[167.0345,-9.5565],[166.969,-9.5765],[166.914,-9.6165],[166.8755,-9.6725],[166.858,-9.7725]]],[[[168.607,-12.3035],[168.6255,-12.3885],[168.6995,-12.474],[168.7695,-12.503],[168.881,-12.4995],[168.9625,-12.4625],[169.015,-12.411],[169.045,-12.344],[169.044,-12.2435],[169.012,-12.1755],[168.929,-12.1045],[168.837,-12.0825],[168.767,-12.092],[168.7035,-12.125],[168.631,-12.209],[168.607,-12.3035]]],[[[169.6445,-11.6295],[169.676,-11.7205],[169.726,-11.774],[169.796,-11.8075],[169.894,-11.8105],[169.956,-11.7865],[170.0265,-11.7175],[170.0515,-11.658],[170.051,-11.564],[170.0065,-11.477],[169.956,-11.435],[169.8905,-11.41],[169.82,-11.4085],[169.751,-11.4315],[169.6975,-11.474],[169.6655,-11.5205],[169.6445,-11.6295]]],[[[169.9825,-11.9235],[170.016,-12.0195],[170.07,-12.0765],[170.1235,-12.104],[170.2085,-12.1145],[170.2795,-12.0955],[170.3365,-12.0555],[170.3735,-12.005],[170.394,-11.9425],[170.381,-11.8345],[170.3355,-11.765],[170.286,-11.7285],[170.2195,-11.706],[170.149,-11.7075],[170.063,-11.7455],[170.0185,-11.7915],[169.99,-11.8495],[169.9825,-11.9235]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/74/Flag_of_the_Solomon_Islands.svg","name:en":"Solomon Islands","wikidata":"Q685","ISO3166-1:alpha2":"SB","ISO3166-1:alpha3":"SLB","ISO3166-1:numeric":"090"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[51.829,24.716],[51.827,24.8015],[51.802,24.854],[51.857,24.9565],[51.8735,25.024],[51.864,25.093],[51.839,25.1395],[51.8665,25.282],[51.864,25.329],[51.8305,25.4075],[51.7875,25.4515],[51.7475,25.536],[51.7785,25.585],[51.815,25.6995],[51.8105,25.85],[51.827,25.909],[51.8195,25.9785],[51.756,26.0735],[51.6895,26.113],[51.577,26.142],[51.5385,26.22],[51.4905,26.264],[51.388,26.335],[51.2975,26.374],[51.183,26.3815],[51.1055,26.3585],[51.044,26.313],[51.0115,26.267],[50.9195,26.2145],[50.9075,26.077],[50.85,26.011],[50.816,25.895],[50.868,25.7495],[50.8395,25.6385],[50.7985,25.6225],[50.7695,25.547],[50.7345,25.5725],[50.58,25.586],[50.5675,25.576],[50.573,25.5415],[50.615,25.4785],[50.647,25.3205],[50.647,25.256],[50.666,25.1375],[50.7135,25.0425],[50.706,24.923],[50.794,24.8775],[50.8115,24.7445],[50.9285,24.547],[50.9945,24.504],[51.0985,24.471],[51.266,24.5055],[51.302,24.5045],[51.376,24.5785],[51.391,24.6235],[51.586,24.664],[51.829,24.716]]],[[[52.45,24.983],[52.448,25.0855],[52.3845,25.1795],[52.315,25.219],[52.23,25.234],[52.119,25.208],[52.0535,25.1575],[52.017,25.0955],[52.009,25.0015],[52.0315,24.9395],[52.097,24.87],[52.1645,24.8385],[52.2645,24.8315],[52.3475,24.874],[52.3785,24.9005],[52.334,24.967],[52.346,24.9975],[52.3965,25.0195],[52.45,24.983]]],[[[52.1825,25.676],[52.206,25.5815],[52.2475,25.5275],[52.3055,25.489],[52.385,25.468],[52.4625,25.472],[52.5265,25.496],[52.612,25.5775],[52.6345,25.639],[52.6315,25.7245],[52.605,25.7825],[52.5505,25.839],[52.4785,25.8735],[52.403,25.8825],[52.296,25.8535],[52.213,25.7765],[52.1825,25.676]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/65/Flag_of_Qatar.svg","name:en":"Qatar","wikidata":"Q846","ISO3166-1:alpha2":"QA","ISO3166-1:alpha3":"QAT","ISO3166-1:numeric":"634"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[3.6095,11.6935],[3.5765,11.6685],[3.525,11.575],[3.4765,11.438],[3.6935,11.129],[3.7225,11.1285],[3.725,11.0275],[3.751,11.013],[3.7695,10.924],[3.7455,10.817],[3.78,10.736],[3.835,10.696],[3.845,10.5945],[3.8055,10.514],[3.8055,10.4505],[3.785,10.4075],[3.6835,10.4615],[3.602,10.4125],[3.604,10.344],[3.5765,10.2725],[3.608,10.2115],[3.6795,10.177],[3.659,10.109],[3.617,10.084],[3.598,9.957],[3.5635,9.901],[3.515,9.8605],[3.4625,9.8705],[3.3625,9.835],[3.332,9.8055],[3.325,9.761],[3.361,9.708],[3.314,9.6595],[3.267,9.659],[3.252,9.6065],[3.1885,9.5115],[3.1615,9.4975],[3.133,9.441],[3.1555,9.3605],[3.158,9.284],[3.123,9.2325],[3.122,9.1925],[3.0885,9.102],[2.9725,9.075],[2.953,9.0925],[2.8795,9.0645],[2.783,9.0695],[2.7905,8.9895],[2.7595,8.8445],[2.731,8.787],[2.749,8.7405],[2.739,8.683],[2.7555,8.654],[2.7495,8.4485],[2.7085,8.4225],[2.7195,8.3875],[2.696,8.3545],[2.7205,8.25],[2.754,8.2125],[2.733,8.0815],[2.6995,7.9325],[2.6785,7.8745],[2.7255,7.8025],[2.729,7.7555],[2.7165,7.6365],[2.735,7.55],[2.7925,7.4975],[2.794,7.4295],[2.745,7.4245],[2.745,7.2815],[2.7575,7.2515],[2.773,7.133],[2.7405,7.105],[2.7615,7.0435],[2.729,7.017],[2.7125,6.9515],[2.7415,6.9275],[2.7345,6.785],[2.7845,6.764],[2.784,6.699],[2.7295,6.637],[2.748,6.568],[2.706,6.52],[2.7065,6.3775],[2.738,6.178],[2.904,6.196],[3.1915,6.2045],[3.3905,6.194],[3.5005,6.2225],[3.7175,6.228],[3.8155,6.2395],[3.8955,6.238],[4.3425,6.167],[4.413,6.1355],[4.486,6.082],[4.639,5.956],[4.816,5.7765],[4.9575,5.535],[4.9655,5.4815],[5.002,5.411],[5.053,5.344],[5.132,5.2745],[5.1505,5.2085],[5.1615,5.101],[5.235,4.9555],[5.27,4.823],[5.364,4.641],[5.474,4.4805],[5.5565,4.3775],[5.733,4.235],[5.8295,4.1775],[5.8755,4.139],[6.0675,4.07],[6.231,4.0775],[6.2925,4.0915],[6.471,4.1035],[6.539,4.124],[6.6225,4.118],[6.725,4.141],[6.895,4.1605],[6.95,4.176],[7.026,4.177],[7.0775,4.194],[7.1715,4.179],[7.2755,4.1905],[7.4095,4.235],[7.519,4.2515],[7.6415,4.2595],[7.7265,4.3015],[7.9455,4.3355],[8.089,4.345],[8.1865,4.3445],[8.256,4.334],[8.336,4.3435],[8.388,4.3675],[8.403,4.442],[8.4075,4.5335],[8.4525,4.6165],[8.539,4.702],[8.526,4.737],[8.544,4.8015],[8.6255,4.821],[8.607,4.8645],[8.6205,4.9055],[8.653,4.9175],[8.6955,5.013],[8.724,5.041],[8.745,5.098],[8.7835,5.113],[8.821,5.1845],[8.8145,5.284],[8.8415,5.3915],[8.8335,5.4305],[8.9215,5.564],[8.904,5.619],[8.842,5.6795],[8.884,5.775],[8.8505,5.805],[8.8605,5.845],[9.0545,6.0005],[9.1535,6.094],[9.169,6.1335],[9.2115,6.1685],[9.2645,6.1815],[9.307,6.263],[9.3345,6.292],[9.347,6.3535],[9.431,6.3155],[9.4675,6.456],[9.529,6.4425],[9.587,6.4735],[9.5955,6.5285],[9.706,6.512],[9.751,6.6525],[9.774,6.7845],[9.863,6.776],[9.996,6.898],[10.0125,6.904],[10.1505,7.0385],[10.181,6.968],[10.172,6.942],[10.2155,6.8895],[10.462,6.916],[10.4815,6.9015],[10.5415,6.9395],[10.56,7.032],[10.5715,7.162],[10.595,7.1475],[10.5955,7.0785],[10.6265,7.049],[10.6795,7.0385],[10.7205,6.9885],[10.7665,6.9555],[10.7985,6.9625],[10.8415,6.9305],[10.8145,6.8535],[10.8395,6.811],[10.914,6.7575],[10.916,6.7095],[10.9455,6.687],[10.9925,6.687],[11.0305,6.7145],[11.0965,6.6795],[11.097,6.5205],[11.167,6.5],[11.222,6.5395],[11.28,6.538],[11.316,6.5065],[11.422,6.532],[11.4275,6.579],[11.462,6.611],[11.5175,6.613],[11.5465,6.701],[11.585,6.7375],[11.5695,6.773],[11.5795,6.8415],[11.573,6.8995],[11.6035,6.909],[11.631,6.9895],[11.7375,7.05],[11.779,7.046],[11.8025,7.0825],[11.863,7.0775],[11.8805,7.108],[11.8565,7.1685],[11.847,7.2545],[11.879,7.3475],[11.928,7.394],[11.9315,7.4865],[12.019,7.5185],[12.005,7.5805],[12.0115,7.6305],[11.9965,7.667],[12.0525,7.7355],[12.098,7.8475],[12.136,7.862],[12.214,7.9835],[12.201,8.009],[12.1925,8.108],[12.256,8.1765],[12.249,8.216],[12.244,8.3865],[12.2675,8.448],[12.3135,8.4255],[12.342,8.4585],[12.4615,8.537],[12.4465,8.6025],[12.4875,8.643],[12.575,8.614],[12.6985,8.669],[12.7205,8.763],[12.784,8.7455],[12.819,8.8305],[12.8,8.8565],[12.823,8.9715],[12.901,9.114],[12.888,9.1775],[12.9175,9.2485],[12.9025,9.3545],[12.8785,9.394],[12.9455,9.422],[12.9755,9.4645],[13.0345,9.506],[13.1245,9.5265],[13.2095,9.557],[13.234,9.614],[13.2625,9.78],[13.302,9.827],[13.249,9.8575],[13.237,9.911],[13.265,9.927],[13.288,9.9795],[13.2475,10.0055],[13.2865,10.046],[13.299,10.087],[13.3395,10.114],[13.412,10.1215],[13.47,10.161],[13.4835,10.196],[13.466,10.246],[13.5015,10.3375],[13.5005,10.3905],[13.535,10.4],[13.531,10.4575],[13.5785,10.537],[13.547,10.6125],[13.5745,10.635],[13.5885,10.698],[13.6265,10.7115],[13.6505,10.813],[13.717,10.863],[13.73,10.924],[13.7115,10.9635],[13.7355,11.006],[13.7895,11.003],[13.815,11.0645],[13.856,11.0955],[13.9345,11.2065],[13.976,11.311],[14.1785,11.24],[14.239,11.296],[14.3645,11.3595],[14.4325,11.4225],[14.4715,11.428],[14.5175,11.468],[14.618,11.51],[14.647,11.572],[14.644,11.65],[14.6,11.698],[14.5555,11.7055],[14.6065,11.799],[14.6045,11.8695],[14.643,11.912],[14.6445,11.9725],[14.624,12.0495],[14.656,12.1385],[14.659,12.198],[14.6175,12.186],[14.591,12.2295],[14.5255,12.2915],[14.4825,12.3525],[14.439,12.3675],[14.3765,12.3595],[14.334,12.3735],[14.2225,12.362],[14.1895,12.405],[14.1775,12.481],[14.2035,12.538],[14.0835,13.0835],[13.6335,13.708],[13.363,13.708],[13.313,13.6975],[13.2055,13.5415],[13.1195,13.5225],[13.0305,13.531],[12.9695,13.519],[12.917,13.487],[12.8715,13.501],[12.851,13.4445],[12.7625,13.3875],[12.6775,13.2815],[12.644,13.293],[12.5765,13.2715],[12.5535,13.2275],[12.5505,13.1595],[12.514,13.1535],[12.469,13.0675],[12.3215,13.085],[12.257,13.118],[12.1865,13.124],[12.1645,13.0985],[12.0385,13.141],[12.009,13.172],[11.883,13.256],[11.6785,13.299],[11.5905,13.3465],[11.459,13.3805],[11.279,13.379],[10.656,13.3605],[10.4655,13.2875],[10.3365,13.275],[10.204,13.2705],[10.0115,13.1825],[9.919,13.095],[9.889,13.052],[9.841,13.0265],[9.7965,12.9555],[9.6585,12.8075],[9.3865,12.823],[9.331,12.811],[9.182,12.8345],[8.978,12.8335],[8.8115,12.8895],[8.703,12.919],[8.646,12.9435],[8.597,13.0235],[8.502,13.074],[8.416,13.057],[8.2495,13.214],[8.1915,13.2315],[8.1515,13.2735],[8.069,13.314],[7.816,13.3425],[7.7025,13.277],[7.661,13.244],[7.4395,13.115],[7.389,13.0985],[7.2215,13.129],[7.1215,13.0205],[7.053,13.0],[6.942,13.0035],[6.82,13.1435],[6.6965,13.3395],[6.428,13.6005],[6.2775,13.6765],[6.2325,13.6735],[6.1555,13.645],[6.091,13.677],[5.832,13.7615],[5.631,13.8365],[5.53,13.8855],[5.4515,13.871],[5.3505,13.8345],[5.281,13.7555],[5.2055,13.736],[5.077,13.751],[5.0045,13.7355],[4.9095,13.7455],[4.874,13.781],[4.8395,13.768],[4.4655,13.6815],[4.2345,13.478],[4.1415,13.478],[4.1475,13.2925],[4.142,13.162],[4.1035,12.9875],[4.054,12.9],[3.9835,12.8175],[3.942,12.7465],[3.774,12.626],[3.6535,12.5225],[3.6505,12.402],[3.6665,12.259],[3.647,12.2125],[3.6315,12.1195],[3.6715,12.032],[3.6785,11.9765],[3.62,11.9205],[3.6295,11.831],[3.669,11.8105],[3.681,11.7545],[3.6095,11.6935]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/79/Flag_of_Nigeria.svg","name:en":"Nigeria","wikidata":"Q1033","ISO3166-1:alpha2":"NG","ISO3166-1:alpha3":"NGA","ISO3166-1:numeric":"566"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-61.1305,13.58],[-61.248,13.57],[-61.3365,13.522],[-61.3755,13.479],[-61.449,13.367],[-61.4755,13.295],[-61.484,13.223],[-61.477,13.151],[-61.458,13.104],[-61.485,13.05],[-61.4945,12.988],[-61.471,12.886],[-61.515,12.83],[-61.6085,12.741],[-61.6415,12.69],[-61.6655,12.5965],[-61.3825,12.53],[-61.173,12.5165],[-61.143,12.6],[-61.063,12.665],[-60.9545,12.83],[-60.9215,12.938],[-60.928,13.02],[-60.955,13.076],[-60.9165,13.188],[-60.9095,13.242],[-60.9165,13.366],[-60.938,13.436],[-60.9865,13.5115],[-61.1305,13.58]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/6d/Flag_of_Saint_Vincent_and_the_Grenadines.svg","name:en":"Saint Vincent and the Grenadines","wikidata":"Q757","ISO3166-1:alpha2":"VC","ISO3166-1:alpha3":"VCT","ISO3166-1:numeric":"670"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[104.125,1.276],[104.0795,1.3575],[104.0765,1.431],[104.041,1.444],[104.003,1.4205],[103.898,1.428],[103.8125,1.4785],[103.7605,1.4485],[103.714,1.4575],[103.674,1.428],[103.618,1.3215],[103.603,1.264],[103.5765,1.2545],[103.5665,1.1955],[103.6585,1.185],[103.7405,1.1305],[103.805,1.1715],[104.0335,1.2695],[104.125,1.276]]],[[[104.508,1.5015],[104.4635,1.4995],[104.3785,1.4105],[104.349,1.333],[104.389,1.317],[104.523,1.3935],[104.5705,1.442],[104.508,1.5015]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/48/Flag_of_Singapore.svg","name:en":"Singapore","wikidata":"Q334","ISO3166-1:alpha2":"SG","ISO3166-1:alpha3":"SGP","ISO3166-1:numeric":"702"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[13.6335,13.708],[13.4855,14.3585],[13.4735,14.443],[13.4935,14.4745],[13.572,14.5055],[13.641,14.5135],[13.696,14.551],[13.6775,14.6365],[13.7455,14.6985],[13.81,14.7215],[13.7895,14.863],[13.8695,15.043],[13.97,15.1565],[14.276,15.588],[14.385,15.7315],[14.6105,15.956],[14.9135,16.2765],[14.9565,16.3255],[15.2585,16.6395],[15.5055,16.898],[15.522,17.333],[15.5435,17.729],[15.573,18.2625],[15.6025,18.782],[15.6665,19.264],[15.754,19.9325],[15.9965,20.353],[15.6755,20.6965],[15.576,20.7665],[15.554,20.8525],[15.5685,20.9105],[15.627,20.9555],[15.2845,21.4455],[15.2025,21.496],[15.1945,21.999],[15.074,22.6135],[14.9985,23.001],[14.6635,22.832],[14.2345,22.6135],[13.6235,23.1165],[13.5435,23.1685],[13.417,23.214],[12.9315,23.3195],[12.576,23.396],[11.9975,23.517],[11.729,23.364],[11.2925,23.116],[11.0015,22.9335],[10.6715,22.7455],[10.2385,22.4965],[9.756,22.211],[9.351,21.976],[9.051,21.8],[8.997,21.7785],[8.4345,21.4395],[8.0135,21.1835],[7.994,21.167],[7.4595,20.8425],[6.999,20.455],[6.4695,20.0085],[6.437,19.9755],[6.1275,19.7125],[5.9875,19.599],[5.8105,19.447],[5.179,19.3205],[4.8495,19.2535],[4.336,19.1495],[4.2665,19.144],[4.267,18.7635],[4.2665,18.2855],[4.2675,17.92],[4.266,17.388],[4.2665,17.0015],[4.2145,17.0],[4.2065,16.837],[4.1975,16.815],[4.2,16.395],[4.1185,16.35],[4.097,16.323],[4.063,16.2225],[3.986,16.081],[3.9935,16.0295],[3.9775,15.935],[3.928,15.8645],[3.901,15.747],[3.856,15.6865],[3.798,15.6635],[3.7225,15.659],[3.645,15.586],[3.5915,15.517],[3.537,15.485],[3.5365,15.4035],[3.493,15.358],[3.0315,15.4285],[3.02,15.338],[2.6365,15.347],[2.166,15.3225],[1.8495,15.3065],[1.3115,15.278],[1.0,15.0],[0.9065,14.972],[0.7335,14.96],[0.6875,14.9445],[0.5725,14.989],[0.5145,14.9975],[0.3945,14.964],[0.232,15.0],[0.2445,14.914],[0.1965,14.8265],[0.236,14.7355],[0.169,14.522],[0.213,14.418],[0.381,14.051],[0.417,14.023],[0.424,13.9835],[0.473,13.951],[0.465,13.9135],[0.526,13.8435],[0.618,13.767],[0.598,13.731],[0.625,13.684],[0.773,13.6885],[0.7745,13.644],[0.833,13.625],[0.902,13.623],[0.977,13.57],[1.019,13.523],[1.0315,13.4625],[1.124,13.4135],[1.2035,13.387],[1.2445,13.393],[1.283,13.3545],[1.1855,13.321],[1.115,13.3315],[0.992,13.3745],[0.992,13.1035],[0.974,13.0075],[1.1595,13.0075],[1.33,12.8315],[1.5665,12.6115],[1.8685,12.6055],[1.928,12.7005],[1.989,12.731],[2.0385,12.7165],[2.096,12.7255],[2.146,12.691],[2.1545,12.6515],[2.201,12.631],[2.221,12.5935],[2.2255,12.5165],[2.264,12.4735],[2.2615,12.4145],[2.1615,12.4155],[2.118,12.387],[2.078,12.386],[2.0655,12.3475],[2.2055,12.1675],[2.3975,11.91],[2.409,11.9025],[2.4065,11.9525],[2.464,11.9855],[2.4035,12.1035],[2.3875,12.2165],[2.4015,12.258],[2.471,12.273],[2.5475,12.274],[2.5925,12.2995],[2.662,12.3065],[2.6945,12.283],[2.7225,12.3525],[2.8415,12.4055],[2.8695,12.3895],[2.9665,12.2865],[3.0145,12.266],[3.0745,12.1815],[3.126,12.156],[3.2205,12.0615],[3.2795,11.942],[3.323,11.8845],[3.3735,11.889],[3.448,11.8585],[3.482,11.86],[3.5615,11.7785],[3.5515,11.7285],[3.6095,11.6935],[3.681,11.7545],[3.669,11.8105],[3.6295,11.831],[3.62,11.9205],[3.6785,11.9765],[3.6715,12.032],[3.6315,12.1195],[3.647,12.2125],[3.6665,12.259],[3.6505,12.402],[3.6535,12.5225],[3.774,12.626],[3.942,12.7465],[3.9835,12.8175],[4.054,12.9],[4.1035,12.9875],[4.142,13.162],[4.1475,13.2925],[4.1415,13.478],[4.2345,13.478],[4.4655,13.6815],[4.8395,13.768],[4.874,13.781],[4.9095,13.7455],[5.0045,13.7355],[5.077,13.751],[5.2055,13.736],[5.281,13.7555],[5.3505,13.8345],[5.4515,13.871],[5.53,13.8855],[5.631,13.8365],[5.832,13.7615],[6.091,13.677],[6.1555,13.645],[6.2325,13.6735],[6.2775,13.6765],[6.428,13.6005],[6.6965,13.3395],[6.82,13.1435],[6.942,13.0035],[7.053,13.0],[7.1215,13.0205],[7.2215,13.129],[7.389,13.0985],[7.4395,13.115],[7.661,13.244],[7.7025,13.277],[7.816,13.3425],[8.069,13.314],[8.1515,13.2735],[8.1915,13.2315],[8.2495,13.214],[8.416,13.057],[8.502,13.074],[8.597,13.0235],[8.646,12.9435],[8.703,12.919],[8.8115,12.8895],[8.978,12.8335],[9.182,12.8345],[9.331,12.811],[9.3865,12.823],[9.6585,12.8075],[9.7965,12.9555],[9.841,13.0265],[9.889,13.052],[9.919,13.095],[10.0115,13.1825],[10.204,13.2705],[10.3365,13.275],[10.4655,13.2875],[10.656,13.3605],[11.279,13.379],[11.459,13.3805],[11.5905,13.3465],[11.6785,13.299],[11.883,13.256],[12.009,13.172],[12.0385,13.141],[12.1645,13.0985],[12.1865,13.124],[12.257,13.118],[12.3215,13.085],[12.469,13.0675],[12.514,13.1535],[12.5505,13.1595],[12.5535,13.2275],[12.5765,13.2715],[12.644,13.293],[12.6775,13.2815],[12.7625,13.3875],[12.851,13.4445],[12.8715,13.501],[12.917,13.487],[12.9695,13.519],[13.0305,13.531],[13.1195,13.5225],[13.2055,13.5415],[13.313,13.6975],[13.363,13.708],[13.6335,13.708]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/f4/Flag_of_Niger.svg","name:en":"Niger","wikidata":"Q1032","ISO3166-1:alpha2":"NE","ISO3166-1:alpha3":"NER","ISO3166-1:numeric":"562"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-9.685,70.868],[-9.6545,70.785],[-9.4995,70.69],[-9.3075,70.6455],[-8.96,70.6265],[-8.681,70.6525],[-8.4485,70.7015],[-8.273,70.768],[-7.8195,70.82],[-7.638,70.855],[-7.493,70.9035],[-7.3865,70.9825],[-7.3155,71.1275],[-7.3185,71.1745],[-7.4215,71.2625],[-7.567,71.3155],[-7.696,71.341],[-7.947,71.36],[-8.219,71.352],[-8.578,71.298],[-8.748,71.255],[-8.968,71.1545],[-9.299,71.0825],[-9.486,71.02],[-9.6275,70.9515],[-9.685,70.868]]],[[[2.9345,-54.406],[2.964,-54.512],[3.0745,-54.599],[3.1895,-54.641],[3.2775,-54.653],[3.4905,-54.642],[3.623,-54.606],[3.7225,-54.539],[3.77,-54.469],[3.762,-54.359],[3.678,-54.269],[3.6,-54.228],[3.481,-54.197],[3.2925,-54.19],[3.158,-54.21],[3.0545,-54.246],[3.0,-54.281],[2.944,-54.354],[2.9345,-54.406]]],[[[20.5485,69.06],[20.7175,69.12],[21.0575,69.0365],[21.1085,69.104],[20.9875,69.192],[21.096,69.261],[21.279,69.312],[21.627,69.2765],[21.7235,69.2145],[22.176,68.9565],[22.192,68.919],[22.341,68.827],[22.3745,68.7165],[22.5355,68.7445],[22.801,68.6875],[23.046,68.6895],[23.1675,68.6285],[23.4405,68.692],[23.6735,68.7055],[23.7755,68.819],[23.9835,68.827],[24.0755,68.78],[24.3025,68.7175],[24.6085,68.682],[24.917,68.605],[25.0765,68.622],[25.157,68.8],[25.2675,68.851],[25.4815,68.905],[25.627,68.8925],[25.7775,69.0195],[25.7205,69.109],[25.742,69.145],[25.694,69.1955],[25.742,69.319],[25.8465,69.394],[25.793,69.421],[25.876,69.529],[25.976,69.61],[25.8925,69.6655],[26.007,69.723],[26.136,69.7385],[26.3605,69.845],[26.4665,69.94],[26.676,69.9645],[26.7325,69.9465],[26.986,69.935],[27.0315,69.911],[27.3055,69.958],[27.437,70.0205],[27.5235,70.023],[27.5745,70.067],[27.9595,70.092],[27.9845,70.014],[28.1605,69.921],[28.3455,69.881],[28.404,69.8185],[29.134,69.6955],[29.1705,69.639],[29.3365,69.4785],[29.2195,69.3975],[28.8315,69.2245],[28.8055,69.111],[28.9295,69.052],[29.0565,69.015],[29.242,69.113],[29.313,69.2295],[29.286,69.294],[29.3905,69.322],[29.573,69.3185],[29.7245,69.3895],[29.8685,69.424],[29.9705,69.408],[30.115,69.4685],[30.1195,69.5135],[30.188,69.566],[30.138,69.6435],[30.224,69.653],[30.4175,69.59],[30.516,69.5405],[30.818,69.529],[30.939,69.5605],[30.9455,69.6775],[30.8175,69.795],[31.1045,69.9795],[31.441,70.0985],[31.5855,70.144],[31.6355,70.2375],[31.7245,70.3185],[31.7615,70.3885],[31.6875,70.4845],[31.5485,70.5455],[30.6095,70.8265],[30.381,70.8825],[29.566,71.0235],[28.5555,71.2625],[28.2995,71.297],[27.718,71.3325],[26.26,71.3745],[25.7105,71.3845],[25.5435,71.3805],[24.6295,71.314],[24.0165,71.301],[23.723,71.2795],[22.4765,71.027],[21.7155,70.8555],[20.4795,70.7035],[18.847,70.487],[18.3595,70.3995],[18.163,70.3405],[17.452,69.993],[17.0555,69.745],[16.677,69.667],[15.79,69.5175],[15.6985,69.498],[14.816,69.2595],[14.7335,69.2315],[13.892,68.8725],[13.7215,68.746],[13.264,68.4635],[12.8325,68.3465],[12.584,68.2355],[12.306,68.0315],[12.1285,67.823],[11.6965,67.696],[11.507,67.631],[11.3925,67.5715],[11.307,67.4555],[11.359,67.331],[11.859,66.8155],[11.654,66.724],[11.566,66.6675],[11.0935,66.1995],[11.0715,66.172],[10.7985,65.702],[10.579,65.471],[10.0895,64.9925],[9.997,64.87],[8.9405,64.3575],[8.1645,64.0665],[8.09,64.031],[7.462,63.6555],[7.358,63.581],[6.839,63.26],[5.996,62.974],[5.671,62.8265],[4.949,62.472],[4.667,62.2705],[4.506,62.106],[4.178,61.7265],[4.1475,61.6615],[4.088,61.084],[4.104,60.976],[4.494,60.272],[4.649,59.775],[4.672,59.665],[4.4835,59.369],[4.4545,59.278],[4.4875,59.1955],[4.6125,59.112],[4.895,59.0005],[5.006,58.923],[5.112,58.701],[5.18,58.5995],[5.3275,58.456],[5.4155,58.3945],[5.623,58.2735],[6.344,57.9355],[6.529,57.872],[6.8635,57.799],[7.1805,57.7625],[7.5575,57.759],[7.693,57.7665],[7.83,57.791],[8.163,57.865],[8.4135,57.928],[8.506,57.961],[8.9425,58.176],[9.7885,58.6725],[10.074,58.748],[10.271,58.775],[10.593,58.761],[10.639,58.8925],[10.918,58.9425],[11.0665,58.9775],[11.117,59.015],[11.1535,59.0795],[11.3685,59.0985],[11.4645,58.991],[11.4555,58.8895],[11.63,58.9085],[11.688,58.9555],[11.7105,59.0335],[11.775,59.0865],[11.783,59.206],[11.83,59.2425],[11.816,59.3445],[11.691,59.5895],[11.7205,59.6255],[11.8555,59.6485],[11.94,59.6945],[11.926,59.794],[11.8395,59.841],[11.9415,59.89],[12.1745,59.89],[12.231,59.9275],[12.341,59.9655],[12.4485,60.039],[12.5005,60.099],[12.542,60.1935],[12.499,60.3235],[12.606,60.406],[12.607,60.5125],[12.516,60.6],[12.511,60.6425],[12.3955,60.734],[12.3345,60.8525],[12.333,60.89],[12.224,61.013],[12.4475,61.0505],[12.6105,61.0465],[12.6825,61.061],[12.707,61.1435],[12.7905,61.197],[12.834,61.2585],[12.871,61.3565],[12.5695,61.5685],[12.4195,61.563],[12.1375,61.724],[12.2995,62.2675],[12.056,62.612],[12.1365,62.748],[12.0745,62.9025],[12.218,63.0005],[11.9745,63.269],[12.084,63.3555],[12.213,63.4785],[12.15,63.594],[12.2995,63.672],[12.3305,63.715],[12.48,63.819],[12.6835,63.974],[12.9265,64.058],[13.211,64.0955],[13.7155,64.0465],[13.9675,64.008],[14.157,64.195],[14.114,64.4625],[13.891,64.507],[13.6545,64.5805],[13.7055,64.64],[14.13,64.9785],[14.326,65.119],[14.379,65.2475],[14.507,65.3095],[14.499,65.5215],[14.5415,65.7005],[14.6255,65.812],[14.5845,65.9015],[14.5165,66.1325],[15.0355,66.1535],[15.4845,66.2825],[15.377,66.4845],[15.6215,66.5945],[16.039,66.9125],[16.194,66.9825],[16.3875,67.0455],[16.404,67.205],[16.09,67.4355],[16.158,67.519],[16.4075,67.534],[16.556,67.647],[16.738,67.914],[17.1805,68.0505],[17.2815,68.119],[17.6645,68.0385],[17.9,67.9695],[18.1515,68.199],[18.101,68.406],[18.126,68.5365],[18.4055,68.582],[18.621,68.507],[18.984,68.517],[19.9215,68.356],[20.2265,68.491],[19.9375,68.558],[20.0525,68.591],[20.203,68.666],[20.336,68.8025],[20.3065,68.926],[20.06,69.046],[20.5485,69.06]]],[[[9.421,78.8965],[9.471,78.7705],[9.6495,78.6395],[10.112,78.385],[10.253,78.3325],[11.058,78.1105],[11.4255,78.037],[11.7845,78.0075],[12.5135,78.0005],[12.581,77.9835],[12.6085,77.8355],[12.7955,77.6975],[13.017,77.3445],[13.383,77.139],[13.5455,77.079],[13.91,76.9855],[14.217,76.9255],[14.658,76.762],[15.165,76.61],[15.554,76.445],[15.8605,76.337],[16.192,76.2695],[16.4745,76.2455],[16.919,76.2565],[17.2915,76.3045],[17.433,76.3355],[17.7325,76.4305],[17.8885,76.517],[18.0035,76.6805],[18.293,77.1485],[18.4465,77.3195],[18.671,77.3175],[19.733,77.3785],[20.172,77.3065],[20.4385,77.2075],[20.5795,77.105],[20.7735,76.8395],[20.8315,76.794],[21.0265,76.7285],[21.398,76.6765],[21.6565,76.667],[22.0515,76.678],[22.4105,76.7285],[23.1665,76.9],[23.632,77.0355],[24.092,77.211],[24.579,77.4495],[25.679,77.625],[25.909,77.675],[26.0455,77.7345],[26.0995,77.816],[25.9825,77.921],[25.682,77.989],[24.8665,78.1005],[24.0365,78.2695],[23.9365,78.315],[22.8605,78.6655],[22.5985,78.8225],[22.637,78.838],[24.4775,79.0225],[25.954,79.2],[26.2165,79.243],[26.675,79.3475],[27.544,79.5735],[28.1335,79.6915],[28.522,79.8085],[29.191,79.9565],[29.333,79.9955],[29.4405,80.101],[29.3255,80.188],[29.119,80.2435],[28.654,80.309],[28.287,80.3365],[27.4935,80.3725],[27.3355,80.523],[27.0855,80.5935],[25.898,80.801],[25.3825,80.8525],[24.8725,80.861],[24.52,80.8465],[23.004,80.74],[22.0845,80.857],[21.1555,80.982],[20.847,81.012],[20.258,81.0275],[19.6285,80.993],[19.202,80.9135],[18.7135,80.742],[17.2545,80.4835],[16.8775,80.369],[16.769,80.293],[16.0435,80.26],[14.4,80.2365],[13.9405,80.213],[13.675,80.18],[13.146,80.0655],[12.5745,80.049],[11.8135,80.1045],[11.2495,80.0945],[10.6505,80.04],[9.8265,79.919],[9.558,79.8575],[9.447,79.753],[9.577,79.495],[9.784,79.306],[10.0065,79.143],[9.563,78.9965],[9.421,78.8965]]],[[[17.996,74.477],[18.0455,74.399],[18.2115,74.3115],[18.5335,74.194],[18.7305,74.156],[18.955,74.137],[19.3025,74.147],[19.6815,74.213],[19.8905,74.305],[19.995,74.41],[20.0055,74.4955],[19.9035,74.571],[19.6945,74.645],[19.4395,74.6935],[19.2015,74.7125],[18.751,74.703],[18.413,74.6665],[18.2665,74.6365],[18.1085,74.5785],[17.996,74.477]]],[[[24.0435,76.4465],[24.1785,76.343],[24.3715,76.2925],[24.7595,76.2485],[25.007,76.245],[25.272,76.2605],[25.5015,76.293],[25.7555,76.367],[26.2765,76.6205],[26.365,76.7015],[26.288,76.7965],[26.1355,76.85],[25.738,76.9065],[25.334,76.911],[25.145,76.8975],[24.859,76.855],[24.6385,76.791],[24.29,76.6495],[24.1365,76.5705],[24.0435,76.4465]]],[[[25.335,78.8145],[25.408,78.7195],[25.749,78.572],[26.071,78.4895],[26.342,78.4555],[26.7185,78.4395],[27.252,78.4665],[27.628,78.5155],[27.9725,78.5755],[28.3115,78.5375],[28.852,78.5285],[29.34,78.576],[30.255,78.7335],[30.8,78.8005],[31.2295,78.8805],[31.448,78.9735],[31.396,79.081],[31.1935,79.146],[30.898,79.193],[30.663,79.212],[30.24,79.2185],[28.226,79.164],[27.8985,79.144],[27.3745,79.0645],[26.362,79.035],[25.8915,78.997],[25.564,78.934],[25.394,78.874],[25.335,78.8145]]],[[[30.2665,80.089],[30.361,80.0045],[30.748,79.9065],[31.123,79.8555],[31.461,79.8345],[32.0795,79.845],[33.766,79.954],[34.196,80.0005],[34.413,80.051],[34.601,80.1305],[34.6725,80.2645],[34.5505,80.3245],[33.996,80.435],[33.6485,80.478],[32.917,80.5155],[32.4425,80.5035],[31.8855,80.4685],[31.2705,80.4015],[30.7755,80.32],[30.5415,80.2665],[30.373,80.1995],[30.2665,80.089]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d9/Flag_of_Norway.svg","name:en":"Norway","wikidata":"Q20","ISO3166-1:alpha2":"NO","ISO3166-1:alpha3":"NOR","ISO3166-1:numeric":"578"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[25.0,22.0],[25.0015,22.337],[25.001,23.04],[24.9995,23.5665],[24.999,23.899],[24.9985,24.468],[24.997,24.7865],[24.998,25.513],[24.999,25.9845],[24.999,26.4145],[25.0005,27.0665],[25.001,27.524],[25.0025,28.1205],[25.0005,28.4295],[24.9985,28.8885],[24.998,29.249],[24.876,29.512],[24.887,29.664],[24.8125,29.7845],[24.8345,29.8765],[24.7405,30.0985],[24.65,30.1265],[24.698,30.2295],[24.7975,30.348],[24.849,30.393],[24.89,30.452],[24.9455,30.6445],[25.011,30.782],[24.9805,30.9],[24.9035,31.0395],[24.8585,31.155],[24.8635,31.2385],[24.8445,31.3625],[24.8625,31.406],[24.981,31.5],[25.0765,31.551],[25.076,31.599],[25.134,31.67],[25.377,31.722],[25.2495,32.0195],[25.216,32.0645],[25.1425,32.1205],[25.0565,32.161],[24.9305,32.182],[24.8395,32.2085],[24.716,32.2235],[24.344,32.2025],[24.2005,32.2085],[24.1575,32.243],[24.049,32.2955],[23.8415,32.3575],[23.687,32.3825],[23.5005,32.3795],[23.479,32.3865],[23.4395,32.4805],[23.363,32.5825],[23.3635,32.6375],[23.339,32.706],[23.301,32.755],[23.22,32.8115],[23.0985,32.8475],[23.025,32.8575],[22.571,33.0315],[22.4345,33.067],[22.334,33.0775],[22.24,33.123],[22.155,33.139],[21.939,33.1075],[21.754,33.139],[21.667,33.138],[21.546,33.1145],[21.491,33.09],[21.3345,32.9895],[21.058,32.968],[20.963,32.944],[20.8625,32.905],[20.487,32.7345],[20.3955,32.6435],[20.184,32.4905],[19.963,32.2835],[19.7795,32.0475],[19.7745,32.0115],[19.719,31.849],[19.7205,31.7565],[19.7535,31.6355],[19.78,31.4505],[19.827,31.322],[19.9345,31.1495],[19.9535,31.09],[19.788,30.8855],[19.608,30.6775],[19.411,30.5385],[19.2175,30.458],[18.976,30.464],[18.6635,30.6545],[18.3325,30.9045],[18.0385,30.987],[17.9925,31.0505],[17.5835,31.157],[17.4855,31.218],[16.781,31.354],[16.3205,31.368],[16.086,31.4015],[15.863,31.509],[15.736,31.6285],[15.593,31.8555],[15.524,32.0145],[15.5235,32.173],[15.397,32.3985],[15.3105,32.467],[15.23,32.5105],[14.8245,32.577],[14.594,32.7135],[14.486,32.796],[14.3335,32.879],[14.0135,32.965],[13.824,33.002],[13.6105,33.016],[13.493,33.0775],[13.4375,33.0935],[13.2435,33.12],[13.112,33.1025],[12.9265,33.03],[12.684,32.9965],[12.491,33.0175],[12.296,33.0705],[12.039,33.2135],[11.9205,33.27],[11.789,33.315],[11.664,33.349],[11.5615,33.167],[11.527,33.0785],[11.5275,32.986],[11.4955,32.918],[11.5015,32.8305],[11.4875,32.7625],[11.494,32.694],[11.478,32.656],[11.5015,32.612],[11.5515,32.581],[11.6,32.519],[11.595,32.4545],[11.549,32.4085],[11.3845,32.345],[11.029,32.1995],[10.8615,32.1055],[10.861,32.0855],[10.795,31.998],[10.7225,31.9675],[10.658,31.9775],[10.6125,31.9165],[10.635,31.8795],[10.527,31.741],[10.4485,31.7305],[10.386,31.7405],[10.312,31.703],[10.2125,31.566],[10.139,31.49],[10.1665,31.377],[10.294,31.0915],[10.275,31.041],[10.2975,30.8865],[10.272,30.8005],[10.202,30.6935],[10.0695,30.5085],[10.017,30.479],[9.944,30.3835],[9.8775,30.337],[9.832,30.337],[9.559,30.229],[9.4155,30.1875],[9.3915,30.1465],[9.415,30.0425],[9.5385,29.901],[9.78,29.425],[9.8715,29.0275],[9.9025,28.759],[9.833,28.286],[9.962,27.886],[9.943,27.836],[9.9425,27.7325],[9.8885,27.609],[9.863,27.5045],[9.8135,27.463],[9.7945,27.395],[9.7825,27.2565],[9.798,27.1745],[9.818,27.1495],[9.8625,26.9425],[9.9255,26.862],[9.902,26.743],[9.9135,26.647],[9.8635,26.52],[9.5125,26.385],[9.456,26.2535],[9.399,26.1955],[9.5375,26.0025],[9.751,25.725],[10.032,25.3595],[10.038,24.9655],[10.0975,24.881],[10.106,24.846],[10.153,24.784],[10.2165,24.6505],[10.3025,24.599],[10.3565,24.537],[10.542,24.534],[10.6335,24.5665],[10.706,24.573],[10.7665,24.5105],[10.821,24.5675],[10.8965,24.556],[10.9495,24.5325],[10.9815,24.491],[11.0775,24.411],[11.1385,24.391],[11.4225,24.2005],[11.6045,24.2645],[11.846,23.806],[11.9975,23.517],[12.576,23.396],[12.9315,23.3195],[13.417,23.214],[13.5435,23.1685],[13.6235,23.1165],[14.2345,22.6135],[14.6635,22.832],[14.9985,23.001],[16.0,23.4515],[17.7885,22.661],[17.819,22.6505],[18.1465,22.494],[18.63,22.262],[19.186,21.99],[19.6715,21.7505],[20.2605,21.4555],[20.7355,21.216],[21.2735,20.941],[21.6615,20.741],[22.268,20.4255],[22.831,20.129],[23.4995,19.772],[24.0,19.5],[24.0,20.0],[25.0,20.0],[25.0015,20.479],[25.002,21.086],[25.0035,21.6245],[25.004,21.943],[25.0,22.0]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/05/Flag_of_Libya.svg","name:en":"Libya","wikidata":"Q1016","ISO3166-1:alpha2":"LY","ISO3166-1:alpha3":"LBY","ISO3166-1:numeric":"434"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-130.805,-23.9355],[-130.7895,-23.9685],[-130.7345,-23.983],[-130.69,-23.954],[-130.6875,-23.892],[-130.7455,-23.8665],[-130.7945,-23.901],[-130.805,-23.9355]]],[[[-130.1795,-25.069],[-130.166,-25.102],[-130.1185,-25.1285],[-130.053,-25.116],[-130.034,-25.087],[-130.0475,-25.0375],[-130.1215,-25.008],[-130.1625,-25.031],[-130.1795,-25.069]]],[[[-128.4115,-24.3655],[-128.379,-24.4395],[-128.3085,-24.4725],[-128.254,-24.456],[-128.237,-24.4195],[-128.249,-24.3145],[-128.301,-24.2805],[-128.3525,-24.2885],[-128.393,-24.3165],[-128.4115,-24.3655]]],[[[-124.8515,-24.682],[-124.81,-24.7335],[-124.74,-24.7215],[-124.7185,-24.6885],[-124.731,-24.644],[-124.7675,-24.6235],[-124.827,-24.6345],[-124.8515,-24.682]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/88/Flag_of_the_Pitcairn_Islands.svg","name:en":"Pitcairn Islands","wikidata":"Q35672","ISO3166-1:alpha2":"PN","ISO3166-1:alpha3":"PCN","ISO3166-1:numeric":"612"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[7.0455,45.9225],[6.99,45.868],[6.939,45.847],[6.818,45.836],[6.808,45.7275],[6.8465,45.6905],[6.97,45.654],[6.9955,45.5755],[6.9905,45.531],[7.0485,45.4725],[7.1825,45.408],[7.1595,45.3595],[7.1095,45.3185],[7.136,45.28],[7.081,45.2245],[6.966,45.208],[6.9555,45.1805],[6.85,45.1275],[6.768,45.16],[6.6805,45.1405],[6.6275,45.107],[6.6735,45.02],[6.7485,45.016],[6.7635,44.971],[6.75,44.9075],[6.8645,44.8515],[6.932,44.8635],[7.0065,44.8395],[7.0,44.789],[7.041,44.7195],[6.948,44.654],[6.969,44.626],[6.9155,44.56],[6.8545,44.529],[6.876,44.4825],[6.9365,44.44],[6.897,44.3755],[7.0085,44.235],[7.07,44.233],[7.194,44.19],[7.2805,44.141],[7.357,44.1165],[7.575,44.153],[7.667,44.1335],[7.7185,44.0825],[7.652,43.9735],[7.585,43.954],[7.5615,43.899],[7.5115,43.885],[7.5075,43.842],[7.539,43.744],[7.6105,43.656],[7.631,43.5785],[7.733,43.578],[8.051,43.658],[8.2605,43.7395],[8.399,43.832],[8.4625,43.9085],[8.642,44.0655],[8.7105,44.16],[8.751,44.188],[8.8755,44.186],[9.1505,44.088],[9.2855,44.0635],[9.6865,43.858],[9.795,43.823],[9.867,43.819],[9.943,43.704],[9.7005,43.581],[9.6215,43.481],[9.559,43.193],[9.589,43.1755],[9.6415,42.955],[9.7165,42.8485],[9.7645,42.8085],[9.733,42.7035],[9.7825,42.6325],[9.815,42.435],[9.801,42.3815],[9.828,42.261],[9.92,42.19],[10.0,42.1635],[10.233,42.118],[10.3215,42.1095],[10.849,42.1125],[11.0365,42.041],[11.605,41.9215],[11.6465,41.8835],[12.033,41.599],[12.217,41.522],[12.2765,41.4745],[12.377,41.3685],[12.579,40.9225],[12.618,40.814],[12.6745,40.764],[12.872,40.683],[13.0415,40.6425],[13.7835,40.5035],[14.074,40.3555],[14.75,40.0835],[15.105,39.866],[15.168,39.837],[15.332,39.793],[15.5385,39.695],[15.5515,39.635],[15.5915,39.5705],[15.636,39.467],[15.6815,39.4125],[15.7625,39.343],[15.795,39.2505],[15.798,39.126],[15.831,39.04],[15.76,38.8575],[15.6775,38.8155],[15.605,38.74],[15.577,38.6805],[15.526,38.505],[15.2725,38.477],[15.3385,38.5355],[15.372,38.6105],[15.45,38.6755],[15.508,38.776],[15.5055,38.869],[15.4715,38.9295],[15.3835,38.9945],[15.3045,39.0185],[15.2365,39.0225],[15.117,39.001],[15.0575,38.9745],[14.964,38.8985],[14.9295,38.819],[14.876,38.7865],[14.7575,38.7825],[14.69,38.7645],[14.628,38.782],[14.487,38.786],[14.3845,38.757],[14.3145,38.756],[14.2305,38.736],[14.124,38.661],[14.0895,38.6005],[14.0805,38.5405],[14.1025,38.4565],[14.148,38.3995],[14.2315,38.35],[14.298,38.3325],[14.4085,38.3325],[14.4975,38.361],[14.62,38.3525],[14.0195,38.247],[13.636,38.304],[13.4555,38.3945],[13.348,38.425],[13.271,38.423],[13.0765,38.393],[12.8095,38.383],[12.7485,38.391],[12.6425,38.379],[12.5695,38.347],[12.4115,38.247],[12.286,38.2185],[12.0005,38.192],[11.9415,38.181],[11.856,38.1395],[11.791,38.063],[11.776,37.9715],[11.817,37.8615],[11.856,37.8185],[11.927,37.776],[12.223,37.6555],[12.2615,37.588],[12.3415,37.5135],[12.5005,37.4085],[12.618,37.3605],[12.929,37.303],[13.1895,37.189],[13.315,37.12],[13.523,37.015],[13.755,36.922],[14.362,36.613],[14.644,36.5195],[15.018,36.4455],[15.093,36.44],[15.1875,36.46],[15.2695,36.4945],[15.3515,36.5585],[15.3815,36.597],[15.567,36.9145],[15.5895,37.025],[15.5565,37.105],[15.5085,37.282],[15.44,37.57],[15.4615,37.6385],[15.547,37.778],[15.656,37.7315],[15.788,37.713],[15.897,37.7225],[16.0105,37.713],[16.1255,37.7265],[16.2435,37.7785],[16.295,37.823],[16.664,38.2045],[16.7675,38.29],[17.3675,38.8215],[17.414,38.8795],[17.4605,38.974],[17.461,39.0755],[17.4125,39.2295],[17.414,39.262],[17.9405,39.4335],[18.4685,39.6045],[18.5695,39.662],[18.64,39.7505],[18.77,40.0405],[18.784,40.097],[18.762,40.212],[18.6865,40.3515],[18.6405,40.412],[18.4055,40.6045],[18.23,40.769],[18.146,40.826],[18.016,40.879],[17.9,40.9055],[17.8135,40.945],[17.652,40.992],[17.538,41.062],[17.4895,41.1065],[17.3,41.21],[17.1685,41.263],[17.0445,41.296],[16.9915,41.324],[16.717,41.392],[16.5385,41.4675],[16.4695,41.781],[16.473,41.823],[16.44,41.9615],[16.4055,42.008],[16.323,42.0675],[16.2345,42.111],[16.1185,42.147],[16.007,42.166],[16.0175,42.253],[15.9665,42.346],[15.9125,42.3845],[15.8235,42.419],[15.693,42.421],[15.609,42.3995],[15.5285,42.34],[15.4225,42.3305],[15.0565,42.234],[14.8805,42.3375],[14.734,42.3845],[14.6135,42.4995],[14.443,42.5925],[14.306,42.712],[14.2055,42.8675],[14.1905,42.9325],[14.1355,43.055],[14.105,43.167],[14.041,43.3015],[13.962,43.4275],[13.906,43.5415],[13.8665,43.6515],[13.7275,43.764],[13.613,43.818],[13.4815,43.8325],[13.441,43.8485],[13.2425,43.983],[13.073,44.0895],[12.897,44.1545],[12.8325,44.1675],[12.7565,44.24],[12.6965,44.268],[12.6275,44.3295],[12.5735,44.4895],[12.566,44.575],[12.7925,44.8555],[12.832,44.9475],[12.8605,45.3515],[13.046,45.432],[13.173,45.4395],[13.2105,45.453],[13.312,45.546],[13.467,45.5895],[13.639,45.639],[13.7185,45.5945],[13.8515,45.585],[13.9185,45.6335],[13.8575,45.666],[13.784,45.7485],[13.6695,45.7995],[13.596,45.808],[13.5745,45.8435],[13.638,45.9365],[13.643,45.9825],[13.568,45.9685],[13.502,45.9805],[13.516,46.0625],[13.6445,46.1375],[13.6335,46.192],[13.444,46.2255],[13.3925,46.2825],[13.4495,46.335],[13.437,46.3545],[13.5655,46.398],[13.582,46.4305],[13.6935,46.444],[13.714,46.523],[13.5045,46.5665],[13.322,46.553],[13.193,46.5725],[13.0905,46.601],[12.9335,46.6095],[12.6905,46.657],[12.5485,46.659],[12.384,46.7165],[12.3575,46.775],[12.309,46.785],[12.306,46.8335],[12.2665,46.887],[12.211,46.8775],[12.1385,46.9565],[12.126,47.013],[12.2045,47.027],[12.227,47.082],[12.186,47.092],[12.02,47.047],[11.9315,47.037],[11.7645,46.9725],[11.627,47.0135],[11.538,46.984],[11.479,47.011],[11.4425,46.9765],[11.3825,46.9705],[11.3205,46.9925],[11.187,46.9695],[11.072,46.856],[11.021,46.7665],[10.814,46.775],[10.763,46.8205],[10.6625,46.8745],[10.5705,46.8425],[10.4695,46.855],[10.444,46.76],[10.3845,46.683],[10.4015,46.637],[10.4915,46.6115],[10.472,46.5435],[10.364,46.5555],[10.296,46.55],[10.2415,46.589],[10.224,46.629],[10.1275,46.605],[10.044,46.54],[10.0545,46.4645],[10.087,46.4215],[10.143,46.4285],[10.164,46.3905],[10.108,46.3515],[10.1165,46.314],[10.175,46.2545],[10.0715,46.218],[10.0555,46.266],[9.996,46.285],[9.996,46.3425],[9.9095,46.3795],[9.77,46.336],[9.7145,46.293],[9.6185,46.2875],[9.534,46.312],[9.4545,46.419],[9.4625,46.498],[9.3905,46.4735],[9.3735,46.504],[9.282,46.496],[9.2495,46.431],[9.28,46.4145],[9.2995,46.3265],[9.194,46.1785],[9.0725,46.118],[9.077,46.064],[9.017,46.05],[9.0195,45.93],[9.0745,45.9125],[9.031,45.8245],[8.949,45.8435],[8.925,45.904],[8.8305,45.988],[8.787,45.9915],[8.852,46.0755],[8.6125,46.1215],[8.5325,46.2185],[8.469,46.233],[8.428,46.2985],[8.4655,46.334],[8.466,46.4425],[8.438,46.464],[8.367,46.452],[8.291,46.409],[8.313,46.377],[8.222,46.33],[8.138,46.302],[8.081,46.2585],[8.139,46.226],[8.1655,46.177],[8.108,46.1115],[8.0345,46.101],[8.034,46.0435],[7.9885,45.996],[7.9085,45.997],[7.8635,45.9165],[7.7695,45.937],[7.735,45.924],[7.658,45.9765],[7.4785,45.9525],[7.383,45.8965],[7.3445,45.915],[7.1535,45.8795],[7.118,45.859],[7.0455,45.9225]],[[12.4455,41.902],[12.4515,41.9065],[12.4575,41.906],[12.4545,41.9],[12.4455,41.902]],[[12.4035,43.9525],[12.5065,43.9915],[12.516,43.941],[12.4875,43.8965],[12.4155,43.9],[12.4035,43.9525]]],[[[8.831,41.261],[8.789,41.2325],[8.4955,41.2735],[8.436,41.303],[8.3165,41.3235],[8.211,41.3075],[8.0955,41.255],[7.9875,41.141],[7.9075,40.9675],[7.869,40.759],[7.8725,40.5955],[7.905,40.4915],[8.0995,40.2885],[8.022,39.9935],[8.0205,39.84],[8.0465,39.787],[8.165,39.686],[8.119,39.4885],[8.1145,39.4295],[7.9815,39.246],[7.948,39.145],[7.98,39.0495],[8.1805,38.762],[8.252,38.697],[8.3265,38.666],[8.4155,38.656],[8.6525,38.6605],[8.874,38.673],[8.985,38.6995],[9.167,38.816],[9.5865,38.882],[9.7105,38.929],[9.83,39.0295],[9.8675,39.117],[9.894,39.28],[9.939,39.6935],[9.979,39.9075],[9.999,40.062],[10.058,40.404],[10.0865,40.471],[10.0835,40.5875],[10.015,40.78],[10.041,40.833],[10.0335,40.938],[9.9625,41.0385],[9.901,41.098],[9.8445,41.197],[9.784,41.2535],[9.7615,41.3065],[9.658,41.393],[9.631,41.4335],[9.45,41.4065],[9.3165,41.336],[9.27,41.292],[9.135,41.318],[8.831,41.261]]],[[[11.673,36.8045],[11.6895,36.734],[11.737,36.665],[11.82,36.585],[11.953,36.536],[12.079,36.5365],[12.1675,36.567],[12.265,36.6475],[12.3065,36.756],[12.296,36.84],[12.2645,36.8995],[12.1455,36.993],[12.0145,37.034],[11.933,37.0385],[11.815,37.0105],[11.714,36.933],[11.678,36.863],[11.673,36.8045]]],[[[12.072,35.558],[12.095,35.462],[12.1425,35.405],[12.2155,35.3675],[12.294,35.3505],[12.3795,35.3555],[12.5465,35.295],[12.653,35.2905],[12.716,35.3015],[12.8215,35.3615],[12.863,35.4185],[12.8785,35.4695],[12.8645,35.585],[12.828,35.652],[12.93,35.655],[12.991,35.6725],[13.079,35.734],[13.116,35.793],[13.128,35.8465],[13.1155,35.927],[13.0495,36.017],[12.9315,36.071],[12.801,36.072],[12.691,36.0285],[12.6365,35.9795],[12.5975,35.8795],[12.628,35.768],[12.6835,35.713],[12.5085,35.727],[12.4585,35.722],[12.3685,35.751],[12.234,35.736],[12.141,35.689],[12.102,35.6475],[12.072,35.558]]],[[[12.896,38.69],[12.9215,38.6065],[12.964,38.556],[13.0765,38.499],[13.158,38.488],[13.275,38.5045],[13.358,38.546],[13.407,38.588],[13.457,38.6865],[13.447,38.7725],[13.3795,38.857],[13.297,38.902],[13.2245,38.921],[13.142,38.92],[13.0635,38.9],[12.986,38.859],[12.939,38.814],[12.8975,38.724],[12.896,38.69]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/03/Flag_of_Italy.svg","name:en":"Italy","wikidata":"Q38","ISO3166-1:alpha2":"IT","ISO3166-1:alpha3":"ITA","ISO3166-1:numeric":"380"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[20.5945,41.877],[20.5585,41.8675],[20.573,41.788],[20.5205,41.7515],[20.517,41.663],[20.5545,41.5835],[20.4705,41.559],[20.469,41.4975],[20.5135,41.4375],[20.557,41.417],[20.5515,41.377],[20.498,41.322],[20.516,41.2305],[20.596,41.133],[20.596,41.092],[20.675,41.08],[20.7315,40.911],[20.8015,40.898],[20.848,40.9375],[20.9485,40.9225],[20.9775,40.8555],[21.1565,40.8575],[21.2065,40.884],[21.252,40.8625],[21.3635,40.8805],[21.417,40.9175],[21.5325,40.9075],[21.5665,40.8665],[21.649,40.9015],[21.707,40.943],[21.783,40.9285],[21.801,40.9745],[21.914,41.0505],[21.932,41.104],[22.0675,41.1565],[22.1285,41.1235],[22.2145,41.17],[22.416,41.1195],[22.618,41.134],[22.667,41.186],[22.706,41.143],[22.748,41.1665],[22.7645,41.322],[22.812,41.344],[22.9275,41.3385],[22.9655,41.3535],[22.952,41.4175],[22.9795,41.4465],[22.958,41.4945],[22.973,41.56],[22.9515,41.642],[23.0335,41.7225],[23.0085,41.769],[22.965,41.778],[22.928,41.862],[22.9025,41.876],[22.874,41.9535],[22.867,42.022],[22.798,42.0455],[22.6805,42.0635],[22.528,42.14],[22.434,42.258],[22.3605,42.311],[22.2915,42.3525],[22.195,42.34],[22.0655,42.2985],[22.032,42.2985],[21.9465,42.3445],[21.8925,42.3005],[21.8515,42.3255],[21.774,42.2655],[21.6945,42.233],[21.587,42.263],[21.569,42.2505],[21.445,42.278],[21.3605,42.222],[21.3025,42.1415],[21.318,42.107],[21.2215,42.0995],[21.162,42.197],[21.104,42.206],[21.0265,42.1525],[20.945,42.138],[20.8835,42.096],[20.791,42.083],[20.7485,42.0265],[20.7545,41.947],[20.7765,41.9185],[20.728,41.8645],[20.5945,41.877]]]},"properties":{"flag":"https://upload.wikimedia.org/wikipedia/commons/7/79/Flag_of_North_Macedonia.svg","name:en":"North Macedonia","wikidata":"Q221","ISO3166-1:alpha2":"MK","ISO3166-1:alpha3":"MKD","ISO3166-1:numeric":"807"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[19.226,43.528],[19.153,43.5365],[19.062,43.505],[18.9885,43.5555],[18.95,43.5035],[18.961,43.4605],[19.0045,43.443],[19.04,43.3655],[19.0835,43.319],[19.009,43.251],[18.9555,43.2875],[18.96,43.3175],[18.911,43.3605],[18.852,43.324],[18.733,43.281],[18.6675,43.204],[18.6485,43.147],[18.6605,43.0375],[18.5435,43.0325],[18.498,42.9955],[18.4825,42.9285],[18.509,42.8865],[18.458,42.8255],[18.4735,42.757],[18.528,42.724],[18.569,42.656],[18.5,42.588],[18.438,42.5555],[18.434,42.484],[18.524,42.4205],[18.54,42.39],[18.4195,42.2095],[18.6485,42.121],[18.876,41.953],[18.9285,41.8635],[19.2645,41.7495],[19.373,41.8445],[19.347,41.9135],[19.377,41.974],[19.3705,42.037],[19.4025,42.1035],[19.281,42.1805],[19.349,42.2405],[19.414,42.351],[19.468,42.3895],[19.563,42.5015],[19.616,42.5435],[19.61,42.5755],[19.662,42.628],[19.723,42.661],[19.7755,42.591],[19.7435,42.545],[19.7665,42.5015],[19.8315,42.466],[19.9435,42.5175],[20.0035,42.5095],[20.0175,42.5435],[20.0765,42.556],[20.0755,42.621],[20.112,42.6555],[20.033,42.6975],[20.0245,42.765],[20.098,42.7765],[20.1895,42.7465],[20.2505,42.759],[20.298,42.836],[20.353,42.8335],[20.3495,42.895],[20.2845,42.9315],[20.1375,42.98],[20.0595,42.9945],[19.992,43.052],[19.9605,43.1095],[19.84,43.0945],[19.771,43.163],[19.686,43.168],[19.619,43.2295],[19.5465,43.2455],[19.528,43.316],[19.461,43.3445],[19.449,43.3875],[19.35,43.4155],[19.223,43.482],[19.226,43.528]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/64/Flag_of_Montenegro.svg","name:en":"Montenegro","wikidata":"Q236","ISO3166-1:alpha2":"ME","ISO3166-1:alpha3":"MNE","ISO3166-1:numeric":"499"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[21.587,42.263],[21.5255,42.342],[21.547,42.3655],[21.6205,42.3785],[21.646,42.4145],[21.6225,42.453],[21.69,42.502],[21.706,42.5475],[21.7475,42.559],[21.7355,42.604],[21.79,42.651],[21.7535,42.701],[21.6745,42.687],[21.5865,42.704],[21.4795,42.7475],[21.393,42.745],[21.435,42.8725],[21.361,42.8725],[21.283,42.8955],[21.2345,42.96],[21.235,43.01],[21.1525,43.0405],[21.137,43.1135],[21.0505,43.1075],[20.967,43.1245],[20.8635,43.165],[20.8795,43.223],[20.8285,43.266],[20.793,43.245],[20.728,43.2485],[20.6715,43.2085],[20.615,43.2045],[20.6115,43.1695],[20.685,43.123],[20.6715,43.039],[20.5815,43.013],[20.4865,42.929],[20.5355,42.889],[20.4415,42.834],[20.353,42.8335],[20.298,42.836],[20.2505,42.759],[20.1895,42.7465],[20.098,42.7765],[20.0245,42.765],[20.033,42.6975],[20.112,42.6555],[20.0755,42.621],[20.0765,42.556],[20.165,42.5065],[20.2275,42.4195],[20.2575,42.319],[20.327,42.3275],[20.4565,42.2725],[20.524,42.2105],[20.568,42.121],[20.5545,42.0825],[20.5945,42.043],[20.6265,41.961],[20.5765,41.917],[20.5945,41.877],[20.728,41.8645],[20.7765,41.9185],[20.7545,41.947],[20.7485,42.0265],[20.791,42.083],[20.8835,42.096],[20.945,42.138],[21.0265,42.1525],[21.104,42.206],[21.162,42.197],[21.2215,42.0995],[21.318,42.107],[21.3025,42.1415],[21.3605,42.222],[21.445,42.278],[21.569,42.2505],[21.587,42.263]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/1f/Flag_of_Kosovo.svg","name:en":"Kosovo","wikidata":"Q1246","ISO3166-1:alpha2":"XK","ISO3166-1:alpha3":"XKX"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[22.3605,42.311],[22.4555,42.3335],[22.478,42.3935],[22.52,42.3975],[22.559,42.485],[22.5475,42.507],[22.438,42.572],[22.4675,42.641],[22.487,42.749],[22.441,42.806],[22.5355,42.882],[22.6195,42.896],[22.6815,42.872],[22.7445,42.886],[22.78,42.932],[22.7855,42.9825],[22.8985,43.036],[22.919,43.0775],[22.983,43.111],[23.0055,43.186],[22.8965,43.227],[22.8485,43.2755],[22.8275,43.329],[22.7665,43.3775],[22.6795,43.3945],[22.6605,43.426],[22.5975,43.4355],[22.5335,43.471],[22.4875,43.5685],[22.492,43.639],[22.408,43.696],[22.3955,43.7595],[22.357,43.8095],[22.409,43.9395],[22.4095,44.004],[22.5225,44.019],[22.535,44.052],[22.6215,44.0635],[22.6105,44.1065],[22.6475,44.2045],[22.6745,44.216],[22.6835,44.2715],[22.558,44.309],[22.5235,44.3405],[22.475,44.4805],[22.5505,44.4825],[22.5645,44.5355],[22.619,44.552],[22.699,44.5165],[22.7545,44.565],[22.6695,44.619],[22.5695,44.637],[22.4845,44.707],[22.4295,44.71],[22.371,44.6725],[22.333,44.676],[22.2735,44.623],[22.184,44.482],[22.1295,44.474],[22.037,44.542],[21.9955,44.632],[21.842,44.6555],[21.642,44.66],[21.608,44.7335],[21.537,44.7725],[21.425,44.7735],[21.3585,44.8215],[21.3715,44.8665],[21.4855,44.868],[21.56,44.889],[21.5465,44.9305],[21.4065,44.9785],[21.36,45.0205],[21.4475,45.0575],[21.4785,45.1215],[21.5275,45.138],[21.483,45.192],[21.203,45.2655],[21.18,45.3125],[21.0965,45.296],[21.0635,45.332],[21.0165,45.3245],[20.9605,45.367],[20.879,45.4545],[20.769,45.5],[20.8325,45.536],[20.766,45.6125],[20.805,45.657],[20.8025,45.738],[20.7005,45.7505],[20.6605,45.829],[20.516,45.892],[20.4835,45.9535],[20.4055,45.965],[20.3485,45.9995],[20.346,46.0475],[20.273,46.0985],[20.2645,46.1265],[20.182,46.16],[20.1375,46.144],[20.096,46.177],[19.935,46.176],[19.818,46.128],[19.6965,46.1875],[19.6315,46.1695],[19.5675,46.1785],[19.5025,46.142],[19.4155,46.0455],[19.364,46.052],[19.2855,45.997],[19.1475,45.996],[19.1045,46.04],[19.0655,46.0],[19.0795,45.9635],[19.006,45.9585],[18.962,45.926],[18.89,45.922],[18.8855,45.859],[18.9185,45.819],[18.8735,45.7845],[18.894,45.7515],[18.9585,45.7675],[18.9795,45.7395],[18.9285,45.691],[18.936,45.6385],[18.9085,45.5675],[18.984,45.537],[19.096,45.5205],[19.0825,45.4885],[18.997,45.491],[18.987,45.455],[19.0235,45.403],[18.9715,45.377],[19.093,45.335],[19.1045,45.299],[19.26,45.243],[19.4205,45.2335],[19.4475,45.196],[19.418,45.1665],[19.295,45.173],[19.187,45.166],[19.142,45.13],[19.077,45.1135],[19.106,45.079],[19.0925,44.995],[19.143,44.941],[18.989,44.906],[19.019,44.855],[19.065,44.8615],[19.175,44.9215],[19.2255,44.8985],[19.3095,44.908],[19.3695,44.8805],[19.318,44.8215],[19.303,44.688],[19.1835,44.573],[19.19,44.547],[19.1285,44.52],[19.147,44.415],[19.103,44.3715],[19.1325,44.317],[19.2285,44.2645],[19.328,44.269],[19.3605,44.1835],[19.491,44.1165],[19.512,44.081],[19.5995,44.0705],[19.6185,44.0135],[19.5645,43.9995],[19.5265,43.956],[19.3885,43.9605],[19.3105,43.996],[19.235,44.0085],[19.2535,43.951],[19.294,43.92],[19.395,43.798],[19.4655,43.7645],[19.5185,43.7145],[19.532,43.668],[19.481,43.5715],[19.3575,43.606],[19.273,43.5965],[19.226,43.528],[19.223,43.482],[19.35,43.4155],[19.449,43.3875],[19.461,43.3445],[19.528,43.316],[19.5465,43.2455],[19.619,43.2295],[19.686,43.168],[19.771,43.163],[19.84,43.0945],[19.9605,43.1095],[19.992,43.052],[20.0595,42.9945],[20.1375,42.98],[20.2845,42.9315],[20.3495,42.895],[20.353,42.8335],[20.4415,42.834],[20.5355,42.889],[20.4865,42.929],[20.5815,43.013],[20.6715,43.039],[20.685,43.123],[20.6115,43.1695],[20.615,43.2045],[20.6715,43.2085],[20.728,43.2485],[20.793,43.245],[20.8285,43.266],[20.8795,43.223],[20.8635,43.165],[20.967,43.1245],[21.0505,43.1075],[21.137,43.1135],[21.1525,43.0405],[21.235,43.01],[21.2345,42.96],[21.283,42.8955],[21.361,42.8725],[21.435,42.8725],[21.393,42.745],[21.4795,42.7475],[21.5865,42.704],[21.6745,42.687],[21.7535,42.701],[21.79,42.651],[21.7355,42.604],[21.7475,42.559],[21.706,42.5475],[21.69,42.502],[21.6225,42.453],[21.646,42.4145],[21.6205,42.3785],[21.547,42.3655],[21.5255,42.342],[21.587,42.263],[21.6945,42.233],[21.774,42.2655],[21.8515,42.3255],[21.8925,42.3005],[21.9465,42.3445],[22.032,42.2985],[22.0655,42.2985],[22.195,42.34],[22.2915,42.3525],[22.3605,42.311]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/ff/Flag_of_Serbia.svg","name:en":"Serbia","wikidata":"Q403","ISO3166-1:alpha2":"RS","ISO3166-1:alpha3":"SRB","ISO3166-1:numeric":"688"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.7565,32.745],[35.633,32.6855],[35.5735,32.641],[35.577,32.5525],[35.554,32.513],[35.555,32.389],[35.555,32.386],[35.5565,32.3685],[35.559,32.3365],[35.5655,32.258],[35.5705,32.1915],[35.533,32.108],[35.519,32.0355],[35.5445,31.97],[35.5245,31.907],[35.549,31.8655],[35.5555,31.7515],[35.5275,31.7215],[35.4915,31.621],[35.475,31.4965],[35.4565,31.3625],[35.4065,31.2815],[35.4,31.239],[35.4495,31.1575],[35.4545,31.1045],[35.4275,31.05],[35.4165,30.951],[35.371,30.927],[35.333,30.867],[35.341,30.8155],[35.296,30.762],[35.2615,30.657],[35.204,30.582],[35.193,30.499],[35.162,30.4415],[35.192,30.346],[35.146,30.282],[35.1445,30.1625],[35.161,30.1345],[35.146,30.063],[35.1125,30.039],[35.0785,29.957],[35.0845,29.8855],[35.03,29.772],[35.012,29.697],[35.0145,29.639],[34.9785,29.577],[34.9665,29.519],[34.921,29.4535],[34.8845,29.375],[34.9495,29.359],[35.737,29.238],[35.823,29.2195],[36.073,29.1835],[36.152,29.232],[36.2125,29.286],[36.371,29.3975],[36.4095,29.4055],[36.5275,29.5145],[36.5485,29.5605],[36.626,29.639],[36.681,29.7495],[36.736,29.789],[36.785,29.868],[36.865,29.8855],[37.5055,30.001],[37.521,30.0425],[37.6655,30.3325],[37.997,30.5005],[37.933,30.5765],[37.5835,30.928],[37.283,31.228],[37.006,31.5005],[37.4035,31.6015],[38.04,31.7635],[38.4785,31.872],[39.0065,32.0005],[39.201,32.1545],[39.3015,32.2305],[39.26,32.355],[39.043,32.3035],[38.9855,32.4775],[39.086,32.502],[38.88,33.119],[38.7935,33.375],[38.7695,33.358],[38.3505,33.1415],[37.8885,32.894],[37.818,32.9055],[37.7695,32.8785],[37.762,32.788],[37.741,32.762],[37.5205,32.7175],[37.444,32.643],[37.4245,32.607],[37.36,32.56],[37.2555,32.572],[37.2065,32.5575],[37.105,32.4545],[37.065,32.4285],[36.9625,32.44],[36.881,32.334],[36.838,32.3115],[36.71,32.3165],[36.6895,32.3345],[36.512,32.357],[36.407,32.377],[36.3055,32.4595],[36.2065,32.5195],[36.1225,32.5245],[36.0785,32.5135],[36.0625,32.576],[36.0295,32.597],[36.027,32.6535],[35.967,32.6625],[35.936,32.718],[35.882,32.7145],[35.7865,32.7475],[35.7565,32.745]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/c0/Flag_of_Jordan.svg","name:en":"Jordan","wikidata":"Q810","ISO3166-1:alpha2":"JO","ISO3166-1:alpha3":"JOR","ISO3166-1:numeric":"400"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[38.7935,33.375],[38.88,33.119],[39.086,32.502],[38.9855,32.4775],[39.043,32.3035],[39.26,32.355],[39.3015,32.2305],[39.201,32.1545],[39.973,32.0245],[40.4135,31.948],[40.796,31.736],[41.4405,31.373],[42.0855,31.1115],[42.6585,30.7065],[43.2445,30.2865],[43.7065,29.9505],[44.0405,29.705],[44.5135,29.3545],[44.727,29.1915],[44.8335,29.187],[45.3745,29.142],[45.725,29.1195],[46.4245,29.0585],[46.5525,29.1005],[46.6605,29.196],[46.7675,29.322],[46.7845,29.3555],[46.867,29.437],[46.9045,29.525],[46.998,29.6635],[47.007,29.722],[47.0815,29.8515],[47.1395,29.987],[47.1895,30.0285],[47.2875,30.0585],[47.371,30.1035],[47.7075,30.1035],[48.023,29.9785],[48.065,30.021],[48.1515,30.017],[48.318,29.881],[48.3835,29.8045],[48.464,29.757],[48.5235,29.707],[48.5815,29.638],[48.634,29.669],[48.7065,29.6855],[48.7885,29.7305],[48.859,29.8165],[48.802,29.8245],[48.68,29.888],[48.611,29.9365],[48.508,29.9675],[48.4515,30.0075],[48.4485,30.0595],[48.386,30.1335],[48.415,30.166],[48.405,30.2165],[48.278,30.3325],[48.1955,30.336],[48.176,30.4155],[48.136,30.444],[48.0285,30.4775],[48.04,30.5445],[48.0435,30.724],[48.0355,30.998],[47.8705,31.01],[47.8,31.0005],[47.6875,31.003],[47.6855,31.395],[47.866,31.7835],[47.8065,31.842],[47.8095,31.8815],[47.7315,31.9395],[47.6975,32.022],[47.646,32.06],[47.5995,32.1165],[47.529,32.142],[47.5655,32.193],[47.4625,32.321],[47.4655,32.3915],[47.378,32.474],[47.282,32.4885],[47.247,32.4635],[47.1685,32.4575],[47.111,32.4895],[46.956,32.5975],[46.887,32.632],[46.7205,32.786],[46.656,32.8075],[46.5185,32.898],[46.4155,32.9415],[46.3125,32.969],[46.244,32.97],[46.1735,32.9535],[46.094,32.985],[46.148,33.0505],[46.0845,33.0915],[46.1235,33.116],[46.1985,33.1915],[46.19,33.264],[46.134,33.292],[46.078,33.3575],[46.008,33.5045],[45.954,33.4905],[45.863,33.493],[45.961,33.5615],[45.858,33.632],[45.753,33.593],[45.7715,33.637],[45.6745,33.699],[45.648,33.7635],[45.501,33.951],[45.451,33.945],[45.4205,33.9875],[45.467,34.019],[45.515,34.1125],[45.5815,34.146],[45.5555,34.198],[45.583,34.248],[45.562,34.3315],[45.4675,34.379],[45.44,34.459],[45.5255,34.493],[45.515,34.555],[45.577,34.5585],[45.714,34.541],[45.7395,34.5865],[45.702,34.6945],[45.658,34.7185],[45.7225,34.838],[45.795,34.856],[45.7885,34.9115],[45.865,34.8965],[45.8925,34.95],[45.8805,35.038],[45.944,35.091],[46.0675,35.043],[46.1915,35.111],[46.157,35.164],[46.194,35.211],[46.1125,35.235],[46.1585,35.2845],[46.1455,35.313],[46.0385,35.4055],[45.9855,35.496],[46.0135,35.574],[46.014,35.6845],[46.0355,35.7055],[46.101,35.6875],[46.165,35.689],[46.2335,35.715],[46.347,35.787],[46.34,35.82],[46.2895,35.827],[46.2125,35.795],[46.1665,35.8005],[46.1375,35.8435],[46.0585,35.856],[45.9555,35.823],[45.896,35.8375],[45.8315,35.8095],[45.763,35.8],[45.7095,35.8825],[45.6655,35.927],[45.5555,35.9995],[45.4535,35.9945],[45.402,35.971],[45.343,36.0095],[45.3815,36.084],[45.327,36.1395],[45.3365,36.204],[45.3025,36.275],[45.261,36.3015],[45.2765,36.3765],[45.2375,36.433],[45.1505,36.4045],[45.0815,36.4255],[45.0265,36.6005],[45.071,36.63],[45.0615,36.695],[45.0305,36.7405],[44.9725,36.7495],[44.9425,36.7845],[44.845,36.7785],[44.8385,36.816],[44.8945,36.8535],[44.9125,36.9165],[44.8825,36.954],[44.904,37.004],[44.8545,37.047],[44.817,37.04],[44.7775,37.0965],[44.7845,37.144],[44.769,37.164],[44.682,37.171],[44.627,37.19],[44.576,37.1625],[44.5115,37.101],[44.392,37.0505],[44.3065,36.97],[44.2675,36.976],[44.235,37.042],[44.185,37.098],[44.222,37.158],[44.2765,37.165],[44.2685,37.2445],[44.1245,37.323],[44.0145,37.322],[43.9165,37.2235],[43.694,37.2355],[43.6205,37.2215],[43.578,37.25],[43.4745,37.253],[43.3275,37.326],[43.3015,37.3035],[43.2405,37.345],[43.151,37.371],[43.072,37.3665],[42.9635,37.3155],[42.8975,37.3285],[42.8245,37.371],[42.78,37.3755],[42.73,37.3175],[42.6175,37.2185],[42.579,37.146],[42.464,37.142],[42.3515,37.1055],[42.3745,37.073],[42.0,36.7385],[41.9705,36.7005],[41.817,36.5875],[41.402,36.524],[41.294,36.365],[41.256,36.06],[41.3695,35.8435],[41.382,35.6255],[41.262,35.456],[41.275,35.385],[41.2425,35.2855],[41.218,35.132],[41.2275,34.974],[41.2285,34.7785],[41.1225,34.6535],[41.0165,34.448],[40.978,34.398],[40.64,34.315],[39.9825,33.985],[39.3045,33.64],[38.7935,33.375]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/f6/Flag_of_Iraq.svg","name:en":"Iraq","wikidata":"Q796","ISO3166-1:alpha2":"IQ","ISO3166-1:alpha3":"IRQ","ISO3166-1:numeric":"368"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.6745,34.686],[35.6245,34.665],[35.541,34.5865],[35.518,34.519],[35.5205,34.4675],[35.443,34.3835],[35.414,34.3205],[35.387,34.1735],[35.393,34.091],[35.28,34.0245],[35.2495,33.9835],[35.2275,33.9095],[35.2355,33.84],[35.201,33.7775],[35.1325,33.6145],[35.054,33.5225],[35.0165,33.4185],[34.9615,33.3315],[34.953,33.261],[34.8825,33.172],[35.0795,33.096],[35.193,33.0855],[35.2935,33.108],[35.354,33.0575],[35.4315,33.0655],[35.5035,33.0895],[35.527,33.1415],[35.5465,33.238],[35.5835,33.2675],[35.658,33.2745],[35.7155,33.3255],[35.7735,33.3355],[35.827,33.405],[35.8765,33.426],[35.9535,33.488],[35.952,33.534],[36.029,33.549],[36.059,33.5795],[36.019,33.6135],[35.946,33.6375],[35.9685,33.715],[36.067,33.8245],[36.1525,33.8555],[36.202,33.833],[36.246,33.8595],[36.323,33.833],[36.3965,33.8335],[36.356,33.8815],[36.2835,33.918],[36.328,33.978],[36.4405,34.0585],[36.476,34.0505],[36.564,34.1345],[36.595,34.1895],[36.5805,34.2775],[36.5285,34.37],[36.572,34.405],[36.4635,34.4645],[36.4435,34.5055],[36.3805,34.508],[36.4095,34.6115],[36.3115,34.683],[36.2975,34.6355],[36.1895,34.6375],[36.036,34.6285],[36.0055,34.642],[35.8395,34.6005],[35.754,34.662],[35.6745,34.686]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/5/59/Flag_of_Lebanon.svg","name:en":"Lebanon","wikidata":"Q822","ISO3166-1:alpha2":"LB","ISO3166-1:alpha3":"LBN","ISO3166-1:numeric":"422"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[41.907,3.9825],[41.7165,3.7155],[41.575,3.5245],[41.312,3.141],[41.1465,2.9855],[40.989,2.8285],[40.9915,2.1765],[40.9935,1.231],[40.9945,0.9305],[40.9945,0.5165],[40.9935,-0.1995],[40.9935,-0.834],[41.2765,-1.2165],[41.559,-1.5985],[41.5595,-1.662],[41.718,-1.803],[41.8185,-1.6625],[41.8365,-1.623],[41.9475,-1.464],[41.9965,-1.384],[42.0455,-1.339],[42.1555,-1.2025],[42.198,-1.1385],[42.258,-1.078],[42.4705,-0.819],[42.529,-0.724],[42.604,-0.658],[42.631,-0.6065],[42.752,-0.4605],[42.7815,-0.3895],[43.026,-0.1475],[43.2525,0.0975],[43.5275,0.4015],[43.6855,0.558],[43.796,0.658],[43.8845,0.7265],[43.9135,0.7595],[44.1,0.904],[44.1475,0.9325],[44.209,1.001],[44.2745,1.0465],[44.304,1.0885],[44.484,1.2455],[44.693,1.411],[44.889,1.548],[44.988,1.6125],[45.209,1.743],[45.3135,1.785],[45.4455,1.8525],[45.876,2.1095],[45.9985,2.1765],[46.16,2.294],[46.2475,2.376],[46.3595,2.511],[46.456,2.5905],[46.4945,2.647],[46.585,2.73],[46.7095,2.8295],[46.837,2.9505],[46.939,3.06],[47.0755,3.2255],[47.231,3.3835],[47.3605,3.531],[47.457,3.6215],[47.62,3.788],[47.7355,3.9325],[47.9015,4.1085],[48.003,4.2305],[48.0755,4.302],[48.156,4.4065],[48.3265,4.6965],[48.3555,4.763],[48.4375,4.8915],[48.583,5.084],[48.6995,5.231],[48.814,5.3885],[48.8965,5.5295],[49.088,5.824],[49.1645,5.9715],[49.2365,6.131],[49.279,6.2825],[49.2785,6.3645],[49.335,6.5175],[49.3955,6.662],[49.5405,6.9025],[49.639,7.052],[49.75,7.1985],[49.838,7.328],[49.925,7.495],[49.964,7.593],[50.0035,7.6485],[50.0365,7.781],[50.0285,7.852],[50.1095,7.934],[50.187,7.9775],[50.284,8.0675],[50.32,8.1305],[50.342,8.2085],[50.409,8.3195],[50.4915,8.4155],[50.521,8.48],[50.5365,8.5685],[50.576,8.6385],[50.6095,8.7705],[50.7775,8.9255],[50.821,8.988],[50.847,9.061],[50.8545,9.1235],[50.902,9.165],[51.011,9.33],[51.0465,9.429],[51.046,9.505],[51.014,9.5845],[51.048,9.7865],[51.0955,9.94],[51.1035,9.9955],[51.0925,10.088],[51.1075,10.203],[51.152,10.2145],[51.216,10.1825],[51.2705,10.172],[51.401,10.1645],[51.471,10.1865],[51.5465,10.255],[51.612,10.391],[51.618,10.4605],[51.598,10.5325],[51.527,10.628],[51.4625,10.667],[51.349,10.683],[51.3335,10.742],[51.322,10.95],[51.369,11.058],[51.3865,11.1435],[51.3735,11.214],[51.333,11.283],[51.2975,11.315],[51.3305,11.444],[51.4035,11.529],[51.4405,11.5915],[51.4595,11.703],[51.4905,11.796],[51.475,11.913],[51.4225,11.985],[51.3415,12.032],[51.255,12.043],[51.2035,12.065],[51.1115,12.0725],[51.036,12.112],[50.93,12.1405],[50.882,12.1695],[50.7695,12.189],[50.6965,12.173],[50.646,12.1435],[50.5485,12.1265],[50.475,12.0845],[50.41,12.0295],[50.3585,11.95],[50.3255,11.849],[50.215,11.7995],[50.166,11.7685],[50.012,11.706],[49.946,11.7165],[49.887,11.708],[49.79,11.6615],[49.7035,11.6805],[49.632,11.673],[49.479,11.6355],[49.442,11.616],[49.351,11.533],[49.2695,11.534],[49.178,11.495],[48.9395,11.4425],[48.837,11.4825],[48.688,11.524],[48.4875,11.5075],[48.419,11.485],[48.3635,11.4815],[48.2725,11.453],[48.2015,11.4035],[48.1605,11.3905],[48.074,11.3295],[47.991,11.319],[47.921,11.3275],[47.85,11.316],[47.798,11.323],[47.7095,11.307],[47.6305,11.3605],[47.5615,11.381],[47.3865,11.3755],[47.2935,11.4245],[47.2245,11.428],[47.129,11.3925],[47.068,11.333],[47.032,11.227],[46.9655,11.1575],[46.8205,11.0895],[46.746,11.0275],[46.576,10.929],[46.428,10.892],[46.3375,10.9575],[46.234,10.984],[46.095,10.9655],[46.045,10.975],[45.872,11.0585],[45.7675,11.066],[45.68,11.039],[45.5735,10.964],[45.4845,10.931],[45.3895,10.8615],[45.3345,10.865],[45.235,10.843],[45.1735,10.7995],[45.1075,10.727],[44.9965,10.688],[44.8665,10.611],[44.747,10.6195],[44.582,10.5845],[44.391,10.6055],[44.32,10.6695],[44.2285,10.7295],[44.1735,10.78],[44.088,10.836],[43.9455,10.962],[43.8355,11.0875],[43.702,11.314],[43.707,11.4135],[43.6715,11.4995],[43.666,11.5615],[43.6405,11.62],[43.5785,11.682],[43.469,11.716],[43.4255,11.7115],[43.3445,11.6225],[43.228,11.412],[42.967,10.997],[42.9615,10.9845],[42.93,10.947],[42.766,10.7215],[42.719,10.6475],[42.7085,10.578],[42.747,10.512],[42.792,10.4035],[42.833,10.278],[42.8875,10.1895],[43.036,10.034],[43.086,9.908],[43.15,9.899],[43.257,9.843],[43.298,9.606],[43.33,9.6015],[43.4055,9.545],[43.439,9.447],[43.5155,9.3815],[43.643,9.357],[43.9965,9.0],[44.69,8.7725],[45.2675,8.5795],[45.6265,8.4625],[46.2255,8.2635],[46.993,8.0015],[47.9825,8.0],[47.557,7.561],[47.3375,7.3825],[46.873,6.9075],[46.511,6.5205],[45.845,5.9565],[45.411,5.5365],[44.9985,4.946],[44.978,4.9235],[44.625,4.933],[44.549,4.926],[44.385,4.941],[44.202,4.9425],[44.154,4.9505],[44.0315,4.9435],[43.809,4.902],[43.552,4.8355],[43.4345,4.7945],[43.232,4.695],[43.0795,4.602],[43.026,4.5415],[43.0075,4.468],[42.9695,4.403],[42.8975,4.318],[42.825,4.267],[42.7355,4.2645],[42.669,4.2495],[42.587,4.216],[42.37,4.1895],[42.194,4.179],[42.0875,4.179],[42.0005,4.0935],[41.9465,4.0555],[41.943,4.0165],[41.907,3.9825]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/a/a0/Flag_of_Somalia.svg","name:en":"Somalia","wikidata":"Q1045","ISO3166-1:alpha2":"SO","ISO3166-1:alpha3":"SOM","ISO3166-1:numeric":"706"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[55.2115,22.7055],[55.4815,22.287],[55.6665,22.0],[55.4795,21.438],[55.3355,21.007],[55.1495,20.453],[55.0,20.0],[54.644,19.8815],[54.209,19.737],[53.667,19.5565],[52.917,19.3065],[52.423,19.1415],[52.0,19.0],[52.2835,18.404],[52.5005,17.9455],[52.782,17.3495],[52.746,17.2945],[52.8125,17.2855],[53.1105,16.647],[53.189,16.465],[53.2725,16.5025],[53.3985,16.536],[53.5225,16.5565],[53.6275,16.5525],[53.7705,16.604],[53.8585,16.686],[53.9965,16.7035],[54.065,16.7305],[54.178,16.808],[54.31,16.8275],[54.543,16.8295],[54.5975,16.7895],[54.688,16.751],[54.8295,16.7375],[55.0935,16.8125],[55.138,16.8345],[55.233,16.906],[55.278,16.958],[55.3575,17.0165],[55.458,17.168],[55.472,17.2355],[55.5945,17.262],[55.8045,17.281],[55.8655,17.272],[55.947,17.2885],[56.017,17.2785],[56.126,17.2965],[56.2495,17.3],[56.3135,17.287],[56.387,17.2945],[56.475,17.317],[56.5355,17.351],[56.5805,17.402],[56.606,17.465],[56.608,17.532],[56.5615,17.859],[56.612,17.9185],[56.6925,17.9775],[56.725,18.014],[56.7555,18.0805],[56.7605,18.157],[56.799,18.236],[56.819,18.3485],[56.8435,18.4255],[56.842,18.489],[56.927,18.5865],[56.9735,18.615],[57.1085,18.6675],[57.323,18.721],[57.485,18.7375],[57.682,18.737],[57.757,18.746],[57.912,18.7965],[58.0045,18.8675],[58.05,18.9455],[58.052,19.061],[58.014,19.1315],[58.0125,19.188],[57.959,19.2935],[57.9735,19.403],[57.963,19.4635],[57.922,19.56],[57.939,19.6475],[57.982,19.6975],[58.007,19.7665],[57.992,19.876],[58.017,19.9335],[58.0475,20.123],[58.2705,20.137],[58.525,19.9855],[58.5975,19.958],[58.675,19.9575],[58.748,19.983],[58.8585,20.058],[58.958,20.1595],[59.0885,20.333],[59.1595,20.399],[59.1905,20.498],[59.18,20.5665],[59.0405,20.96],[59.198,21.117],[59.3025,21.194],[59.445,21.2545],[59.513,21.309],[59.5615,21.4045],[59.6625,21.5775],[59.697,21.671],[59.7565,21.731],[59.8555,21.881],[59.884,21.989],[59.989,22.115],[60.0195,22.181],[60.036,22.256],[60.054,22.4435],[60.035,22.5275],[59.9805,22.643],[59.9275,22.697],[59.8385,22.735],[59.755,22.737],[59.6185,22.7645],[59.5685,22.803],[59.4345,22.9505],[59.3755,23.071],[59.332,23.118],[59.259,23.1605],[59.2085,23.2295],[59.1905,23.285],[59.132,23.3595],[59.1085,23.4195],[59.0445,23.4855],[58.9915,23.5195],[58.9645,23.589],[58.9085,23.664],[58.7165,23.7955],[58.571,23.8735],[58.2205,24.046],[58.099,24.068],[57.923,24.039],[57.77,23.9955],[57.688,23.9795],[57.599,24.023],[57.542,24.0325],[57.3675,24.082],[57.2775,24.116],[57.1,24.252],[57.005,24.374],[56.9275,24.491],[56.839,24.5775],[56.791,24.66],[56.7365,24.7015],[56.691,24.7845],[56.6565,24.8785],[56.594,25.009],[56.3235,24.973],[56.349,24.933],[56.3255,24.8985],[56.2595,24.86],[56.2055,24.8505],[56.201,24.785],[56.1195,24.7345],[56.0675,24.7415],[56.036,24.811],[55.9785,24.8775],[56.042,24.8865],[56.059,24.9495],[56.007,24.9945],[55.961,25.006],[55.911,24.9655],[55.8515,24.966],[55.8125,24.911],[55.8145,24.7995],[55.8315,24.78],[55.8365,24.6715],[55.816,24.6155],[55.7675,24.5725],[55.765,24.5295],[55.834,24.4095],[55.834,24.3275],[55.791,24.2795],[55.777,24.2345],[55.8335,24.201],[55.9545,24.2225],[55.9605,24.1705],[56.0175,24.0665],[55.9025,24.047],[55.833,24.0145],[55.782,24.056],[55.731,24.058],[55.633,24.02],[55.495,23.9535],[55.485,23.94],[55.5335,23.8495],[55.533,23.756],[55.569,23.7205],[55.5725,23.6295],[55.447,23.457],[55.437,23.412],[55.4015,23.3925],[55.2805,23.1775],[55.2325,23.1105],[55.2135,22.9355],[55.2275,22.7925],[55.2115,22.7055]]],[[[56.549,25.6925],[56.652,25.8255],[56.6875,25.8965],[56.696,26.016],[56.749,26.0875],[56.7715,26.187],[56.76,26.372],[56.757,26.521],[56.73,26.5875],[56.697,26.6275],[56.629,26.676],[56.546,26.7025],[56.298,26.6035],[56.0365,26.384],[55.984,26.3185],[55.8885,26.1515],[56.0865,26.0505],[56.153,26.0695],[56.1955,25.9795],[56.163,25.942],[56.18,25.91],[56.14,25.827],[56.1675,25.725],[56.1585,25.6615],[56.202,25.612],[56.4185,25.6825],[56.549,25.6925]]],[[[56.208,25.256],[56.2735,25.232],[56.344,25.264],[56.2705,25.328],[56.2455,25.2755],[56.208,25.256]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/dd/Flag_of_Oman.svg","name:en":"Oman","wikidata":"Q842","ISO3166-1:alpha2":"OM","ISO3166-1:alpha3":"OMN","ISO3166-1:numeric":"512"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[79.396,9.549],[79.378,9.6665],[79.56,9.923],[79.6675,9.973],[80.0395,10.0965],[80.226,10.825],[80.221,11.0335],[80.139,11.516],[80.175,11.748],[80.2055,11.843],[80.329,12.14],[80.431,12.333],[80.574,12.7865],[80.5855,12.889],[80.642,13.207],[80.6695,13.565],[80.4985,15.025],[80.486,15.066],[80.474,15.2525],[80.492,15.3415],[80.5475,15.43],[80.653,15.4875],[80.8425,15.5205],[81.6015,16.0475],[81.784,16.0535],[82.2085,16.169],[82.8245,16.927],[83.209,17.1575],[83.779,17.6335],[83.981,17.7975],[84.4435,18.219],[84.9495,18.6765],[85.012,18.692],[85.0545,18.7715],[85.145,18.853],[85.2,18.994],[85.5155,19.2785],[85.6455,19.3995],[85.8125,19.452],[86.0995,19.559],[86.277,19.632],[86.666,19.78],[86.7235,19.8585],[86.781,19.9885],[86.9645,20.162],[87.4865,20.654],[87.5075,20.752],[87.5035,20.8535],[87.441,20.9045],[87.3115,21.0365],[87.2655,21.138],[87.274,21.235],[87.366,21.282],[87.6745,21.34],[88.3755,21.2585],[88.7805,21.3015],[89.0455,21.403],[89.14,21.4265],[89.14,21.6085],[89.0285,21.791],[89.0425,21.867],[89.0275,21.9195],[89.073,21.929],[89.0825,22.003],[89.04,22.1025],[89.07,22.1505],[89.0705,22.197],[89.033,22.2585],[88.996,22.2855],[88.985,22.3255],[89.0005,22.4315],[88.9595,22.552],[88.9425,22.558],[88.931,22.652],[88.96,22.685],[88.9115,22.7575],[88.963,22.819],[88.95,22.876],[88.9105,22.88],[88.8905,22.9275],[88.855,22.9585],[88.884,23.0405],[88.8685,23.101],[88.916,23.13],[88.9415,23.2065],[88.912,23.234],[88.85,23.2305],[88.8095,23.255],[88.734,23.2435],[88.703,23.307],[88.7575,23.3845],[88.753,23.4795],[88.6515,23.556],[88.637,23.605],[88.5905,23.639],[88.5595,23.712],[88.59,23.7995],[88.587,23.873],[88.6695,23.868],[88.737,23.919],[88.723,23.997],[88.7455,24.0325],[88.699,24.0845],[88.7005,24.153],[88.744,24.1875],[88.7395,24.2445],[88.7065,24.303],[88.652,24.294],[88.577,24.3165],[88.498,24.321],[88.3655,24.412],[88.226,24.469],[88.111,24.524],[88.1065,24.573],[88.0755,24.6335],[88.008,24.668],[88.063,24.7255],[88.109,24.8125],[88.165,24.8615],[88.152,24.9065],[88.1695,24.9515],[88.2295,24.958],[88.264,24.8855],[88.3425,24.8705],[88.396,24.9375],[88.3975,24.969],[88.438,25.009],[88.463,25.0795],[88.444,25.198],[88.477,25.213],[88.559,25.192],[88.6205,25.2055],[88.7155,25.207],[88.7995,25.171],[88.8315,25.206],[88.875,25.179],[88.949,25.181],[88.952,25.247],[89.0095,25.2985],[88.9155,25.312],[88.9055,25.338],[88.838,25.3695],[88.8035,25.5245],[88.7595,25.5265],[88.7105,25.4815],[88.6465,25.478],[88.603,25.516],[88.548,25.5175],[88.45,25.6045],[88.454,25.6645],[88.402,25.673],[88.357,25.7215],[88.2365,25.81],[88.172,25.787],[88.102,25.8285],[88.0855,25.9145],[88.111,25.934],[88.1415,26.0145],[88.1775,26.0215],[88.1855,26.0635],[88.1585,26.095],[88.1775,26.148],[88.325,26.2045],[88.3475,26.221],[88.3495,26.2825],[88.434,26.335],[88.524,26.36],[88.4845,26.459],[88.4155,26.4695],[88.35,26.5105],[88.397,26.626],[88.448,26.5355],[88.5605,26.461],[88.65,26.4295],[88.702,26.336],[88.6675,26.272],[88.803,26.3055],[88.8385,26.2325],[88.8755,26.2865],[88.9185,26.288],[88.9535,26.242],[89.0455,26.241],[89.04,26.281],[88.9825,26.309],[88.911,26.3705],[88.9575,26.4575],[89.0375,26.4],[89.09,26.392],[89.105,26.3265],[89.1355,26.3095],[89.125,26.264],[89.148,26.209],[89.155,26.139],[89.2285,26.123],[89.254,26.064],[89.3405,26.0155],[89.4275,26.013],[89.4635,25.998],[89.5165,26.0095],[89.54,25.97],[89.5865,25.9805],[89.589,26.039],[89.6435,26.063],[89.6295,26.1165],[89.601,26.1295],[89.6185,26.1795],[89.6875,26.1815],[89.7655,26.1285],[89.8215,26.01],[89.85,25.99],[89.8255,25.9455],[89.8875,25.945],[89.833,25.8705],[89.8265,25.81],[89.8355,25.7155],[89.8825,25.6215],[89.861,25.5155],[89.8145,25.374],[89.838,25.296],[89.904,25.3105],[90.1105,25.2245],[90.289,25.1955],[90.3835,25.154],[90.4385,25.147],[90.5235,25.1745],[90.741,25.1585],[90.776,25.176],[90.816,25.151],[91.082,25.198],[91.1795,25.1955],[91.269,25.2045],[91.328,25.1765],[91.44,25.151],[91.548,25.1495],[91.5755,25.172],[91.636,25.127],[91.6945,25.134],[91.7575,25.174],[91.791,25.1655],[91.915,25.181],[91.9825,25.1705],[92.0345,25.1885],[92.193,25.14],[92.224,25.0995],[92.347,25.05],[92.428,25.0315],[92.4205,24.967],[92.4845,24.9325],[92.499,24.904],[92.424,24.853],[92.317,24.895],[92.249,24.909],[92.244,24.8425],[92.27,24.832],[92.2975,24.7345],[92.26,24.683],[92.2145,24.503],[92.17,24.5305],[92.1545,24.4795],[92.169,24.4355],[92.091,24.371],[91.996,24.3875],[91.923,24.3335],[91.9475,24.2645],[91.9115,24.144],[91.8235,24.2085],[91.75,24.241],[91.762,24.1545],[91.7255,24.144],[91.6455,24.1705],[91.6435,24.1135],[91.586,24.0715],[91.474,24.099],[91.376,24.1055],[91.398,24.05],[91.361,23.984],[91.3225,23.993],[91.272,23.9605],[91.231,23.8815],[91.2535,23.8345],[91.21,23.747],[91.1695,23.7485],[91.1575,23.69],[91.21,23.6875],[91.2065,23.65],[91.1605,23.639],[91.202,23.523],[91.251,23.4765],[91.2915,23.319],[91.325,23.2385],[91.3255,23.1585],[91.3705,23.0715],[91.412,23.077],[91.3835,23.1555],[91.384,23.193],[91.418,23.2685],[91.449,23.261],[91.503,23.1935],[91.4975,23.147],[91.5515,23.0385],[91.544,23.0],[91.5775,22.9675],[91.627,22.956],[91.7215,22.989],[91.7835,23.037],[91.834,23.094],[91.8085,23.135],[91.7625,23.3095],[91.813,23.349],[91.8535,23.4065],[91.966,23.484],[91.9495,23.617],[91.9295,23.683],[91.978,23.735],[92.0005,23.673],[92.068,23.648],[92.141,23.7325],[92.205,23.705],[92.279,23.7265],[92.291,23.681],[92.273,23.6435],[92.306,23.583],[92.3165,23.4865],[92.3535,23.39],[92.381,23.352],[92.402,23.2365],[92.354,23.236],[92.361,23.0985],[92.3925,23.0565],[92.374,22.971],[92.378,22.9295],[92.4195,22.911],[92.4645,22.8545],[92.4545,22.82],[92.479,22.7535],[92.5165,22.7225],[92.543,22.491],[92.591,22.255],[92.599,22.137],[92.5665,22.1415],[92.601,21.982],[92.6795,22.027],[92.678,22.0995],[92.7185,22.1595],[92.806,22.104],[92.8545,22.0595],[92.8655,22.0255],[92.9225,21.994],[92.9895,22.061],[93.042,22.1365],[93.0385,22.1955],[93.0715,22.2115],[93.1575,22.187],[93.142,22.2445],[93.202,22.2645],[93.1855,22.339],[93.1855,22.428],[93.1315,22.468],[93.113,22.5595],[93.141,22.594],[93.108,22.6355],[93.092,22.7095],[93.1065,22.746],[93.0955,22.807],[93.146,22.9275],[93.123,23.0075],[93.14,23.054],[93.2105,23.0475],[93.235,23.011],[93.2945,23.008],[93.32,23.029],[93.3645,23.121],[93.3875,23.216],[93.3555,23.3535],[93.4025,23.3895],[93.388,23.4205],[93.3975,23.5105],[93.4185,23.5415],[93.4175,23.6365],[93.436,23.6875],[93.3945,23.754],[93.385,23.8765],[93.3925,23.924],[93.352,23.947],[93.3255,24.0465],[93.344,24.092],[93.3985,24.0855],[93.4655,23.972],[93.51,23.9465],[93.564,23.9785],[93.5945,23.962],[93.6265,24.0115],[93.7225,23.999],[93.7565,24.006],[93.815,23.924],[93.894,23.9515],[93.9735,23.9235],[94.022,23.926],[94.0465,23.8925],[94.095,23.8855],[94.117,23.838],[94.156,23.847],[94.169,23.9275],[94.238,24.033],[94.2555,24.081],[94.2605,24.164],[94.4085,24.44],[94.4555,24.5695],[94.5095,24.5925],[94.541,24.6425],[94.5465,24.707],[94.607,24.711],[94.6295,24.754],[94.633,24.835],[94.6845,24.882],[94.7135,24.93],[94.697,24.961],[94.737,25.001],[94.745,25.063],[94.7255,25.134],[94.6035,25.1845],[94.5775,25.216],[94.5855,25.268],[94.6865,25.466],[94.808,25.4955],[94.845,25.5605],[94.918,25.6145],[94.9405,25.672],[95.038,25.74],[95.0515,25.798],[95.02,25.872],[95.029,25.935],[95.081,25.947],[95.1565,26.0195],[95.185,26.075],[95.12,26.0995],[95.1225,26.3065],[95.1325,26.3775],[95.073,26.4725],[95.114,26.518],[95.1085,26.543],[95.154,26.583],[95.149,26.6155],[95.208,26.6475],[95.315,26.6645],[95.4395,26.7025],[95.485,26.7485],[95.5035,26.8065],[95.546,26.8295],[95.6095,26.814],[95.658,26.8915],[95.7135,26.883],[95.7555,26.91],[95.7555,26.951],[95.804,27.0155],[95.831,27.008],[95.946,27.052],[96.0075,27.14],[96.022,27.18],[96.091,27.2235],[96.314,27.2935],[96.4095,27.292],[96.4335,27.305],[96.5265,27.289],[96.582,27.314],[96.606,27.363],[96.6765,27.3355],[96.7155,27.376],[96.7765,27.356],[96.8295,27.312],[96.855,27.267],[96.858,27.215],[96.89,27.1765],[97.0105,27.145],[97.076,27.0955],[97.1455,27.0925],[97.1765,27.14],[97.1015,27.2145],[97.088,27.245],[97.002,27.3455],[96.9145,27.461],[96.9335,27.508],[96.9,27.6085],[96.995,27.671],[97.026,27.7355],[97.1125,27.7695],[97.255,27.894],[97.293,27.9135],[97.36,27.873],[97.379,27.892],[97.3675,27.977],[97.3945,28.0185],[97.3585,28.063],[97.3445,28.117],[97.361,28.1655],[97.346,28.2145],[97.2485,28.264],[97.146,28.353],[97.0785,28.3715],[97.027,28.33],[96.977,28.3295],[96.8895,28.386],[96.893,28.4185],[96.861,28.4855],[96.767,28.515],[96.7455,28.5715],[96.655,28.609],[96.6135,28.6135],[96.538,28.572],[96.4785,28.4905],[96.412,28.517],[96.4815,28.555],[96.451,28.583],[96.537,28.6545],[96.536,28.681],[96.597,28.6965],[96.62,28.7275],[96.5765,28.8185],[96.519,28.8675],[96.508,28.9465],[96.441,28.9525],[96.4345,29.0065],[96.362,29.048],[96.358,29.094],[96.2695,29.0965],[96.23,29.047],[96.183,29.111],[96.235,29.1295],[96.2995,29.191],[96.261,29.2445],[96.196,29.2635],[96.1495,29.295],[96.139,29.343],[96.0535,29.3825],[96.0165,29.3625],[95.9655,29.376],[95.8775,29.3145],[95.8125,29.3475],[95.737,29.298],[95.7525,29.276],[95.7055,29.213],[95.648,29.2105],[95.6055,29.236],[95.589,29.188],[95.5085,29.195],[95.5095,29.1265],[95.458,29.137],[95.419,29.1805],[95.379,29.137],[95.2995,29.1365],[95.273,29.1055],[95.179,29.1045],[95.136,29.0885],[95.097,29.142],[94.9945,29.144],[94.8475,29.1825],[94.8095,29.165],[94.7945,29.2175],[94.7515,29.2295],[94.735,29.287],[94.6935,29.318],[94.5905,29.272],[94.541,29.2195],[94.5105,29.231],[94.452,29.189],[94.3905,29.184],[94.379,29.154],[94.293,29.1525],[94.2845,29.0865],[94.3125,29.079],[94.342,29.002],[94.274,28.968],[94.2605,28.9315],[94.1785,28.936],[94.1315,28.889],[94.0785,28.8825],[93.975,28.8215],[93.8965,28.7575],[93.7875,28.733],[93.7845,28.7135],[93.707,28.6645],[93.643,28.6575],[93.623,28.6885],[93.512,28.6675],[93.4575,28.668],[93.3895,28.5325],[93.3075,28.527],[93.23,28.5915],[93.197,28.545],[93.1485,28.3665],[92.9915,28.273],[92.9325,28.249],[92.9215,28.2005],[92.8265,28.175],[92.791,28.1875],[92.739,28.154],[92.6775,28.151],[92.6645,28.0705],[92.7375,28.0415],[92.7235,27.9735],[92.6505,27.915],[92.5815,27.89],[92.4775,27.8365],[92.422,27.8355],[92.3205,27.8],[92.2905,27.873],[92.2445,27.888],[92.143,27.835],[92.108,27.803],[92.036,27.7755],[91.979,27.7745],[91.925,27.7165],[91.8725,27.72],[91.829,27.788],[91.8015,27.7725],[91.722,27.783],[91.6605,27.829],[91.6125,27.839],[91.5695,27.8175],[91.6145,27.8215],[91.645,27.77],[91.6285,27.6985],[91.573,27.661],[91.566,27.584],[91.6515,27.484],[91.7775,27.4655],[91.925,27.473],[91.9435,27.46],[92.017,27.4805],[92.056,27.4],[92.065,27.3275],[92.1235,27.2865],[92.0465,27.2695],[92.0715,27.238],[92.0275,27.1625],[92.023,27.11],[92.0445,27.052],[92.0815,27.0405],[92.12,26.9715],[92.1035,26.8695],[92.0565,26.8495],[91.987,26.861],[91.894,26.9195],[91.8595,26.9135],[91.825,26.864],[91.7245,26.814],[91.627,26.821],[91.4945,26.7925],[91.41,26.8395],[91.378,26.796],[91.3385,26.7805],[91.235,26.8125],[91.0985,26.8225],[91.0555,26.7815],[90.9955,26.7905],[90.822,26.7765],[90.691,26.7715],[90.5475,26.8165],[90.418,26.9045],[90.3545,26.9015],[90.3,26.8485],[90.248,26.86],[90.2005,26.835],[90.1905,26.768],[90.045,26.73],[89.902,26.723],[89.8625,26.7015],[89.771,26.702],[89.745,26.73],[89.679,26.7395],[89.649,26.7705],[89.557,26.8135],[89.4765,26.802],[89.4395,26.8405],[89.379,26.8625],[89.3175,26.8515],[89.2625,26.8155],[89.141,26.812],[89.102,26.836],[89.0955,26.8915],[89.0155,26.94],[88.9795,26.918],[88.9225,26.993],[88.874,26.954],[88.87,27.11],[88.7465,27.142],[88.7995,27.2085],[88.8025,27.2485],[88.9045,27.273],[88.9215,27.3275],[88.8075,27.4045],[88.783,27.4535],[88.7715,27.558],[88.8065,27.5965],[88.8095,27.6375],[88.8445,27.662],[88.8565,27.717],[88.857,27.8155],[88.888,27.8565],[88.843,27.956],[88.836,28.0155],[88.7545,28.0805],[88.667,28.0765],[88.64,28.116],[88.5575,28.0755],[88.546,28.034],[88.492,28.0485],[88.468,28.017],[88.3225,27.98],[88.2635,27.9555],[88.2375,27.9695],[88.1875,27.9425],[88.1425,27.9655],[88.118,27.918],[88.135,27.8815],[88.201,27.8375],[88.197,27.791],[88.1585,27.741],[88.144,27.665],[88.085,27.5905],[88.044,27.4785],[88.0785,27.432],[88.0415,27.3705],[88.0665,27.3365],[88.0315,27.2865],[88.007,27.1435],[87.991,27.131],[88.0375,27.037],[88.083,27.029],[88.118,26.9875],[88.136,26.899],[88.172,26.869],[88.1895,26.7465],[88.1635,26.646],[88.1055,26.561],[88.1035,26.4665],[88.0775,26.419],[87.992,26.3695],[87.916,26.434],[87.9,26.4775],[87.8485,26.437],[87.793,26.469],[87.7695,26.4215],[87.678,26.416],[87.6515,26.3935],[87.5875,26.393],[87.516,26.431],[87.4665,26.4405],[87.3885,26.419],[87.3135,26.3675],[87.266,26.374],[87.247,26.4145],[87.1615,26.404],[87.0915,26.4505],[87.0725,26.543],[86.932,26.516],[86.894,26.462],[86.8335,26.439],[86.7655,26.4595],[86.737,26.423],[86.694,26.451],[86.57,26.4965],[86.542,26.5385],[86.306,26.62],[86.141,26.617],[86.028,26.6665],[85.974,26.657],[85.861,26.5705],[85.851,26.6085],[85.732,26.654],[85.723,26.687],[85.735,26.7795],[85.7205,26.8205],[85.642,26.853],[85.543,26.8385],[85.4515,26.7815],[85.4075,26.7915],[85.334,26.742],[85.197,26.771],[85.177,26.8145],[85.193,26.8665],[85.099,26.871],[85.0575,26.849],[84.9625,26.9605],[84.855,26.986],[84.8195,27.0215],[84.7575,27.003],[84.6435,27.046],[84.6705,27.091],[84.682,27.2365],[84.6225,27.336],[84.294,27.385],[84.254,27.453],[84.176,27.4745],[84.1055,27.521],[84.052,27.4435],[83.874,27.4345],[83.865,27.371],[83.8295,27.3705],[83.6145,27.469],[83.389,27.48],[83.4075,27.415],[83.388,27.3755],[83.3375,27.3325],[83.2965,27.3335],[83.272,27.3835],[83.188,27.4545],[83.0345,27.449],[82.954,27.468],[82.9295,27.501],[82.7365,27.5025],[82.757,27.5835],[82.7075,27.715],[82.605,27.706],[82.473,27.6765],[82.402,27.7035],[82.368,27.7425],[82.3035,27.773],[82.209,27.843],[82.121,27.866],[82.0625,27.9215],[81.9685,27.929],[81.9,27.8535],[81.805,27.904],[81.6985,27.988],[81.644,27.9935],[81.4785,28.0825],[81.484,28.1185],[81.447,28.161],[81.3745,28.177],[81.368,28.141],[81.313,28.1565],[81.321,28.1975],[81.2325,28.289],[81.211,28.361],[81.0815,28.3845],[81.0335,28.428],[80.9055,28.466],[80.768,28.5655],[80.7145,28.5685],[80.6675,28.642],[80.614,28.639],[80.5395,28.6905],[80.5035,28.665],[80.5225,28.5525],[80.4605,28.622],[80.377,28.629],[80.275,28.711],[80.2565,28.754],[80.216,28.7555],[80.1185,28.828],[80.0645,28.8375],[80.059,28.916],[80.127,29.006],[80.1455,29.1045],[80.1865,29.137],[80.2355,29.1175],[80.2715,29.1465],[80.262,29.206],[80.29,29.2315],[80.318,29.3045],[80.2795,29.3475],[80.246,29.447],[80.3025,29.4545],[80.298,29.49],[80.342,29.5105],[80.3485,29.5555],[80.408,29.597],[80.4165,29.652],[80.385,29.6735],[80.366,29.726],[80.4175,29.796],[80.493,29.795],[80.5535,29.853],[80.574,29.9235],[80.6015,29.958],[80.6745,29.957],[80.7385,29.999],[80.8055,30.09],[80.878,30.1285],[80.8715,30.1605],[81.0345,30.197],[81.031,30.247],[80.981,30.2705],[80.927,30.267],[80.9065,30.3035],[80.8335,30.313],[80.7875,30.3405],[80.715,30.414],[80.693,30.413],[80.6065,30.4715],[80.541,30.449],[80.497,30.488],[80.4195,30.515],[80.3455,30.5205],[80.3155,30.5645],[80.2545,30.5645],[80.2085,30.588],[80.2195,30.643],[80.1925,30.666],[80.249,30.7205],[80.2385,30.7625],[80.1975,30.765],[80.1795,30.806],[80.1085,30.7815],[80.05,30.841],[79.9885,30.877],[79.929,30.8825],[79.889,30.9175],[79.8575,30.9755],[79.776,30.986],[79.663,30.9645],[79.601,30.939],[79.553,30.9575],[79.507,31.0325],[79.427,31.0235],[79.414,31.1065],[79.321,31.1385],[79.301,31.219],[79.2265,31.2605],[79.25,31.2935],[79.2235,31.346],[79.1425,31.432],[79.075,31.459],[79.019,31.4265],[79.0185,31.349],[78.944,31.3655],[78.913,31.31],[78.833,31.294],[78.778,31.3125],[78.753,31.385],[78.795,31.444],[78.7205,31.5075],[78.7445,31.542],[78.8225,31.5785],[78.8465,31.607],[78.761,31.675],[78.702,31.807],[78.7355,31.8325],[78.7425,31.902],[78.779,31.9665],[78.74,32.0015],[78.7065,32.063],[78.597,32.158],[78.4965,32.276],[78.476,32.3325],[78.4725,32.4435],[78.3925,32.538],[78.4145,32.566],[78.502,32.5845],[78.5465,32.619],[78.611,32.6005],[78.635,32.6445],[78.723,32.6745],[78.7815,32.6165],[78.7575,32.567],[78.813,32.434],[78.8685,32.4135],[78.9685,32.3355],[78.9895,32.37],[79.058,32.387],[79.1045,32.3745],[79.1175,32.4545],[79.184,32.498],[79.248,32.5165],[79.311,32.56],[79.41,32.519],[79.399,32.604],[79.4605,32.71],[79.4145,32.737],[79.407,32.7855],[79.303,32.892],[79.3065,32.928],[79.1605,33.015],[79.14,33.106],[79.1625,33.1675],[79.073,33.223],[79.035,33.2725],[79.027,33.32],[78.9625,33.3395],[78.9365,33.387],[78.836,33.4265],[78.8035,33.4895],[78.74,33.554],[78.741,33.601],[78.6785,33.667],[78.731,33.6835],[78.775,33.738],[78.754,33.7845],[78.7655,33.836],[78.7315,33.9275],[78.7425,34.0],[78.6575,34.0315],[78.6575,34.0745],[78.7425,34.0925],[78.826,34.1245],[78.862,34.1655],[78.9255,34.1545],[78.944,34.225],[78.9855,34.314],[78.982,34.357],[78.947,34.3895],[78.902,34.381],[78.851,34.4155],[78.7425,34.453],[78.7565,34.4845],[78.7095,34.526],[78.634,34.544],[78.5635,34.5095],[78.552,34.5715],[78.4825,34.5785],[78.3855,34.6065],[78.29,34.615],[78.2625,34.6635],[78.272,34.7015],[78.2085,34.7205],[78.237,34.868],[78.1785,34.9285],[78.2015,34.973],[78.1145,35.032],[78.1275,35.1005],[78.0495,35.114],[78.0565,35.175],[78.003,35.243],[78.023,35.359],[78.101,35.4305],[78.098,35.4995],[78.025,35.4685],[77.968,35.4945],[77.911,35.462],[77.812,35.523],[77.7425,35.4955],[77.719,35.4615],[77.6295,35.4625],[77.5595,35.487],[77.501,35.4895],[77.4425,35.462],[77.3815,35.4735],[77.3025,35.5455],[77.194,35.522],[77.011,35.611],[76.9575,35.5965],[76.815,35.67],[76.776,35.658],[76.755,35.6295],[76.7935,35.588],[76.7495,35.555],[76.759,35.5185],[76.8385,35.443],[76.863,35.3895],[76.948,35.393],[77.0065,35.2945],[76.975,35.2515],[77.0175,35.183],[77.0855,35.169],[77.0825,35.099],[77.1105,35.049],[77.047,35.0515],[76.961,35.0175],[76.898,34.926],[76.8615,34.9485],[76.792,34.954],[76.7455,34.8885],[76.737,34.8065],[76.7015,34.796],[76.697,34.7505],[76.642,34.734],[76.541,34.762],[76.4865,34.796],[76.4195,34.7375],[76.328,34.7195],[76.2825,34.7235],[76.2505,34.6765],[76.1635,34.6325],[76.116,34.6395],[76.063,34.688],[75.9455,34.6255],[75.785,34.5135],[75.6705,34.54],[75.463,34.5345],[75.365,34.538],[75.301,34.5865],[75.2475,34.6035],[75.267,34.6365],[75.163,34.6565],[75.026,34.6295],[74.936,34.673],[74.7305,34.681],[74.651,34.706],[74.58,34.7615],[74.4485,34.7825],[74.3095,34.7825],[74.2415,34.727],[74.158,34.6885],[74.142,34.6605],[73.969,34.6855],[73.932,34.6415],[73.9535,34.5605],[73.905,34.528],[73.88,34.4535],[73.803,34.42],[73.7505,34.369],[73.774,34.3325],[73.859,34.324],[73.939,34.3365],[73.9455,34.276],[73.9905,34.2705],[74.0035,34.1905],[73.927,34.1275],[73.896,34.06],[73.965,34.0135],[74.016,34.0395],[74.1325,34.047],[74.1975,34.017],[74.233,34.0235],[74.277,33.9695],[74.2815,33.901],[74.1895,33.839],[74.06,33.824],[74.01,33.7765],[73.9815,33.707],[73.981,33.6415],[74.0315,33.5785],[74.081,33.585],[74.1405,33.5575],[74.1925,33.4785],[74.1885,33.4085],[74.1665,33.3375],[74.1265,33.3045],[74.018,33.2705],[74.017,33.205],[74.0705,33.194],[74.154,33.1365],[74.2015,33.0665],[74.318,33.032],[74.3385,32.9555],[74.42,32.8855],[74.4195,32.84],[74.4485,32.7935],[74.5185,32.7485],[74.6305,32.7675],[74.6585,32.83],[74.7045,32.817],[74.6535,32.697],[74.695,32.6605],[74.657,32.6305],[74.6515,32.563],[74.69,32.5345],[74.6855,32.4995],[74.7295,32.481],[74.8125,32.48],[74.859,32.4935],[74.943,32.452],[75.0155,32.4645],[75.0265,32.4975],[75.109,32.46],[75.137,32.41],[75.192,32.426],[75.238,32.387],[75.2895,32.3695],[75.3715,32.2795],[75.373,32.2285],[75.3315,32.2195],[75.314,32.172],[75.261,32.1015],[75.2205,32.1075],[75.1795,32.0765],[75.123,32.0815],[75.0125,32.0355],[74.9265,32.0665],[74.8285,31.998],[74.819,31.9585],[74.677,31.926],[74.6155,31.89],[74.5705,31.837],[74.5595,31.758],[74.4865,31.715],[74.535,31.6775],[74.5555,31.608],[74.616,31.5575],[74.5875,31.504],[74.635,31.4845],[74.6435,31.416],[74.5965,31.4165],[74.537,31.33],[74.5105,31.132],[74.5515,31.0865],[74.689,31.128],[74.7025,31.0915],[74.671,31.0535],[74.548,31.032],[74.5455,30.992],[74.48,30.974],[74.366,30.893],[74.38,30.862],[74.3205,30.8465],[74.2745,30.7355],[74.149,30.6315],[74.1285,30.6325],[74.0725,30.523],[73.993,30.514],[73.946,30.468],[73.9025,30.349],[73.9595,30.27],[73.9725,30.1985],[73.83,30.0935],[73.8065,30.068],[73.597,30.019],[73.397,29.946],[73.2825,29.572],[73.0855,29.238],[73.0045,29.153],[72.946,29.028],[72.733,28.948],[72.4795,28.812],[72.404,28.783],[72.2995,28.6695],[72.206,28.394],[72.1325,28.313],[72.0045,28.2185],[71.9275,28.1215],[71.8985,27.961],[71.6665,27.878],[71.383,27.8725],[71.2055,27.8355],[70.906,27.7095],[70.7595,27.721],[70.6845,27.828],[70.6775,27.922],[70.5895,28.0105],[70.5055,28.037],[70.372,28.012],[70.296,27.936],[70.227,27.9015],[70.1325,27.806],[70.026,27.563],[69.934,27.4975],[69.863,27.402],[69.703,27.2845],[69.587,27.1835],[69.5135,27.01],[69.4845,26.805],[69.5105,26.7435],[69.7255,26.652],[69.792,26.598],[69.886,26.5785],[70.0565,26.6005],[70.1175,26.587],[70.1745,26.5515],[70.1855,26.374],[70.1635,26.295],[70.175,26.2515],[70.141,26.156],[70.0835,26.0835],[70.1,25.938],[70.1745,25.8285],[70.2225,25.793],[70.2685,25.7135],[70.387,25.6735],[70.53,25.6835],[70.6085,25.715],[70.645,25.713],[70.674,25.676],[70.667,25.5315],[70.6785,25.5255],[70.665,25.397],[70.737,25.3325],[70.7515,25.2775],[70.8885,25.148],[70.9375,24.9385],[71.0265,24.809],[71.0655,24.7175],[71.0745,24.662],[71.009,24.6355],[70.9865,24.5955],[71.005,24.523],[70.9985,24.4445],[71.103,24.4355],[71.0495,24.356],[70.986,24.3655],[70.8785,24.302],[70.8515,24.245],[70.8055,24.221],[70.714,24.2155],[70.6445,24.225],[70.5715,24.252],[70.585,24.278],[70.5625,24.351],[70.6025,24.408],[70.573,24.4225],[70.327,24.352],[70.254,24.3255],[70.11,24.295],[70.0665,24.199],[70.025,24.171],[69.7315,24.171],[69.5945,24.2925],[69.5035,24.2685],[69.4445,24.2805],[69.367,24.27],[69.313,24.2815],[69.194,24.2365],[69.095,24.2745],[69.013,24.224],[68.985,24.2275],[68.9455,24.3025],[68.907,24.28],[68.88,24.216],[68.8565,24.214],[68.831,24.3115],[68.7645,24.296],[68.7695,24.2655],[68.753,23.9715],[68.3795,23.971],[68.32,23.9135],[68.2905,23.9445],[68.207,23.8445],[68.186,23.6885],[68.1355,23.6485],[68.1115,23.6015],[68.115,23.5395],[68.247,23.4475],[68.2765,23.2945],[68.372,23.1015],[68.661,22.9105],[68.98,22.7365],[68.983,22.677],[68.8425,22.592],[68.743,22.4535],[68.6825,22.276],[68.79,22.0165],[69.0395,21.7155],[69.2905,21.487],[69.4985,21.29],[69.807,20.95],[70.0075,20.78],[70.4175,20.576],[70.7395,20.4505],[70.909,20.443],[71.325,20.5605],[71.661,20.639],[71.9185,20.755],[72.079,20.8455],[72.226,20.8985],[72.383,20.9175],[72.47,20.811],[72.5,20.595],[72.48,20.376],[72.538,20.15],[72.6095,19.2635],[72.713,17.971],[72.9885,16.5755],[73.3285,15.608],[73.531,15.229],[73.8725,14.753],[74.324,13.822],[74.5185,12.9955],[74.6405,12.659],[74.7125,12.494],[74.971,11.945],[75.251,11.5245],[75.5525,11.043],[75.5875,10.892],[75.683,10.559],[75.9115,10.046],[76.0185,9.676],[76.2255,8.9565],[76.465,8.593],[76.673,8.343],[76.9215,8.096],[77.6675,7.8585],[78.1035,8.041],[78.361,8.1605],[78.463,8.351],[78.545,8.8475],[78.7395,8.9325],[79.069,9.0225],[79.374,8.979],[79.411,8.9405],[79.4965,8.8975],[79.5715,9.087],[79.5665,9.258],[79.4205,9.3535],[79.396,9.549]]],[[[71.5625,11.888],[71.5875,11.787],[71.666,11.701],[71.7465,11.669],[71.8295,11.6675],[71.88,11.682],[71.9545,11.731],[72.0025,11.8005],[72.02,11.906],[72.005,11.9705],[71.953,12.048],[71.864,12.1015],[71.781,12.113],[71.713,12.0995],[71.6295,12.0475],[71.572,11.9535],[71.5625,11.888]]],[[[71.6765,12.3525],[71.6945,12.2665],[71.745,12.1935],[71.805,12.152],[71.8905,12.13],[71.99,12.145],[72.046,12.1765],[72.0885,12.218],[72.12,12.275],[72.134,12.3335],[72.123,12.424],[72.095,12.4795],[72.018,12.5485],[71.965,12.5695],[71.869,12.5745],[71.7995,12.552],[71.7525,12.52],[71.7025,12.4575],[71.6765,12.3525]]],[[[71.8165,11.153],[71.826,11.089],[71.882,10.997],[71.979,10.939],[71.96,10.8635],[71.9675,10.787],[71.9905,10.7325],[72.032,10.682],[72.0975,10.64],[72.1585,10.6235],[72.2585,10.6325],[72.3315,10.671],[72.402,10.591],[72.411,10.491],[72.4395,10.434],[72.5075,10.3695],[72.6145,10.336],[72.733,10.3595],[72.7965,10.4065],[72.833,10.4575],[72.855,10.522],[72.852,10.613],[72.8285,10.6725],[72.7625,10.744],[72.7545,10.84],[72.724,10.902],[72.7705,10.907],[72.8485,10.938],[72.8985,10.981],[72.988,11.1425],[73.001,11.196],[72.998,11.266],[73.074,11.277],[73.1595,11.3255],[73.2125,11.397],[73.232,11.465],[73.2255,11.549],[73.207,11.5955],[73.148,11.666],[73.0635,11.7075],[73.007,11.7155],[72.9385,11.706],[72.9295,11.7495],[72.872,11.8435],[72.794,11.8935],[72.686,11.9075],[72.615,11.8885],[72.539,11.8335],[72.486,11.732],[72.481,11.6835],[72.4975,11.6015],[72.527,11.55],[72.585,11.4965],[72.6565,11.466],[72.748,11.463],[72.749,11.445],[72.6555,11.414],[72.5945,11.362],[72.521,11.2315],[72.4965,11.156],[72.4615,11.091],[72.3985,11.1385],[72.351,11.157],[72.2745,11.164],[72.265,11.2195],[72.2155,11.305],[72.2205,11.335],[72.309,11.379],[72.361,11.438],[72.392,11.533],[72.385,11.6105],[72.3295,11.7075],[72.253,11.7595],[72.192,11.7755],[72.1215,11.7735],[72.0305,11.7355],[71.9695,11.6725],[71.9355,11.5755],[71.9415,11.4975],[71.983,11.4145],[71.967,11.3655],[71.9075,11.3335],[71.863,11.29],[71.8245,11.213],[71.8165,11.153]]],[[[72.056,10.0635],[72.062,10.0135],[72.096,9.9375],[72.155,9.879],[72.2405,9.843],[72.2975,9.839],[72.3835,9.8605],[72.434,9.8925],[72.4935,9.9705],[72.513,10.0385],[72.507,10.1225],[72.464,10.2055],[72.408,10.255],[72.32,10.2875],[72.243,10.2865],[72.152,10.248],[72.1065,10.2055],[72.074,10.152],[72.056,10.0635]]],[[[72.809,8.278],[72.8305,8.192],[72.871,8.133],[72.956,8.081],[73.0035,8.069],[73.1095,8.076],[73.172,8.109],[73.2485,8.199],[73.281,8.329],[73.252,8.428],[73.19,8.492],[73.114,8.522],[73.015,8.515],[72.924,8.482],[72.869,8.442],[72.8185,8.352],[72.809,8.278]]],[[[73.404,10.1165],[73.426,10.0205],[73.4695,9.9595],[73.5115,9.926],[73.593,9.8955],[73.6605,9.8935],[73.746,9.921],[73.796,9.959],[73.8415,10.0235],[73.858,10.073],[73.8605,10.1465],[73.8225,10.2445],[73.756,10.308],[73.6925,10.3355],[73.579,10.337],[73.505,10.3045],[73.439,10.238],[73.409,10.1655],[73.404,10.1165]]],[[[73.4505,10.816],[73.463,10.7435],[73.505,10.6715],[73.589,10.6105],[73.683,10.592],[73.737,10.599],[73.813,10.634],[73.847,10.663],[73.8925,10.7325],[73.908,10.7975],[73.8935,10.899],[73.828,10.9885],[73.745,11.033],[73.6375,11.0385],[73.5735,11.0165],[73.5045,10.962],[73.477,10.9215],[73.4505,10.816]]],[[[93.035,13.882],[92.9025,13.85],[92.7655,13.755],[92.704,13.68],[92.6185,13.4995],[92.4925,13.1535],[92.447,12.892],[92.445,12.849],[92.4615,12.574],[92.352,12.3535],[92.0125,11.673],[91.992,11.581],[92.0065,10.9725],[92.0205,10.9035],[92.1895,10.477],[92.2145,10.4245],[92.2705,10.369],[92.3435,10.3395],[92.3895,10.336],[92.457,10.3095],[92.5705,10.3155],[92.644,10.3455],[92.7,10.4005],[92.7565,10.5025],[92.798,10.635],[92.803,10.704],[92.7885,10.819],[92.8365,10.8765],[92.867,10.9485],[92.8695,11.001],[92.9035,11.0385],[92.934,11.1105],[92.934,11.1885],[92.9085,11.252],[92.922,11.2955],[92.922,11.3735],[92.905,11.4195],[92.9555,11.58],[93.005,11.596],[93.084,11.5805],[93.1625,11.596],[93.229,11.6395],[93.2735,11.7045],[93.289,11.7815],[93.275,11.879],[93.242,11.9875],[93.2925,12.0385],[93.323,12.111],[93.323,12.189],[93.3085,12.231],[93.2955,12.336],[93.2455,12.4305],[93.174,12.4935],[93.1805,12.5415],[93.1635,12.621],[93.181,12.6695],[93.181,12.7475],[93.1565,12.8275],[93.1975,12.919],[93.236,13.042],[93.241,13.124],[93.2635,13.181],[93.292,13.2985],[93.3105,13.4325],[93.295,13.509],[93.2625,13.559],[93.2675,13.692],[93.239,13.7725],[93.1345,13.8195],[93.035,13.882]]],[[[92.5325,9.2105],[92.5525,9.136],[92.8165,8.758],[92.895,8.195],[93.4755,6.9225],[93.631,6.658],[93.6925,6.5935],[93.776,6.561],[94.1165,6.2345],[94.1845,6.2365],[94.2575,6.2665],[94.3775,6.382],[94.405,6.4365],[94.4195,6.519],[94.452,6.594],[94.4585,6.683],[94.4435,6.7595],[94.402,6.828],[94.3885,6.92],[94.323,7.0805],[94.276,7.1285],[94.2245,7.2185],[94.1325,7.289],[94.0385,7.3385],[93.986,7.3515],[93.916,7.41],[93.999,7.441],[94.0545,7.496],[94.0845,7.568],[94.11,7.7285],[94.0945,7.828],[94.065,7.8985],[94.107,7.929],[94.1505,7.994],[94.166,8.07],[94.1445,8.229],[94.114,8.3255],[94.0705,8.39],[93.992,8.4455],[93.915,8.461],[93.756,8.7415],[93.619,8.778],[93.336,8.7795],[93.3385,8.8855],[93.295,9.0075],[92.96,9.403],[92.903,9.452],[92.83,9.4815],[92.7515,9.4815],[92.679,9.452],[92.597,9.3895],[92.5535,9.325],[92.5325,9.2105]]],[[[93.647,12.2765],[93.6635,12.195],[93.7105,12.126],[93.7815,12.08],[93.8645,12.064],[93.907,12.068],[93.9855,12.0995],[94.0455,12.1585],[94.078,12.235],[94.078,12.318],[94.0455,12.395],[93.9855,12.4535],[93.907,12.485],[93.822,12.485],[93.7435,12.4535],[93.6835,12.395],[93.651,12.318],[93.647,12.2765]]],[[[94.045,13.4385],[94.0465,13.401],[94.0775,13.337],[94.1385,13.2705],[94.202,13.2395],[94.326,13.244],[94.388,13.274],[94.4345,13.3165],[94.4785,13.411],[94.4705,13.525],[94.4375,13.5835],[94.333,13.6515],[94.2805,13.661],[94.2035,13.6485],[94.101,13.5785],[94.064,13.513],[94.045,13.4385]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/41/Flag_of_India.svg","name:en":"India","wikidata":"Q668","ISO3166-1:alpha2":"IN","ISO3166-1:alpha3":"IND","ISO3166-1:numeric":"356"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[51.586,24.664],[51.391,24.6235],[51.376,24.5785],[51.302,24.5045],[51.266,24.5055],[51.0985,24.471],[50.9945,24.504],[50.9285,24.547],[50.8115,24.7445],[50.794,24.8775],[50.706,24.923],[50.7135,25.0425],[50.666,25.1375],[50.647,25.256],[50.647,25.3205],[50.615,25.4785],[50.573,25.5415],[50.5675,25.576],[50.529,25.594],[50.4165,25.6985],[50.366,25.819],[50.304,25.879],[50.27,26.0795],[50.311,26.171],[50.3165,26.252],[50.3415,26.3735],[50.3765,26.4075],[50.3875,26.53],[50.4885,26.5845],[50.4375,27.011],[50.395,27.11],[50.104,27.459],[50.0715,27.502],[50.013,27.545],[50.0445,27.615],[50.106,27.587],[50.2215,27.581],[50.2995,27.6095],[50.362,27.6635],[50.393,27.7195],[50.4035,27.7815],[50.367,27.887],[49.9835,27.882],[49.8985,27.915],[49.8225,27.924],[49.746,27.9095],[49.6485,27.8455],[49.607,27.7745],[49.522,27.847],[49.2815,28.1265],[49.159,28.3035],[49.1,28.36],[48.6965,28.622],[48.6695,28.6725],[48.5895,28.639],[48.4305,28.5345],[47.7045,28.5245],[47.709,28.543],[47.6055,28.655],[47.59,28.716],[47.598,28.7565],[47.5845,28.8355],[47.465,29.0],[46.841,29.068],[46.5525,29.1005],[46.4245,29.0585],[45.725,29.1195],[45.3745,29.142],[44.8335,29.187],[44.727,29.1915],[44.5135,29.3545],[44.0405,29.705],[43.7065,29.9505],[43.2445,30.2865],[42.6585,30.7065],[42.0855,31.1115],[41.4405,31.373],[40.796,31.736],[40.4135,31.948],[39.973,32.0245],[39.201,32.1545],[39.0065,32.0005],[38.4785,31.872],[38.04,31.7635],[37.4035,31.6015],[37.006,31.5005],[37.283,31.228],[37.5835,30.928],[37.933,30.5765],[37.997,30.5005],[37.6655,30.3325],[37.521,30.0425],[37.5055,30.001],[36.865,29.8855],[36.785,29.868],[36.736,29.789],[36.681,29.7495],[36.626,29.639],[36.5485,29.5605],[36.5275,29.5145],[36.4095,29.4055],[36.371,29.3975],[36.2125,29.286],[36.152,29.232],[36.073,29.1835],[35.823,29.2195],[35.737,29.238],[34.9495,29.359],[34.8845,29.375],[34.8305,29.2695],[34.816,29.193],[34.7625,29.053],[34.7615,28.9525],[34.7335,28.905],[34.737,28.8415],[34.7115,28.728],[34.688,28.6725],[34.677,28.579],[34.6225,28.432],[34.5825,28.369],[34.545,28.277],[34.536,28.193],[34.49,28.0645],[34.466,28.025],[34.457,27.9005],[34.477,27.7895],[34.519,27.7135],[34.68,27.7],[34.9725,27.636],[35.0545,27.5545],[35.257,27.398],[35.3325,27.32],[35.4275,27.151],[35.507,27.0525],[35.566,26.8925],[35.5895,26.848],[35.8025,26.5485],[35.9015,26.4285],[36.0655,26.263],[36.1565,26.0965],[36.2775,25.9645],[36.309,25.8205],[36.2685,25.699],[36.259,25.633],[36.274,25.568],[36.314,25.5035],[36.502,25.272],[36.6545,25.124],[36.784,24.7945],[36.912,24.422],[36.9525,24.3235],[37.011,24.2445],[37.07,24.21],[37.291,24.155],[37.513,24.016],[37.71,23.731],[37.847,23.5425],[37.9045,23.486],[38.0875,23.3635],[38.389,22.9785],[38.394,22.75],[38.4315,22.6405],[38.636,22.361],[38.5525,22.1145],[38.543,22.0665],[38.5305,21.868],[38.5505,21.7705],[38.632,21.609],[38.8245,21.0375],[38.8555,20.9805],[38.9815,20.817],[39.078,20.651],[39.276,20.2135],[39.33,20.1315],[39.724,19.6405],[39.823,19.405],[39.889,19.1655],[39.9345,18.9605],[39.963,18.8935],[40.0145,18.841],[40.3195,18.6235],[40.4605,18.428],[40.513,18.1765],[40.595,17.9735],[40.629,17.9085],[40.8405,17.5675],[41.198,16.8665],[41.225,16.805],[41.315,16.6565],[41.4125,16.5445],[41.4935,16.432],[41.545,16.382],[41.68,16.29],[41.7555,16.29],[41.7835,16.29],[42.15,16.404],[42.772,16.404],[42.8305,16.3795],[42.95,16.3995],[42.945,16.49],[42.992,16.5215],[43.1205,16.5285],[43.1465,16.6365],[43.1285,16.6715],[43.2345,16.643],[43.2305,16.7305],[43.252,16.7795],[43.2215,16.838],[43.18,16.849],[43.138,16.9175],[43.178,16.9635],[43.191,17.016],[43.24,17.024],[43.227,17.0755],[43.158,17.114],[43.171,17.1725],[43.2165,17.2105],[43.2015,17.259],[43.326,17.3285],[43.228,17.3855],[43.241,17.4805],[43.355,17.5565],[43.4195,17.566],[43.4845,17.545],[43.5665,17.4835],[43.6835,17.3665],[43.789,17.375],[43.832,17.339],[43.9665,17.3305],[44.0115,17.4025],[44.0665,17.406],[44.1005,17.366],[44.138,17.409],[44.366,17.4335],[44.4665,17.4335],[44.5665,17.4055],[44.65,17.4335],[45.2165,17.4335],[45.4,17.3335],[46.1,17.25],[46.3665,17.2335],[46.75,17.2835],[47.0,16.95],[47.1835,16.95],[47.4665,17.1165],[47.6,17.45],[47.849,17.7565],[48.1835,18.1665],[48.588,18.3615],[49.1165,18.6165],[49.713,18.6785],[50.1295,18.7215],[50.7835,18.789],[51.4245,18.9],[52.0,19.0],[52.423,19.1415],[52.917,19.3065],[53.667,19.5565],[54.209,19.737],[54.644,19.8815],[55.0,20.0],[55.1495,20.453],[55.3355,21.007],[55.4795,21.438],[55.6665,22.0],[55.4815,22.287],[55.2115,22.7055],[55.1375,22.6315],[54.38,22.727],[53.8075,22.797],[53.3315,22.8535],[52.581,22.939],[52.1635,23.4425],[51.92,23.7375],[51.5905,24.127],[51.59,24.266],[51.5295,24.3365],[51.456,24.364],[51.416,24.393],[51.4665,24.4625],[51.5475,24.531],[51.5845,24.599],[51.586,24.664]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/0d/Flag_of_Saudi_Arabia.svg","name:en":"Saudi Arabia","wikidata":"Q851","ISO3166-1:alpha2":"SA","ISO3166-1:alpha3":"SAU","ISO3166-1:numeric":"682"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[44.7845,37.144],[44.7775,37.0965],[44.817,37.04],[44.8545,37.047],[44.904,37.004],[44.8825,36.954],[44.9125,36.9165],[44.8945,36.8535],[44.8385,36.816],[44.845,36.7785],[44.9425,36.7845],[44.9725,36.7495],[45.0305,36.7405],[45.0615,36.695],[45.071,36.63],[45.0265,36.6005],[45.0815,36.4255],[45.1505,36.4045],[45.2375,36.433],[45.2765,36.3765],[45.261,36.3015],[45.3025,36.275],[45.3365,36.204],[45.327,36.1395],[45.3815,36.084],[45.343,36.0095],[45.402,35.971],[45.4535,35.9945],[45.5555,35.9995],[45.6655,35.927],[45.7095,35.8825],[45.763,35.8],[45.8315,35.8095],[45.896,35.8375],[45.9555,35.823],[46.0585,35.856],[46.1375,35.8435],[46.1665,35.8005],[46.2125,35.795],[46.2895,35.827],[46.34,35.82],[46.347,35.787],[46.2335,35.715],[46.165,35.689],[46.101,35.6875],[46.0355,35.7055],[46.014,35.6845],[46.0135,35.574],[45.9855,35.496],[46.0385,35.4055],[46.1455,35.313],[46.1585,35.2845],[46.1125,35.235],[46.194,35.211],[46.157,35.164],[46.1915,35.111],[46.0675,35.043],[45.944,35.091],[45.8805,35.038],[45.8925,34.95],[45.865,34.8965],[45.7885,34.9115],[45.795,34.856],[45.7225,34.838],[45.658,34.7185],[45.702,34.6945],[45.7395,34.5865],[45.714,34.541],[45.577,34.5585],[45.515,34.555],[45.5255,34.493],[45.44,34.459],[45.4675,34.379],[45.562,34.3315],[45.583,34.248],[45.5555,34.198],[45.5815,34.146],[45.515,34.1125],[45.467,34.019],[45.4205,33.9875],[45.451,33.945],[45.501,33.951],[45.648,33.7635],[45.6745,33.699],[45.7715,33.637],[45.753,33.593],[45.858,33.632],[45.961,33.5615],[45.863,33.493],[45.954,33.4905],[46.008,33.5045],[46.078,33.3575],[46.134,33.292],[46.19,33.264],[46.1985,33.1915],[46.1235,33.116],[46.0845,33.0915],[46.148,33.0505],[46.094,32.985],[46.1735,32.9535],[46.244,32.97],[46.3125,32.969],[46.4155,32.9415],[46.5185,32.898],[46.656,32.8075],[46.7205,32.786],[46.887,32.632],[46.956,32.5975],[47.111,32.4895],[47.1685,32.4575],[47.247,32.4635],[47.282,32.4885],[47.378,32.474],[47.4655,32.3915],[47.4625,32.321],[47.5655,32.193],[47.529,32.142],[47.5995,32.1165],[47.646,32.06],[47.6975,32.022],[47.7315,31.9395],[47.8095,31.8815],[47.8065,31.842],[47.866,31.7835],[47.6855,31.395],[47.6875,31.003],[47.8,31.0005],[47.8705,31.01],[48.0355,30.998],[48.0435,30.724],[48.04,30.5445],[48.0285,30.4775],[48.136,30.444],[48.176,30.4155],[48.1955,30.336],[48.278,30.3325],[48.405,30.2165],[48.415,30.166],[48.386,30.1335],[48.4485,30.0595],[48.4515,30.0075],[48.508,29.9675],[48.611,29.9365],[48.68,29.888],[48.802,29.8245],[48.859,29.8165],[48.8545,29.7475],[49.4645,29.7895],[49.8425,29.39],[50.1315,29.084],[50.384,28.785],[50.702,28.4075],[50.9715,28.086],[51.2725,27.7255],[51.33,27.677],[51.8705,27.355],[52.4825,26.9895],[53.0915,26.6245],[53.128,26.606],[53.8205,26.3355],[53.964,26.2945],[54.2235,26.0575],[54.2695,25.9815],[54.2775,25.8605],[54.3345,25.762],[54.4275,25.7085],[54.552,25.692],[54.6505,25.715],[54.867,25.707],[54.9295,25.67],[55.001,25.6545],[55.1285,25.67],[55.228,25.733],[55.266,25.7945],[55.2765,25.887],[55.423,26.07],[55.509,26.14],[55.8935,26.412],[55.96,26.43],[56.1325,26.5035],[56.1735,26.547],[56.2415,26.5905],[56.298,26.6035],[56.546,26.7025],[56.587,26.742],[56.6705,26.903],[56.7195,26.8575],[56.7725,26.772],[56.8085,26.741],[56.845,26.647],[56.859,26.5685],[56.8295,26.519],[56.816,26.432],[56.853,26.291],[56.8845,26.2135],[56.9515,26.105],[56.9535,26.016],[56.978,25.9355],[57.036,25.8665],[57.071,25.8425],[57.096,25.713],[57.1465,25.6475],[57.186,25.619],[57.2965,25.5835],[57.678,25.449],[58.053,25.3575],[58.632,25.269],[59.057,25.2035],[59.092,25.201],[59.5675,25.1955],[60.1705,25.124],[60.3605,25.095],[60.4195,25.0795],[60.5765,25.073],[60.956,24.972],[61.3545,24.8665],[61.5495,24.8465],[61.5755,25.0455],[61.6295,25.2095],[61.6505,25.306],[61.679,25.6585],[61.725,25.726],[61.84,25.745],[61.849,25.8225],[61.825,25.9605],[61.845,26.019],[61.8415,26.065],[61.86,26.176],[61.9175,26.254],[61.997,26.2805],[62.07,26.327],[62.1,26.2845],[62.215,26.268],[62.253,26.3595],[62.2595,26.4085],[62.314,26.473],[62.3035,26.5085],[62.37,26.5395],[62.42,26.533],[62.44,26.565],[62.604,26.5765],[62.6485,26.6085],[62.6965,26.609],[62.755,26.646],[63.0165,26.646],[63.049,26.6275],[63.1005,26.6505],[63.195,26.6465],[63.202,26.7555],[63.187,26.8365],[63.2415,26.8415],[63.2535,26.9035],[63.251,27.092],[63.309,27.1295],[63.277,27.218],[63.2255,27.2255],[63.1985,27.2595],[63.122,27.237],[63.0405,27.2375],[62.947,27.202],[62.8775,27.226],[62.8205,27.2185],[62.809,27.264],[62.8265,27.31],[62.7965,27.3335],[62.8485,27.463],[62.829,27.7645],[62.761,28.026],[62.7945,28.2275],[62.793,28.279],[62.597,28.2485],[62.463,28.378],[62.3885,28.4325],[62.282,28.4505],[62.1685,28.489],[62.044,28.51],[62.008,28.5335],[61.91,28.5645],[61.809,28.649],[61.751,28.718],[61.663,28.784],[61.602,28.873],[61.5335,29.01],[61.4715,29.091],[61.428,29.128],[61.427,29.199],[61.353,29.276],[61.373,29.3395],[61.1625,29.5335],[60.873,29.8585],[61.343,30.359],[61.5115,30.5385],[61.805,30.8395],[61.782,30.911],[61.814,30.9555],[61.8305,31.092],[61.777,31.214],[61.7785,31.3025],[61.7065,31.376],[60.856,31.4865],[60.8135,31.7085],[60.818,31.89],[60.8325,31.98],[60.819,32.0115],[60.883,32.1975],[60.859,32.2665],[60.815,32.4815],[60.7095,32.696],[60.6295,33.021],[60.5835,33.13],[60.634,33.228],[60.8245,33.4],[60.8665,33.4215],[60.851,33.482],[60.903,33.54],[60.7035,33.5455],[60.5815,33.597],[60.5515,33.64],[60.547,33.7315],[60.582,33.8055],[60.5175,34.062],[60.5375,34.1505],[60.6205,34.244],[60.669,34.272],[60.6865,34.3105],[60.9185,34.305],[60.8495,34.3685],[60.8075,34.462],[60.7425,34.527],[60.8485,34.542],[60.8975,34.5675],[60.922,34.623],[60.9845,34.62],[61.003,34.7085],[61.068,34.8125],[61.0575,34.9075],[61.0975,34.952],[61.1085,35.03],[61.14,35.102],[61.093,35.183],[61.092,35.2635],[61.1905,35.293],[61.1875,35.3995],[61.2265,35.4215],[61.2335,35.4625],[61.2825,35.538],[61.274,35.605],[61.226,35.6505],[61.262,35.8135],[61.241,35.8855],[61.1835,35.943],[61.164,36.033],[61.2285,36.1125],[61.2265,36.1665],[61.1615,36.297],[61.1425,36.393],[61.1665,36.4135],[61.157,36.4865],[61.1855,36.5495],[61.146,36.6465],[60.675,36.622],[60.6085,36.628],[60.3455,36.6295],[60.267,36.757],[60.215,36.8205],[60.1695,36.848],[60.0945,36.9365],[60.085,36.978],[60.043,37.021],[59.9815,37.047],[59.931,37.0425],[59.772,37.1255],[59.6535,37.142],[59.6265,37.1185],[59.558,37.1315],[59.538,37.1855],[59.435,37.304],[59.397,37.3175],[59.378,37.4075],[59.392,37.479],[59.3535,37.5285],[59.229,37.512],[59.0605,37.63],[58.926,37.67],[58.8765,37.669],[58.836,37.6995],[58.8045,37.667],[58.713,37.6485],[58.6655,37.655],[58.55,37.706],[58.498,37.648],[58.3885,37.627],[58.3595,37.661],[58.227,37.684],[58.229,37.7295],[58.2035,37.7755],[58.155,37.7935],[58.028,37.806],[57.8915,37.871],[57.8255,37.863],[57.796,37.8995],[57.7205,37.922],[57.6285,37.922],[57.5795,37.938],[57.512,37.9195],[57.458,37.938],[57.3485,37.999],[57.373,38.0755],[57.2465,38.2665],[57.161,38.272],[57.1345,38.236],[57.051,38.1935],[56.992,38.2145],[56.91,38.2145],[56.7925,38.2515],[56.7565,38.285],[56.664,38.2685],[56.614,38.2405],[56.5465,38.266],[56.4245,38.2545],[56.333,38.1975],[56.3195,38.1745],[56.352,38.117],[56.303,38.083],[56.216,38.072],[56.173,38.0945],[56.0215,38.0775],[55.842,38.102],[55.7485,38.1255],[55.443,38.086],[55.3845,38.042],[55.2735,37.9905],[55.127,37.9455],[55.0395,37.8635],[54.942,37.79],[54.843,37.745],[54.7885,37.647],[54.819,37.6005],[54.7985,37.522],[54.7315,37.488],[54.6735,37.437],[54.5825,37.4585],[54.4875,37.418],[54.3975,37.3605],[54.285,37.352],[54.2445,37.32],[53.8965,37.3475],[53.861,37.322],[53.8775,37.247],[53.8625,37.204],[53.897,37.0185],[53.8525,36.9845],[53.2905,36.931],[52.9815,36.875],[52.7355,36.823],[52.452,36.7775],[52.168,36.7125],[52.0385,36.675],[51.9165,36.6625],[51.6235,36.7195],[51.488,36.775],[51.217,36.8155],[50.9555,36.8935],[50.81,36.9575],[50.439,37.1715],[50.2925,37.436],[50.2325,37.4655],[50.0335,37.501],[49.957,37.552],[49.8575,37.551],[49.787,37.527],[49.4285,37.574],[49.1955,37.675],[49.1205,37.7505],[49.0665,37.9665],[49.2125,38.4085],[48.7915,38.4515],[48.7385,38.411],[48.6595,38.3935],[48.608,38.4155],[48.5915,38.453],[48.472,38.5535],[48.425,38.6135],[48.321,38.602],[48.295,38.6475],[48.2525,38.662],[48.249,38.726],[48.192,38.756],[48.1075,38.77],[48.0205,38.838],[48.028,38.8995],[48.088,38.947],[48.1455,38.946],[48.271,38.9675],[48.339,39.034],[48.303,39.109],[48.2305,39.134],[48.2135,39.1665],[48.141,39.208],[48.13,39.271],[48.211,39.329],[48.248,39.3305],[48.361,39.394],[48.332,39.4285],[48.032,39.6925],[47.9855,39.709],[47.9145,39.6615],[47.8125,39.659],[47.753,39.606],[47.4205,39.465],[47.296,39.367],[47.2155,39.319],[47.133,39.292],[47.058,39.238],[47.054,39.194],[46.9635,39.1405],[46.9255,39.1645],[46.8665,39.1405],[46.7905,39.087],[46.762,39.037],[46.6955,39.018],[46.6735,38.978],[46.6035,38.91],[46.534,38.867],[46.458,38.897],[46.425,38.8855],[46.363,38.9165],[46.2825,38.9005],[46.187,38.843],[46.1405,38.8445],[46.112,38.865],[45.979,38.879],[45.918,38.8735],[45.737,38.9275],[45.69,38.9515],[45.6185,38.9425],[45.455,38.9905],[45.4525,39.055],[45.3525,39.133],[45.3135,39.2],[45.2475,39.1855],[45.185,39.2205],[45.0865,39.351],[45.0175,39.406],[44.964,39.424],[44.952,39.492],[44.91,39.525],[44.9035,39.577],[44.8095,39.6275],[44.7135,39.7145],[44.669,39.7145],[44.6145,39.7825],[44.467,39.684],[44.48,39.6105],[44.416,39.5565],[44.4395,39.501],[44.432,39.443],[44.295,39.3755],[44.233,39.4155],[44.1415,39.3975],[44.051,39.403],[44.0375,39.3615],[44.0755,39.332],[44.094,39.2155],[44.1665,39.1775],[44.2135,39.1305],[44.191,39.0765],[44.2115,39.0205],[44.181,39.0105],[44.1895,38.934],[44.2115,38.89],[44.2965,38.841],[44.315,38.809],[44.261,38.7055],[44.2815,38.6415],[44.3215,38.6245],[44.3095,38.531],[44.3255,38.502],[44.3055,38.441],[44.3165,38.3745],[44.3765,38.3595],[44.4395,38.3835],[44.4975,38.3335],[44.478,38.3025],[44.402,38.251],[44.3925,38.1465],[44.351,38.135],[44.3015,38.0325],[44.2405,37.9555],[44.2225,37.888],[44.268,37.867],[44.317,37.8765],[44.403,37.852],[44.392,37.828],[44.445,37.771],[44.5305,37.783],[44.588,37.7635],[44.6245,37.698],[44.568,37.645],[44.6145,37.6005],[44.5875,37.4395],[44.619,37.437],[44.6575,37.386],[44.711,37.38],[44.743,37.3345],[44.798,37.311],[44.815,37.2795],[44.7575,37.2285],[44.7845,37.144]]],[[[50.367,27.887],[50.3955,27.95],[50.3965,28.028],[50.368,28.0965],[50.309,28.1545],[50.225,28.1895],[50.0975,28.1845],[50.03,28.1515],[49.9785,28.1],[49.95,28.036],[49.95,27.951],[49.9835,27.882],[50.367,27.887]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/ca/Flag_of_Iran.svg","name:en":"Iran","wikidata":"Q794","ISO3166-1:alpha2":"IR","ISO3166-1:alpha3":"IRN","ISO3166-1:numeric":"364"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[76.776,35.658],[76.691,35.7485],[76.6155,35.7615],[76.561,35.8225],[76.599,35.9],[76.556,35.927],[76.5175,35.8865],[76.4685,35.891],[76.427,35.8525],[76.3665,35.8635],[76.3565,35.8315],[76.2245,35.8425],[76.145,35.8375],[76.1595,35.917],[76.107,35.977],[76.101,36.016],[76.0045,36.017],[75.957,36.046],[75.9465,36.119],[75.9695,36.1635],[76.014,36.1725],[76.0065,36.234],[76.0575,36.246],[75.995,36.3075],[75.985,36.3415],[76.0355,36.409],[75.9875,36.5025],[75.926,36.573],[75.9405,36.593],[75.804,36.71],[75.7195,36.752],[75.6375,36.77],[75.534,36.7715],[75.4695,36.716],[75.4195,36.7735],[75.432,36.83],[75.406,36.9365],[75.2855,36.9745],[75.2335,36.959],[75.1255,37.0125],[74.919,36.979],[74.906,36.9295],[74.8405,37.02],[74.739,37.0215],[74.702,37.0825],[74.5645,37.031],[74.519,37.0175],[74.5435,36.967],[74.4195,37.0075],[74.4135,36.9755],[74.35,36.9625],[74.3095,36.919],[74.237,36.895],[74.1805,36.9165],[74.121,36.838],[74.048,36.8265],[73.9575,36.8425],[73.9035,36.876],[73.8905,36.9125],[73.836,36.9255],[73.8,36.8905],[73.6915,36.916],[73.548,36.891],[73.4005,36.899],[73.1775,36.879],[73.083,36.887],[73.0295,36.8615],[72.943,36.868],[72.93,36.843],[72.818,36.8415],[72.768,36.854],[72.7175,36.837],[72.6525,36.852],[72.5615,36.829],[72.5025,36.784],[72.3355,36.7665],[72.2985,36.74],[72.2535,36.7525],[72.1845,36.7115],[72.2095,36.6635],[72.0965,36.647],[72.088,36.5965],[72.0305,36.592],[72.0035,36.5575],[71.936,36.5465],[71.9165,36.5085],[71.8095,36.508],[71.8205,36.4345],[71.8065,36.3995],[71.657,36.4835],[71.616,36.403],[71.558,36.3725],[71.583,36.345],[71.4505,36.272],[71.436,36.2325],[71.3605,36.204],[71.214,36.0825],[71.197,36.041],[71.3225,35.959],[71.368,35.968],[71.396,35.8915],[71.431,35.888],[71.4775,35.8215],[71.494,35.7495],[71.553,35.7225],[71.5365,35.6695],[71.4995,35.6295],[71.521,35.6065],[71.6205,35.571],[71.594,35.5],[71.6555,35.437],[71.5445,35.3125],[71.564,35.2785],[71.6335,35.2255],[71.6815,35.2095],[71.625,35.139],[71.532,35.091],[71.5525,35.0225],[71.507,35.006],[71.475,34.9495],[71.351,34.9145],[71.296,34.8775],[71.2775,34.7995],[71.187,34.7495],[71.0895,34.6785],[71.1145,34.628],[71.025,34.5595],[71.011,34.534],[71.024,34.4505],[71.0595,34.4245],[71.1075,34.4335],[71.1155,34.377],[71.173,34.365],[71.13,34.2825],[71.13,34.1645],[71.071,34.1095],[71.0825,34.0635],[71.0305,34.054],[70.8835,33.974],[70.822,33.9785],[70.777,33.962],[70.643,33.9525],[70.584,33.9625],[70.547,33.943],[70.4595,33.945],[70.4145,33.9675],[70.325,33.966],[70.2905,33.9815],[70.194,33.983],[70.149,34.0145],[70.0875,34.0085],[70.0485,34.035],[69.9045,34.0395],[69.856,33.949],[69.9155,33.887],[69.9715,33.7585],[70.1535,33.695],[70.1435,33.658],[70.1985,33.6195],[70.1715,33.539],[70.1995,33.4875],[70.251,33.44],[70.297,33.43],[70.3285,33.343],[70.166,33.2215],[70.096,33.205],[70.0735,33.22],[70.0205,33.1385],[69.941,33.131],[69.879,33.0945],[69.7985,33.131],[69.672,33.101],[69.573,33.0985],[69.4995,33.0205],[69.4955,32.933],[69.5065,32.896],[69.441,32.804],[69.3875,32.768],[69.438,32.731],[69.454,32.6585],[69.371,32.5625],[69.278,32.529],[69.2395,32.4645],[69.278,32.3615],[69.27,32.162],[69.292,32.095],[69.3025,31.987],[69.33,31.9295],[69.193,31.847],[69.116,31.7005],[69.0405,31.6615],[69.02,31.6295],[68.96,31.649],[68.908,31.5965],[68.7895,31.6245],[68.7875,31.6545],[68.728,31.6955],[68.7085,31.771],[68.643,31.778],[68.5755,31.834],[68.475,31.7735],[68.591,31.753],[68.5655,31.7145],[68.523,31.7355],[68.4615,31.735],[68.425,31.756],[68.2795,31.757],[68.2655,31.8035],[68.1795,31.817],[68.1465,31.8045],[68.0705,31.696],[67.9745,31.6405],[67.8575,31.624],[67.7165,31.514],[67.6465,31.5285],[67.571,31.5245],[67.651,31.3925],[67.75,31.4165],[67.796,31.383],[67.7875,31.3325],[67.722,31.327],[67.678,31.2935],[67.4855,31.2325],[67.4,31.211],[67.279,31.1995],[67.1975,31.217],[67.1695,31.2455],[67.07,31.214],[67.024,31.2565],[67.033,31.3105],[66.956,31.308],[66.834,31.265],[66.813,31.223],[66.7155,31.186],[66.688,31.0825],[66.5785,30.9765],[66.392,30.9405],[66.288,30.577],[66.2865,30.5255],[66.3565,30.4765],[66.3625,30.4185],[66.3415,30.3845],[66.3265,30.2475],[66.289,30.153],[66.232,30.063],[66.373,29.9715],[66.332,29.92],[66.25,29.8495],[65.8095,29.7295],[65.6965,29.705],[65.493,29.6445],[65.065,29.5295],[64.5715,29.5865],[64.3445,29.525],[64.2635,29.522],[64.249,29.499],[64.1795,29.4835],[64.142,29.4365],[64.166,29.408],[64.128,29.3785],[64.0545,29.4075],[63.5765,29.487],[63.233,29.4575],[62.471,29.3895],[61.8975,29.56],[61.564,29.658],[60.873,29.8585],[61.1625,29.5335],[61.373,29.3395],[61.353,29.276],[61.427,29.199],[61.428,29.128],[61.4715,29.091],[61.5335,29.01],[61.602,28.873],[61.663,28.784],[61.751,28.718],[61.809,28.649],[61.91,28.5645],[62.008,28.5335],[62.044,28.51],[62.1685,28.489],[62.282,28.4505],[62.3885,28.4325],[62.463,28.378],[62.597,28.2485],[62.793,28.279],[62.7945,28.2275],[62.761,28.026],[62.829,27.7645],[62.8485,27.463],[62.7965,27.3335],[62.8265,27.31],[62.809,27.264],[62.8205,27.2185],[62.8775,27.226],[62.947,27.202],[63.0405,27.2375],[63.122,27.237],[63.1985,27.2595],[63.2255,27.2255],[63.277,27.218],[63.309,27.1295],[63.251,27.092],[63.2535,26.9035],[63.2415,26.8415],[63.187,26.8365],[63.202,26.7555],[63.195,26.6465],[63.1005,26.6505],[63.049,26.6275],[63.0165,26.646],[62.755,26.646],[62.6965,26.609],[62.6485,26.6085],[62.604,26.5765],[62.44,26.565],[62.42,26.533],[62.37,26.5395],[62.3035,26.5085],[62.314,26.473],[62.2595,26.4085],[62.253,26.3595],[62.215,26.268],[62.1,26.2845],[62.07,26.327],[61.997,26.2805],[61.9175,26.254],[61.86,26.176],[61.8415,26.065],[61.845,26.019],[61.825,25.9605],[61.849,25.8225],[61.84,25.745],[61.725,25.726],[61.679,25.6585],[61.6505,25.306],[61.6295,25.2095],[61.5755,25.0455],[61.5495,24.8465],[61.732,24.8415],[61.879,24.876],[61.951,24.918],[61.985,24.9805],[62.2825,24.974],[62.4215,24.98],[62.4885,25.021],[62.5625,25.1195],[62.927,25.0515],[62.9875,25.044],[63.083,25.0725],[63.1445,25.103],[63.4585,25.06],[63.6595,25.025],[63.882,24.9805],[63.9825,25.02],[64.0505,25.067],[64.0825,25.1085],[64.3155,25.039],[64.5305,25.033],[64.61,25.019],[64.775,25.0795],[64.887,25.168],[65.121,25.138],[65.2615,25.154],[65.341,25.2075],[65.3475,25.2355],[65.52,25.218],[65.6025,25.1915],[65.664,25.1845],[65.843,25.245],[65.875,25.273],[66.063,25.2775],[66.2585,25.3205],[66.396,25.2645],[66.521,25.1705],[66.498,25.039],[66.4555,24.976],[66.4155,24.8695],[66.454,24.753],[66.5855,24.6895],[66.7025,24.6895],[66.795,24.713],[66.948,24.601],[67.019,24.513],[67.1025,24.2],[67.1645,24.025],[67.2375,23.858],[67.3755,23.7135],[67.5205,23.6545],[67.742,23.6015],[68.115,23.5395],[68.1115,23.6015],[68.1355,23.6485],[68.186,23.6885],[68.207,23.8445],[68.2905,23.9445],[68.32,23.9135],[68.3795,23.971],[68.753,23.9715],[68.7695,24.2655],[68.7645,24.296],[68.831,24.3115],[68.8565,24.214],[68.88,24.216],[68.907,24.28],[68.9455,24.3025],[68.985,24.2275],[69.013,24.224],[69.095,24.2745],[69.194,24.2365],[69.313,24.2815],[69.367,24.27],[69.4445,24.2805],[69.5035,24.2685],[69.5945,24.2925],[69.7315,24.171],[70.025,24.171],[70.0665,24.199],[70.11,24.295],[70.254,24.3255],[70.327,24.352],[70.573,24.4225],[70.6025,24.408],[70.5625,24.351],[70.585,24.278],[70.5715,24.252],[70.6445,24.225],[70.714,24.2155],[70.8055,24.221],[70.8515,24.245],[70.8785,24.302],[70.986,24.3655],[71.0495,24.356],[71.103,24.4355],[70.9985,24.4445],[71.005,24.523],[70.9865,24.5955],[71.009,24.6355],[71.0745,24.662],[71.0655,24.7175],[71.0265,24.809],[70.9375,24.9385],[70.8885,25.148],[70.7515,25.2775],[70.737,25.3325],[70.665,25.397],[70.6785,25.5255],[70.667,25.5315],[70.674,25.676],[70.645,25.713],[70.6085,25.715],[70.53,25.6835],[70.387,25.6735],[70.2685,25.7135],[70.2225,25.793],[70.1745,25.8285],[70.1,25.938],[70.0835,26.0835],[70.141,26.156],[70.175,26.2515],[70.1635,26.295],[70.1855,26.374],[70.1745,26.5515],[70.1175,26.587],[70.0565,26.6005],[69.886,26.5785],[69.792,26.598],[69.7255,26.652],[69.5105,26.7435],[69.4845,26.805],[69.5135,27.01],[69.587,27.1835],[69.703,27.2845],[69.863,27.402],[69.934,27.4975],[70.026,27.563],[70.1325,27.806],[70.227,27.9015],[70.296,27.936],[70.372,28.012],[70.5055,28.037],[70.5895,28.0105],[70.6775,27.922],[70.6845,27.828],[70.7595,27.721],[70.906,27.7095],[71.2055,27.8355],[71.383,27.8725],[71.6665,27.878],[71.8985,27.961],[71.9275,28.1215],[72.0045,28.2185],[72.1325,28.313],[72.206,28.394],[72.2995,28.6695],[72.404,28.783],[72.4795,28.812],[72.733,28.948],[72.946,29.028],[73.0045,29.153],[73.0855,29.238],[73.2825,29.572],[73.397,29.946],[73.597,30.019],[73.8065,30.068],[73.83,30.0935],[73.9725,30.1985],[73.9595,30.27],[73.9025,30.349],[73.946,30.468],[73.993,30.514],[74.0725,30.523],[74.1285,30.6325],[74.149,30.6315],[74.2745,30.7355],[74.3205,30.8465],[74.38,30.862],[74.366,30.893],[74.48,30.974],[74.5455,30.992],[74.548,31.032],[74.671,31.0535],[74.7025,31.0915],[74.689,31.128],[74.5515,31.0865],[74.5105,31.132],[74.537,31.33],[74.5965,31.4165],[74.6435,31.416],[74.635,31.4845],[74.5875,31.504],[74.616,31.5575],[74.5555,31.608],[74.535,31.6775],[74.4865,31.715],[74.5595,31.758],[74.5705,31.837],[74.6155,31.89],[74.677,31.926],[74.819,31.9585],[74.8285,31.998],[74.9265,32.0665],[75.0125,32.0355],[75.123,32.0815],[75.1795,32.0765],[75.2205,32.1075],[75.261,32.1015],[75.314,32.172],[75.3315,32.2195],[75.373,32.2285],[75.3715,32.2795],[75.2895,32.3695],[75.238,32.387],[75.192,32.426],[75.137,32.41],[75.109,32.46],[75.0265,32.4975],[75.0155,32.4645],[74.943,32.452],[74.859,32.4935],[74.8125,32.48],[74.7295,32.481],[74.6855,32.4995],[74.69,32.5345],[74.6515,32.563],[74.657,32.6305],[74.695,32.6605],[74.6535,32.697],[74.7045,32.817],[74.6585,32.83],[74.6305,32.7675],[74.5185,32.7485],[74.4485,32.7935],[74.4195,32.84],[74.42,32.8855],[74.3385,32.9555],[74.318,33.032],[74.2015,33.0665],[74.154,33.1365],[74.0705,33.194],[74.017,33.205],[74.018,33.2705],[74.1265,33.3045],[74.1665,33.3375],[74.1885,33.4085],[74.1925,33.4785],[74.1405,33.5575],[74.081,33.585],[74.0315,33.5785],[73.981,33.6415],[73.9815,33.707],[74.01,33.7765],[74.06,33.824],[74.1895,33.839],[74.2815,33.901],[74.277,33.9695],[74.233,34.0235],[74.1975,34.017],[74.1325,34.047],[74.016,34.0395],[73.965,34.0135],[73.896,34.06],[73.927,34.1275],[74.0035,34.1905],[73.9905,34.2705],[73.9455,34.276],[73.939,34.3365],[73.859,34.324],[73.774,34.3325],[73.7505,34.369],[73.803,34.42],[73.88,34.4535],[73.905,34.528],[73.9535,34.5605],[73.932,34.6415],[73.969,34.6855],[74.142,34.6605],[74.158,34.6885],[74.2415,34.727],[74.3095,34.7825],[74.4485,34.7825],[74.58,34.7615],[74.651,34.706],[74.7305,34.681],[74.936,34.673],[75.026,34.6295],[75.163,34.6565],[75.267,34.6365],[75.2475,34.6035],[75.301,34.5865],[75.365,34.538],[75.463,34.5345],[75.6705,34.54],[75.785,34.5135],[75.9455,34.6255],[76.063,34.688],[76.116,34.6395],[76.1635,34.6325],[76.2505,34.6765],[76.2825,34.7235],[76.328,34.7195],[76.4195,34.7375],[76.4865,34.796],[76.541,34.762],[76.642,34.734],[76.697,34.7505],[76.7015,34.796],[76.737,34.8065],[76.7455,34.8885],[76.792,34.954],[76.8615,34.9485],[76.898,34.926],[76.961,35.0175],[77.047,35.0515],[77.1105,35.049],[77.0825,35.099],[77.0855,35.169],[77.0175,35.183],[76.975,35.2515],[77.0065,35.2945],[76.948,35.393],[76.863,35.3895],[76.8385,35.443],[76.759,35.5185],[76.7495,35.555],[76.7935,35.588],[76.755,35.6295],[76.776,35.658]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/32/Flag_of_Pakistan.svg","name:en":"Pakistan","wikidata":"Q843","ISO3166-1:alpha2":"PK","ISO3166-1:alpha3":"PAK","ISO3166-1:numeric":"586"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[88.135,27.8815],[88.091,27.8715],[88.027,27.9055],[87.983,27.888],[87.9305,27.918],[87.866,27.911],[87.834,27.952],[87.7855,27.9005],[87.725,27.8055],[87.6645,27.8375],[87.606,27.8185],[87.5785,27.862],[87.4895,27.846],[87.4475,27.8225],[87.4255,27.8595],[87.3165,27.8275],[87.2645,27.851],[87.227,27.821],[87.157,27.8245],[87.111,27.8455],[87.073,27.9205],[87.038,27.9505],[86.9325,27.9615],[86.9245,27.988],[86.7495,28.046],[86.7485,28.0985],[86.6655,28.1025],[86.6075,28.075],[86.575,28.1135],[86.5355,28.051],[86.514,27.957],[86.468,27.946],[86.442,27.906],[86.372,27.942],[86.3195,27.948],[86.2885,27.978],[86.2265,27.9815],[86.2045,28.0785],[86.2185,28.1135],[86.16,28.1385],[86.118,28.091],[86.084,28.088],[86.0805,28.022],[86.1245,27.9275],[86.065,27.9],[86.0,27.9115],[85.9515,27.945],[85.9775,27.988],[85.9,28.0545],[85.897,28.111],[85.8495,28.1715],[85.7505,28.2365],[85.7315,28.337],[85.6835,28.3825],[85.6625,28.303],[85.6085,28.2565],[85.5915,28.308],[85.46,28.3365],[85.4135,28.325],[85.377,28.2775],[85.3375,28.3035],[85.255,28.293],[85.204,28.3405],[85.183,28.323],[85.1085,28.3465],[85.1275,28.3925],[85.118,28.4855],[85.147,28.4875],[85.1905,28.5695],[85.157,28.644],[85.1165,28.6855],[85.058,28.6825],[84.9485,28.5805],[84.9205,28.595],[84.8565,28.571],[84.78,28.6105],[84.696,28.6365],[84.6255,28.7365],[84.558,28.746],[84.493,28.7365],[84.4375,28.77],[84.435,28.821],[84.392,28.86],[84.3285,28.8615],[84.226,28.893],[84.2255,28.9435],[84.249,29.0365],[84.187,29.081],[84.168,29.2005],[84.0915,29.294],[83.903,29.327],[83.814,29.3025],[83.7685,29.241],[83.711,29.244],[83.632,29.159],[83.5705,29.203],[83.518,29.2185],[83.5145,29.2555],[83.4435,29.2995],[83.4135,29.422],[83.3715,29.4285],[83.345,29.495],[83.282,29.499],[83.2175,29.605],[83.15,29.6245],[83.0695,29.61],[83.038,29.649],[82.9565,29.662],[82.928,29.7],[82.869,29.687],[82.8185,29.6965],[82.765,29.731],[82.738,29.814],[82.697,29.8565],[82.603,29.889],[82.5335,29.971],[82.497,29.9475],[82.4215,30.0055],[82.313,30.0385],[82.2845,30.0585],[82.1665,30.076],[82.1855,30.111],[82.185,30.19],[82.141,30.197],[82.117,30.258],[82.127,30.304],[82.1065,30.349],[81.988,30.322],[81.9375,30.3475],[81.75,30.3885],[81.634,30.445],[81.6065,30.4115],[81.5645,30.4295],[81.5395,30.373],[81.429,30.3835],[81.396,30.3235],[81.4225,30.3055],[81.39,30.2345],[81.3955,30.207],[81.2825,30.123],[81.2715,30.0445],[81.1655,30.0105],[81.092,30.0535],[81.109,30.0855],[81.0345,30.197],[80.8715,30.1605],[80.878,30.1285],[80.8055,30.09],[80.7385,29.999],[80.6745,29.957],[80.6015,29.958],[80.574,29.9235],[80.5535,29.853],[80.493,29.795],[80.4175,29.796],[80.366,29.726],[80.385,29.6735],[80.4165,29.652],[80.408,29.597],[80.3485,29.5555],[80.342,29.5105],[80.298,29.49],[80.3025,29.4545],[80.246,29.447],[80.2795,29.3475],[80.318,29.3045],[80.29,29.2315],[80.262,29.206],[80.2715,29.1465],[80.2355,29.1175],[80.1865,29.137],[80.1455,29.1045],[80.127,29.006],[80.059,28.916],[80.0645,28.8375],[80.1185,28.828],[80.216,28.7555],[80.2565,28.754],[80.275,28.711],[80.377,28.629],[80.4605,28.622],[80.5225,28.5525],[80.5035,28.665],[80.5395,28.6905],[80.614,28.639],[80.6675,28.642],[80.7145,28.5685],[80.768,28.5655],[80.9055,28.466],[81.0335,28.428],[81.0815,28.3845],[81.211,28.361],[81.2325,28.289],[81.321,28.1975],[81.313,28.1565],[81.368,28.141],[81.3745,28.177],[81.447,28.161],[81.484,28.1185],[81.4785,28.0825],[81.644,27.9935],[81.6985,27.988],[81.805,27.904],[81.9,27.8535],[81.9685,27.929],[82.0625,27.9215],[82.121,27.866],[82.209,27.843],[82.3035,27.773],[82.368,27.7425],[82.402,27.7035],[82.473,27.6765],[82.605,27.706],[82.7075,27.715],[82.757,27.5835],[82.7365,27.5025],[82.9295,27.501],[82.954,27.468],[83.0345,27.449],[83.188,27.4545],[83.272,27.3835],[83.2965,27.3335],[83.3375,27.3325],[83.388,27.3755],[83.4075,27.415],[83.389,27.48],[83.6145,27.469],[83.8295,27.3705],[83.865,27.371],[83.874,27.4345],[84.052,27.4435],[84.1055,27.521],[84.176,27.4745],[84.254,27.453],[84.294,27.385],[84.6225,27.336],[84.682,27.2365],[84.6705,27.091],[84.6435,27.046],[84.7575,27.003],[84.8195,27.0215],[84.855,26.986],[84.9625,26.9605],[85.0575,26.849],[85.099,26.871],[85.193,26.8665],[85.177,26.8145],[85.197,26.771],[85.334,26.742],[85.4075,26.7915],[85.4515,26.7815],[85.543,26.8385],[85.642,26.853],[85.7205,26.8205],[85.735,26.7795],[85.723,26.687],[85.732,26.654],[85.851,26.6085],[85.861,26.5705],[85.974,26.657],[86.028,26.6665],[86.141,26.617],[86.306,26.62],[86.542,26.5385],[86.57,26.4965],[86.694,26.451],[86.737,26.423],[86.7655,26.4595],[86.8335,26.439],[86.894,26.462],[86.932,26.516],[87.0725,26.543],[87.0915,26.4505],[87.1615,26.404],[87.247,26.4145],[87.266,26.374],[87.3135,26.3675],[87.3885,26.419],[87.4665,26.4405],[87.516,26.431],[87.5875,26.393],[87.6515,26.3935],[87.678,26.416],[87.7695,26.4215],[87.793,26.469],[87.8485,26.437],[87.9,26.4775],[87.916,26.434],[87.992,26.3695],[88.0775,26.419],[88.1035,26.4665],[88.1055,26.561],[88.1635,26.646],[88.1895,26.7465],[88.172,26.869],[88.136,26.899],[88.118,26.9875],[88.083,27.029],[88.0375,27.037],[87.991,27.131],[88.007,27.1435],[88.0315,27.2865],[88.0665,27.3365],[88.0415,27.3705],[88.0785,27.432],[88.044,27.4785],[88.085,27.5905],[88.144,27.665],[88.1585,27.741],[88.197,27.791],[88.201,27.8375],[88.135,27.8815]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9b/Flag_of_Nepal.svg","name:en":"Nepal","wikidata":"Q837","ISO3166-1:alpha2":"NP","ISO3166-1:alpha3":"NPL","ISO3166-1:numeric":"524"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[70.894,40.2315],[70.8855,40.188],[70.7995,40.1475],[70.7685,40.1165],[70.6575,40.11],[70.6095,40.043],[70.5675,40.0305],[70.498,40.054],[70.4245,40.0575],[70.358,40.081],[70.292,40.1295],[70.241,40.124],[70.1755,40.143],[70.021,40.2305],[69.9955,40.2345],[69.784,40.1795],[69.6755,40.1225],[69.582,40.1045],[69.537,40.117],[69.507,40.034],[69.5355,39.9525],[69.4575,39.935],[69.432,39.9885],[69.3945,40.0025],[69.297,39.9205],[69.327,39.877],[69.265,39.806],[69.3265,39.7205],[69.3375,39.5425],[69.408,39.5495],[69.448,39.5285],[69.574,39.553],[69.6295,39.593],[69.678,39.5805],[69.7375,39.595],[69.8285,39.56],[69.867,39.5295],[69.9015,39.576],[69.978,39.568],[69.996,39.537],[70.127,39.5865],[70.168,39.554],[70.2065,39.5785],[70.239,39.538],[70.309,39.53],[70.3295,39.562],[70.3895,39.582],[70.4645,39.583],[70.525,39.622],[70.606,39.586],[70.65,39.582],[70.6925,39.52],[70.7435,39.497],[70.768,39.3995],[70.9125,39.3835],[70.941,39.4335],[71.0165,39.3955],[71.072,39.4255],[71.0895,39.499],[71.1935,39.5115],[71.2585,39.549],[71.3075,39.5235],[71.3355,39.5685],[71.413,39.571],[71.5145,39.6115],[71.565,39.576],[71.544,39.505],[71.5655,39.457],[71.633,39.4455],[71.761,39.4565],[71.804,39.403],[71.7525,39.324],[71.802,39.2735],[71.919,39.2805],[71.958,39.3315],[72.0355,39.3665],[72.089,39.278],[72.1535,39.2735],[72.202,39.2415],[72.2195,39.1825],[72.271,39.1895],[72.3,39.2925],[72.3425,39.332],[72.4625,39.35],[72.493,39.38],[72.589,39.3595],[72.621,39.399],[72.694,39.396],[72.7825,39.362],[72.959,39.3465],[73.1065,39.3765],[73.1605,39.3525],[73.2525,39.381],[73.353,39.3935],[73.3935,39.4525],[73.505,39.4735],[73.6005,39.4595],[73.638,39.474],[73.7255,39.461],[73.8745,39.483],[73.8785,39.5425],[73.949,39.6215],[73.904,39.7445],[73.832,39.7815],[73.848,39.8345],[73.903,39.8615],[73.907,39.9205],[73.977,40.005],[73.983,40.0485],[74.042,40.0995],[74.066,40.0785],[74.2,40.1235],[74.304,40.115],[74.3445,40.0935],[74.5255,40.209],[74.5905,40.282],[74.6605,40.273],[74.701,40.325],[74.79,40.3535],[74.862,40.324],[74.9055,40.3535],[74.796,40.435],[74.838,40.522],[74.921,40.493],[74.987,40.452],[75.018,40.4665],[75.09,40.435],[75.124,40.458],[75.2385,40.461],[75.3725,40.5485],[75.4085,40.5525],[75.462,40.603],[75.539,40.6475],[75.5955,40.6475],[75.63,40.623],[75.6235,40.5455],[75.647,40.505],[75.7235,40.472],[75.68,40.4215],[75.6635,40.355],[75.718,40.287],[75.8115,40.3245],[75.93,40.302],[75.962,40.3745],[76.1315,40.397],[76.1855,40.38],[76.2515,40.418],[76.3125,40.406],[76.3395,40.3495],[76.44,40.389],[76.5385,40.474],[76.552,40.5455],[76.6115,40.6105],[76.654,40.622],[76.64,40.762],[76.7025,40.7875],[76.7625,40.953],[76.8195,40.977],[76.878,41.0275],[76.94,41.0305],[76.9635,41.0585],[77.0855,41.063],[77.1175,41.027],[77.242,41.0265],[77.2625,41.0045],[77.3705,41.0405],[77.4675,40.9985],[77.5835,40.994],[77.6465,41.0145],[77.687,41.0035],[77.765,41.015],[77.796,41.0485],[77.8045,41.1225],[77.912,41.186],[78.0215,41.193],[78.1215,41.2275],[78.146,41.3675],[78.1895,41.394],[78.382,41.3915],[78.532,41.442],[78.5575,41.4765],[78.6445,41.469],[78.7245,41.555],[78.815,41.5585],[78.941,41.6445],[79.083,41.689],[79.124,41.7225],[79.2095,41.725],[79.323,41.811],[79.347,41.7935],[79.4095,41.841],[79.4975,41.8365],[79.6075,41.857],[79.638,41.8935],[79.765,41.893],[79.802,41.9195],[79.8545,42.022],[79.912,42.042],[80.0265,42.049],[80.138,42.0315],[80.225,42.0595],[80.1655,42.101],[80.1745,42.211],[80.0815,42.3395],[80.0305,42.343],[79.982,42.3775],[79.968,42.432],[79.787,42.4465],[79.6735,42.48],[79.641,42.4545],[79.505,42.46],[79.3465,42.6025],[79.271,42.6215],[79.182,42.6975],[79.17,42.7435],[79.1995,42.804],[79.0565,42.7755],[78.9305,42.7745],[78.8655,42.8005],[78.642,42.8425],[78.492,42.9005],[78.3505,42.855],[78.198,42.882],[78.1715,42.8605],[78.035,42.865],[77.9675,42.851],[77.7795,42.915],[77.697,42.904],[77.5805,42.9155],[77.55,42.9395],[77.4225,42.9285],[77.3535,42.899],[77.302,42.9165],[77.2335,42.912],[77.155,42.9725],[77.0845,42.981],[77.034,42.9475],[77.0045,42.983],[76.9165,42.966],[76.8515,42.9785],[76.709,42.904],[76.5225,42.922],[76.486,42.8885],[76.344,42.861],[76.309,42.901],[76.1955,42.931],[76.0285,42.911],[75.937,42.9555],[75.8235,42.9385],[75.7865,42.899],[75.7615,42.8335],[75.717,42.7975],[75.5705,42.8305],[75.419,42.8395],[75.3365,42.8555],[75.2315,42.8535],[75.037,42.9205],[74.8535,43.0],[74.7515,42.99],[74.6155,43.085],[74.5775,43.133],[74.432,43.1655],[74.3845,43.1975],[74.3085,43.2165],[74.294,43.242],[74.1925,43.2555],[74.222,43.205],[74.0765,43.187],[74.042,43.172],[73.969,43.2225],[73.9575,43.1805],[73.8995,43.119],[73.838,43.128],[73.766,43.1065],[73.612,43.041],[73.5595,43.029],[73.5575,42.9825],[73.516,42.935],[73.5215,42.8625],[73.501,42.7655],[73.4655,42.7315],[73.433,42.549],[73.4485,42.5085],[73.5155,42.435],[73.487,42.4175],[73.357,42.4365],[73.3145,42.458],[73.3095,42.518],[73.12,42.552],[72.911,42.535],[72.7975,42.5805],[72.7445,42.639],[72.588,42.684],[72.499,42.685],[72.282,42.7595],[72.166,42.7545],[72.1205,42.7365],[72.057,42.769],[71.8635,42.833],[71.6755,42.79],[71.637,42.767],[71.5705,42.775],[71.534,42.8015],[71.464,42.7855],[71.396,42.7975],[71.268,42.778],[71.274,42.743],[71.182,42.6635],[71.1795,42.6105],[71.0015,42.5855],[71.0755,42.4725],[70.9815,42.498],[70.9555,42.402],[70.898,42.3665],[70.8655,42.32],[70.891,42.263],[70.9435,42.2635],[70.9885,42.2525],[71.025,42.2925],[71.1335,42.282],[71.269,42.1965],[71.218,42.137],[71.174,42.144],[71.029,42.0675],[70.9925,42.0305],[70.865,42.054],[70.838,41.9995],[70.854,41.9415],[70.665,41.907],[70.5965,41.818],[70.5275,41.7985],[70.4965,41.7195],[70.3965,41.684],[70.367,41.6475],[70.263,41.6075],[70.213,41.611],[70.172,41.5175],[70.2405,41.4915],[70.303,41.5205],[70.4075,41.4705],[70.488,41.398],[70.666,41.47],[70.721,41.442],[70.7785,41.3885],[70.776,41.2165],[70.841,41.251],[70.9625,41.166],[71.039,41.1905],[71.127,41.1435],[71.2315,41.1925],[71.2725,41.1735],[71.2795,41.102],[71.3465,41.127],[71.3955,41.1075],[71.4515,41.157],[71.4425,41.248],[71.4705,41.297],[71.569,41.293],[71.6365,41.3425],[71.681,41.422],[71.6695,41.4975],[71.711,41.4875],[71.7595,41.4355],[71.831,41.3875],[71.925,41.294],[71.862,41.201],[71.8865,41.168],[72.074,41.1215],[72.137,41.1625],[72.2085,41.0595],[72.1715,40.995],[72.2465,41.004],[72.3305,41.037],[72.444,41.032],[72.4895,40.976],[72.5515,40.962],[72.593,40.905],[72.5915,40.868],[72.7125,40.8485],[72.832,40.8565],[72.939,40.8005],[73.014,40.845],[73.0895,40.8495],[73.1375,40.8275],[73.1395,40.7855],[72.9905,40.7645],[72.9465,40.732],[72.8015,40.6775],[72.757,40.622],[72.6925,40.597],[72.6695,40.5235],[72.613,40.5175],[72.5055,40.5605],[72.476,40.551],[72.4095,40.619],[72.3855,40.604],[72.42,40.549],[72.441,40.4735],[72.4105,40.401],[72.321,40.446],[72.278,40.434],[72.2535,40.479],[72.159,40.483],[72.087,40.451],[72.089,40.405],[71.9665,40.3195],[71.9785,40.2565],[71.8845,40.2625],[71.8285,40.241],[71.72,40.1535],[71.6895,40.1875],[71.6985,40.2445],[71.62,40.267],[71.6165,40.2065],[71.513,40.231],[71.5115,40.268],[71.428,40.284],[71.369,40.319],[71.308,40.3085],[71.274,40.3435],[71.1245,40.3355],[71.0325,40.306],[70.9815,40.3215],[70.897,40.312],[70.894,40.2315]],[[71.036,40.1805],[71.076,40.1505],[71.091,40.1045],[71.0875,40.07],[71.115,40.0205],[71.21,39.9545],[71.155,39.933],[71.0875,39.9885],[71.1145,40.014],[71.103,40.026],[71.085,40.0755],[71.0535,40.0925],[71.036,40.1805]],[[71.7145,39.966],[71.785,39.999],[71.8305,39.955],[71.7145,39.966]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/c7/Flag_of_Kyrgyzstan.svg","name:en":"Kyrgyzstan","wikidata":"Q813","ISO3166-1:alpha2":"KG","ISO3166-1:alpha3":"KGZ","ISO3166-1:numeric":"417"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[6.3675,49.4695],[6.356,49.5295],[6.3745,49.5915],[6.4215,49.622],[6.431,49.67],[6.506,49.7165],[6.5165,49.805],[6.397,49.8225],[6.364,49.8505],[6.2615,49.881],[6.1985,49.9485],[6.112,50.0595],[6.1375,50.13],[6.1195,50.1635],[6.025,50.183],[5.9645,50.1715],[5.9615,50.1315],[5.901,50.116],[5.819,50.013],[5.832,49.9765],[5.775,49.961],[5.736,49.897],[5.755,49.7915],[5.792,49.787],[5.887,49.7095],[5.9095,49.6645],[5.8185,49.5465],[5.8665,49.5],[5.94,49.501],[6.042,49.448],[6.196,49.5055],[6.257,49.51],[6.3675,49.4695]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/da/Flag_of_Luxembourg.svg","name:en":"Luxembourg","wikidata":"Q32","ISO3166-1:alpha2":"LU","ISO3166-1:alpha3":"LUX","ISO3166-1:numeric":"442"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[13.467,45.5895],[13.601,45.468],[13.6755,45.4425],[13.7655,45.467],[13.8145,45.434],[13.887,45.426],[13.9145,45.4535],[14.0705,45.4855],[14.143,45.475],[14.2455,45.5035],[14.328,45.472],[14.4375,45.511],[14.5005,45.5495],[14.507,45.5945],[14.5955,45.628],[14.698,45.568],[14.7205,45.534],[14.8035,45.4955],[14.8165,45.4605],[14.9795,45.4995],[15.0535,45.4945],[15.1665,45.422],[15.222,45.4255],[15.277,45.466],[15.329,45.4525],[15.3855,45.4865],[15.301,45.5375],[15.3065,45.6325],[15.39,45.6375],[15.3545,45.7145],[15.311,45.684],[15.2595,45.726],[15.3,45.7545],[15.5385,45.8475],[15.629,45.833],[15.683,45.8635],[15.709,45.931],[15.715,46.058],[15.6325,46.0835],[15.6075,46.167],[15.6745,46.227],[15.7855,46.2165],[15.7915,46.2585],[15.8635,46.2675],[16.0015,46.3075],[16.0775,46.348],[16.0505,46.382],[16.148,46.405],[16.189,46.378],[16.3015,46.3785],[16.2415,46.4985],[16.3655,46.5465],[16.4715,46.5165],[16.5385,46.476],[16.597,46.476],[16.5235,46.5055],[16.5085,46.5655],[16.392,46.6335],[16.4285,46.694],[16.3185,46.754],[16.313,46.7975],[16.3475,46.8405],[16.291,46.873],[16.114,46.869],[15.986,46.8275],[15.985,46.753],[16.0425,46.686],[15.95,46.688],[15.8625,46.722],[15.7215,46.696],[15.65,46.706],[15.5465,46.667],[15.5435,46.632],[15.464,46.6145],[15.414,46.6555],[15.236,46.6395],[15.1105,46.6595],[15.016,46.641],[14.9795,46.6015],[14.8865,46.613],[14.844,46.577],[14.8185,46.5095],[14.759,46.5045],[14.675,46.4505],[14.54,46.4115],[14.447,46.4215],[14.4295,46.447],[14.308,46.4305],[14.1915,46.443],[14.1215,46.4765],[14.0195,46.4815],[13.912,46.521],[13.7935,46.5055],[13.714,46.523],[13.6935,46.444],[13.582,46.4305],[13.5655,46.398],[13.437,46.3545],[13.4495,46.335],[13.3925,46.2825],[13.444,46.2255],[13.6335,46.192],[13.6445,46.1375],[13.516,46.0625],[13.502,45.9805],[13.568,45.9685],[13.643,45.9825],[13.638,45.9365],[13.5745,45.8435],[13.596,45.808],[13.6695,45.7995],[13.784,45.7485],[13.8575,45.666],[13.9185,45.6335],[13.8515,45.585],[13.7185,45.5945],[13.639,45.639],[13.467,45.5895]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/f0/Flag_of_Slovenia.svg","name:en":"Slovenia","wikidata":"Q215","ISO3166-1:alpha2":"SI","ISO3166-1:alpha3":"SVN","ISO3166-1:numeric":"705"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[23.618,51.508],[23.5655,51.534],[23.5405,51.601],[23.553,51.715],[23.531,51.7435],[23.6315,51.78],[23.612,51.9175],[23.6885,51.994],[23.64,52.0855],[23.5565,52.1115],[23.49,52.1685],[23.3955,52.201],[23.2035,52.2265],[23.2155,52.3295],[23.3565,52.469],[23.4665,52.5495],[23.6415,52.6075],[23.773,52.623],[23.9385,52.713],[23.9395,52.813],[23.9165,52.939],[23.9255,53.0245],[23.8735,53.087],[23.915,53.162],[23.819,53.244],[23.7075,53.4365],[23.549,53.768],[23.515,53.9565],[23.482,54.0],[23.5285,54.066],[23.486,54.153],[23.4245,54.1775],[23.3375,54.2515],[23.234,54.261],[23.2025,54.2885],[23.092,54.2985],[22.984,54.3895],[22.8365,54.4065],[22.792,54.3635],[22.6415,54.354],[22.153,54.3365],[21.4955,54.324],[21.2745,54.3285],[21.007,54.3535],[20.8185,54.36],[20.4385,54.388],[20.3325,54.401],[19.647,54.4535],[19.404,54.604],[19.0815,54.7605],[18.958,54.8335],[18.504,55.0085],[18.306,55.036],[18.0685,55.0345],[17.837,55.022],[17.5475,54.9725],[17.281,54.9455],[17.099,54.9125],[16.8705,54.8415],[16.7575,54.784],[16.525,54.7535],[16.391,54.726],[16.2965,54.691],[16.204,54.6395],[16.048,54.511],[15.7605,54.4345],[15.5535,54.3905],[15.203,54.3425],[14.7855,54.2445],[14.61,54.212],[14.279,54.1255],[14.2375,54.126],[14.199,54.2505],[14.1565,54.3015],[14.106,54.318],[14.1125,54.4405],[14.0785,54.441],[14.0695,54.2775],[14.168,54.239],[14.242,53.9875],[14.2125,53.8675],[14.2835,53.7725],[14.267,53.6985],[14.284,53.6345],[14.317,53.618],[14.3025,53.5535],[14.3715,53.4565],[14.3735,53.409],[14.4495,53.2595],[14.3775,53.202],[14.3875,53.1425],[14.338,53.0465],[14.2355,52.993],[14.1435,52.9615],[14.158,52.8765],[14.1415,52.824],[14.21,52.818],[14.3505,52.7515],[14.468,52.6595],[14.596,52.6105],[14.638,52.5745],[14.604,52.529],[14.635,52.4975],[14.534,52.396],[14.594,52.274],[14.6895,52.257],[14.716,52.233],[14.683,52.1125],[14.746,52.0815],[14.721,51.9525],[14.693,51.901],[14.5905,51.838],[14.644,51.7965],[14.6565,51.7405],[14.7475,51.6755],[14.766,51.6105],[14.712,51.5615],[14.737,51.5255],[14.9465,51.472],[14.9775,51.3415],[15.033,51.2945],[15.038,51.244],[14.993,51.1625],[14.9795,51.0775],[14.8965,50.9405],[14.8235,50.8705],[15.002,50.869],[14.9895,50.9215],[15.0175,50.967],[15.0165,51.022],[15.105,50.9915],[15.2295,50.9965],[15.292,50.9535],[15.277,50.891],[15.353,50.8505],[15.392,50.776],[15.439,50.809],[15.5245,50.777],[15.579,50.779],[15.7055,50.7375],[15.816,50.7555],[15.8635,50.68],[15.9885,50.685],[16.029,50.604],[16.1035,50.6635],[16.188,50.6275],[16.235,50.6715],[16.343,50.6615],[16.445,50.5795],[16.3605,50.501],[16.199,50.442],[16.2675,50.3795],[16.361,50.3795],[16.4895,50.264],[16.5485,50.23],[16.5605,50.1645],[16.641,50.112],[16.706,50.0965],[16.7835,50.1455],[16.81,50.1895],[16.898,50.222],[16.999,50.216],[17.02,50.2785],[16.9405,50.32],[16.908,50.391],[16.8605,50.411],[16.908,50.4495],[16.9835,50.42],[17.1105,50.405],[17.29,50.3175],[17.3425,50.281],[17.6105,50.266],[17.689,50.302],[17.7585,50.2065],[17.5925,50.16],[17.6765,50.103],[17.75,50.0775],[17.7775,50.02],[17.8685,49.9725],[17.954,50.005],[18.103,50.0225],[18.117,49.994],[18.2065,49.998],[18.278,49.9635],[18.431,49.938],[18.604,49.857],[18.5695,49.8345],[18.636,49.715],[18.719,49.684],[18.8045,49.679],[18.851,49.517],[18.9715,49.5045],[18.961,49.4545],[19.027,49.394],[19.1775,49.414],[19.219,49.4485],[19.2335,49.511],[19.2815,49.5355],[19.36,49.5355],[19.41,49.592],[19.469,49.5965],[19.5295,49.573],[19.5305,49.536],[19.582,49.458],[19.6345,49.4125],[19.7295,49.3915],[19.7905,49.4105],[19.805,49.323],[19.763,49.2075],[19.8445,49.195],[19.9195,49.236],[20.0085,49.22],[20.0755,49.179],[20.1025,49.253],[20.1465,49.318],[20.242,49.3505],[20.3195,49.347],[20.3295,49.391],[20.408,49.393],[20.464,49.416],[20.574,49.3765],[20.6115,49.4135],[20.723,49.4195],[20.826,49.3345],[20.8655,49.3475],[20.9255,49.296],[20.994,49.3125],[21.044,49.3655],[21.104,49.3765],[21.0565,49.4215],[21.124,49.4365],[21.192,49.401],[21.2775,49.461],[21.434,49.4125],[21.631,49.4475],[21.7635,49.3835],[21.961,49.349],[22.034,49.2785],[22.0305,49.225],[22.236,49.1545],[22.3205,49.1355],[22.3695,49.1455],[22.414,49.102],[22.5655,49.088],[22.6395,49.0595],[22.7655,49.0535],[22.8915,49.008],[22.8645,49.0665],[22.789,49.158],[22.7075,49.175],[22.7475,49.2165],[22.747,49.36],[22.6965,49.495],[22.641,49.53],[22.848,49.71],[22.9705,49.8385],[23.0335,49.8815],[23.2795,50.0865],[23.47,50.218],[23.581,50.266],[23.639,50.3205],[23.6865,50.3315],[23.703,50.3755],[23.8035,50.405],[23.9975,50.412],[24.07,50.5035],[24.0985,50.5995],[24.0715,50.7205],[24.0185,50.7255],[24.0095,50.772],[23.9575,50.795],[23.99,50.8375],[24.0945,50.836],[24.099,50.877],[23.9985,50.9275],[23.8765,51.079],[23.8215,51.1645],[23.7265,51.238],[23.695,51.287],[23.636,51.319],[23.6975,51.403],[23.6715,51.475],[23.618,51.508]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/12/Flag_of_Poland.svg","name:en":"Poland","wikidata":"Q36","ISO3166-1:alpha2":"PL","ISO3166-1:alpha3":"POL","ISO3166-1:numeric":"616"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-180.0,62.5545],[-179.955,62.617],[-179.934,62.697],[-179.944,62.763],[-180.0,62.876],[-180.0,62.5545]]],[[[-168.9245,65.986],[-168.977,66.015],[-169.1855,66.014],[-169.1605,66.1055],[-169.2075,66.195],[-169.317,66.2695],[-169.53,66.336],[-169.7395,66.3605],[-169.8305,66.413],[-169.996,66.466],[-170.208,66.502],[-170.291,66.552],[-170.5815,66.667],[-170.8275,66.7455],[-170.8495,66.811],[-170.91,66.8665],[-171.128,66.9455],[-171.1965,67.027],[-171.2955,67.0765],[-171.494,67.129],[-171.788,67.169],[-172.005,67.174],[-172.108,67.1635],[-172.4425,67.2025],[-172.656,67.2435],[-172.9245,67.2405],[-173.3565,67.2805],[-174.1665,67.5795],[-174.2275,67.6075],[-174.94,67.817],[-175.4245,67.9945],[-175.5745,68.028],[-176.4905,68.2655],[-177.2225,68.45],[-177.6285,68.594],[-178.09,68.73],[-178.4005,68.874],[-178.644,68.9485],[-178.9375,69.009],[-179.0035,69.0465],[-179.201,69.1035],[-179.3395,69.124],[-179.623,69.1265],[-180.0,69.2895],[-180.0,68.9755],[-180.0,68.97],[-180.0,68.959],[-180.0,68.93],[-180.0,68.9255],[-180.0,68.5505],[-180.0,68.513],[-180.0,68.3565],[-180.0,67.9265],[-180.0,67.552],[-180.0,67.168],[-180.0,66.9295],[-180.0,66.914],[-180.0,66.894],[-180.0,66.6405],[-180.0,65.866],[-180.0,65.8635],[-180.0,65.8035],[-180.0,65.479],[-180.0,65.454],[-180.0,65.4455],[-180.0,65.432],[-180.0,65.408],[-180.0,65.386],[-180.0,65.3825],[-180.0,65.3605],[-180.0,65.33],[-180.0,65.3245],[-180.0,65.3115],[-180.0,65.0385],[-180.0,64.7465],[-179.7515,64.8655],[-179.3925,64.976],[-179.2545,65.0435],[-179.119,65.1455],[-179.07,65.2125],[-178.728,65.2625],[-178.4635,65.2455],[-177.9815,65.2435],[-177.5195,65.2445],[-177.2845,65.2645],[-177.1555,65.2855],[-176.957,65.3385],[-176.746,65.3525],[-176.555,65.3115],[-176.394,65.255],[-176.3365,65.189],[-176.358,64.9725],[-176.286,64.8915],[-176.156,64.8215],[-175.6825,64.607],[-175.5205,64.578],[-175.134,64.563],[-174.831,64.5225],[-174.556,64.4485],[-174.46,64.39],[-174.3595,64.2955],[-174.259,64.2535],[-173.816,64.1395],[-173.5275,64.103],[-173.341,64.0635],[-173.202,64.0465],[-173.0325,64.0445],[-172.706,64.0885],[-172.382,64.1915],[-172.135,64.2075],[-172.009,64.228],[-171.845,64.294],[-171.789,64.3455],[-171.777,64.4425],[-171.8325,64.559],[-171.698,64.632],[-171.6225,64.6905],[-171.5965,64.7455],[-171.627,64.83],[-171.641,65.0115],[-171.6155,65.119],[-171.6895,65.232],[-171.5575,65.3065],[-171.495,65.3125],[-171.119,65.2675],[-170.941,65.2705],[-170.8165,65.2905],[-170.648,65.35],[-170.2235,65.471],[-170.125,65.5245],[-170.038,65.601],[-170.01,65.7375],[-169.8775,65.787],[-169.384,65.5875],[-169.016,65.481],[-168.9245,65.986]]],[[[-180.0,70.7165],[-179.726,70.669],[-179.532,70.6615],[-179.036,70.6945],[-178.555,70.763],[-178.3425,70.7715],[-177.6325,70.845],[-177.3355,70.908],[-177.1365,70.9645],[-176.9055,71.08],[-176.8245,71.1885],[-176.8175,71.238],[-176.893,71.327],[-177.0005,71.3825],[-177.2605,71.4525],[-177.4565,71.5595],[-177.7225,71.648],[-178.0285,71.714],[-178.5845,71.775],[-179.058,71.803],[-179.612,71.791],[-180.0,71.749],[-180.0,71.534],[-180.0,71.0255],[-180.0,70.7165]]],[[[-176.3625,71.4065],[-176.3,71.3265],[-176.0885,71.2305],[-175.807,71.168],[-175.577,71.157],[-175.3095,71.181],[-175.0685,71.248],[-174.9765,71.3055],[-174.9395,71.3935],[-174.996,71.4715],[-175.141,71.539],[-175.5645,71.6155],[-175.799,71.6265],[-176.028,71.606],[-176.2155,71.557],[-176.333,71.487],[-176.3625,71.4065]]],[[[22.792,54.3635],[22.7365,54.443],[22.7035,54.453],[22.6805,54.533],[22.6865,54.591],[22.743,54.627],[22.7415,54.7285],[22.8735,54.789],[22.855,54.888],[22.726,54.9635],[22.6035,55.0135],[22.589,55.07],[22.466,55.0445],[22.287,55.0645],[22.1575,55.0555],[22.101,55.024],[21.9665,55.074],[21.758,55.124],[21.649,55.181],[21.5725,55.198],[21.5075,55.1855],[21.4495,55.2215],[21.3845,55.2935],[21.271,55.245],[21.0985,55.2565],[20.9545,55.281],[20.6555,55.384],[20.401,55.1805],[20.202,55.16],[19.951,55.16],[19.824,55.14],[19.7045,55.0845],[19.606,54.9795],[19.584,54.909],[19.6175,54.827],[19.5775,54.737],[19.404,54.604],[19.647,54.4535],[20.3325,54.401],[20.4385,54.388],[20.8185,54.36],[21.007,54.3535],[21.2745,54.3285],[21.4955,54.324],[22.153,54.3365],[22.6415,54.354],[22.792,54.3635]]],[[[37.4375,46.716],[37.457,46.6235],[37.512,46.5665],[37.572,46.443],[37.696,46.3035],[37.8835,46.2055],[37.776,46.1545],[37.738,46.115],[37.6675,46.0005],[37.632,45.886],[37.435,45.8065],[37.3595,45.727],[37.3405,45.5395],[37.292,45.515],[37.153,45.5655],[37.042,45.579],[36.954,45.6205],[36.8875,45.633],[36.6685,45.6265],[36.3385,45.6715],[36.1355,45.6475],[36.0725,45.6255],[35.982,45.6165],[35.871,45.6455],[35.7355,45.633],[35.6275,45.5745],[35.5765,45.4905],[35.4595,45.5605],[35.429,45.6175],[35.3375,45.683],[35.3165,45.7235],[35.2335,45.7915],[34.975,45.762],[34.799,45.8105],[34.802,45.9005],[34.7545,45.9085],[34.67,45.9675],[34.561,45.9945],[34.4985,45.9425],[34.443,45.962],[34.405,46.0095],[34.3295,46.0605],[34.248,46.053],[34.1885,46.066],[34.1175,46.1075],[34.0525,46.109],[33.8115,46.204],[33.736,46.186],[33.615,46.226],[33.6355,46.1465],[33.572,46.1025],[33.591,46.061],[33.5405,46.012],[32.7725,45.8265],[32.589,45.7125],[32.478,45.6605],[32.3505,45.6165],[32.2975,45.575],[32.231,45.4915],[32.2025,45.383],[32.229,45.289],[32.314,45.1955],[32.38,45.1645],[32.5535,45.122],[32.6315,45.112],[32.7485,45.1255],[32.8315,45.151],[32.89,45.089],[33.028,45.0075],[33.193,44.9505],[33.2945,44.94],[33.244,44.8215],[33.2465,44.7635],[33.1405,44.696],[33.0915,44.5895],[33.127,44.48],[33.2955,44.335],[33.4205,44.2945],[33.478,44.258],[33.618,44.2095],[33.716,44.188],[33.8565,44.1945],[33.9515,44.1845],[34.0925,44.2135],[34.2375,44.232],[34.3045,44.263],[34.555,44.4055],[34.666,44.547],[34.736,44.585],[34.8085,44.604],[34.929,44.611],[35.06,44.584],[35.1585,44.5925],[35.3015,44.6495],[35.3975,44.7375],[35.535,44.7755],[35.632,44.849],[35.6875,44.82],[35.7805,44.7955],[35.8595,44.793],[36.033,44.811],[36.103,44.8335],[36.216,44.8195],[36.509,44.8755],[36.6095,44.9405],[36.705,44.922],[36.791,44.923],[36.9545,44.8815],[37.035,44.845],[37.1145,44.748],[37.1485,44.6635],[37.1905,44.624],[37.319,44.5525],[37.542,44.4835],[37.7535,44.471],[37.7985,44.4335],[37.9095,44.391],[38.0555,44.269],[38.1945,44.229],[38.263,44.1985],[38.4605,44.1685],[38.6025,44.1295],[38.705,44.0425],[38.7975,44.002],[38.8525,43.9525],[38.9205,43.917],[39.204,43.7205],[39.25,43.665],[39.334,43.608],[39.388,43.547],[39.471,43.4945],[39.542,43.42],[39.6415,43.369],[39.681,43.312],[39.8,43.2395],[39.8845,43.2245],[40.0085,43.3855],[40.0905,43.5555],[40.2615,43.5855],[40.2995,43.57],[40.478,43.549],[40.5665,43.519],[40.662,43.563],[40.8935,43.457],[40.95,43.4195],[41.0045,43.432],[41.0395,43.3995],[41.221,43.3805],[41.288,43.337],[41.363,43.3685],[41.413,43.3245],[41.5435,43.2705],[41.6215,43.222],[41.6845,43.217],[41.7455,43.237],[41.804,43.202],[41.867,43.244],[42.038,43.188],[42.1995,43.228],[42.253,43.2125],[42.3365,43.2195],[42.358,43.247],[42.442,43.251],[42.4475,43.22],[42.5435,43.1765],[42.617,43.1635],[42.6665,43.136],[42.698,43.172],[42.7765,43.189],[42.853,43.179],[42.99,43.119],[43.051,43.019],[43.173,42.963],[43.185,42.9385],[43.561,42.8675],[43.609,42.8135],[43.6775,42.802],[43.779,42.758],[43.804,42.715],[43.759,42.655],[43.715,42.6365],[43.7705,42.589],[43.8365,42.5985],[43.9625,42.5475],[44.0105,42.5915],[44.1155,42.6175],[44.162,42.6065],[44.225,42.6305],[44.2495,42.681],[44.3195,42.723],[44.394,42.7065],[44.5,42.734],[44.68,42.756],[44.753,42.714],[44.7665,42.643],[44.804,42.6145],[44.8875,42.747],[44.98,42.745],[45.053,42.6925],[45.086,42.7115],[45.1585,42.7075],[45.252,42.62],[45.2835,42.606],[45.3545,42.522],[45.4185,42.5515],[45.4935,42.535],[45.5715,42.548],[45.614,42.5055],[45.6875,42.5025],[45.7215,42.4755],[45.7755,42.4885],[45.7745,42.4285],[45.7345,42.3785],[45.7535,42.3095],[45.652,42.292],[45.609,42.234],[45.6475,42.1865],[45.748,42.146],[45.786,42.102],[45.8555,42.1135],[45.9215,42.081],[45.9295,42.032],[45.9875,42.0455],[46.0385,42.02],[46.1615,41.9905],[46.237,42.016],[46.246,41.979],[46.3205,41.9295],[46.3995,41.94],[46.422,41.908],[46.4635,41.8655],[46.5085,41.8895],[46.5515,41.867],[46.5565,41.8205],[46.6215,41.824],[46.757,41.8625],[46.7655,41.801],[46.8665,41.7205],[47.0125,41.6275],[46.9915,41.595],[47.0515,41.5545],[47.11,41.575],[47.1715,41.527],[47.222,41.419],[47.252,41.3985],[47.3005,41.308],[47.377,41.2725],[47.4985,41.2685],[47.5065,41.226],[47.564,41.198],[47.6185,41.234],[47.701,41.226],[47.7265,41.1935],[47.7835,41.185],[47.8205,41.222],[47.8845,41.2185],[47.9115,41.255],[47.8905,41.2875],[47.9915,41.3555],[48.003,41.4165],[48.065,41.4925],[48.203,41.5125],[48.2845,41.561],[48.3775,41.591],[48.491,41.7275],[48.5555,41.7825],[48.5925,41.843],[48.814,41.9505],[48.745,41.9915],[48.685,42.0515],[48.5575,42.141],[48.531,42.196],[48.439,42.2915],[48.322,42.4435],[48.2215,42.522],[48.1495,42.6395],[47.984,42.832],[47.9655,42.9145],[47.904,42.9995],[47.813,43.0605],[47.7775,43.1375],[47.8215,43.2155],[47.8315,43.3195],[47.7935,43.481],[47.822,43.529],[47.845,43.636],[47.933,43.7535],[48.0925,43.868],[48.1355,43.9365],[48.1435,43.993],[48.091,44.1035],[48.0175,44.1555],[47.9145,44.186],[47.6985,44.1995],[47.6635,44.2105],[47.7925,44.3105],[47.84,44.3945],[47.839,44.471],[47.7815,44.594],[47.6905,44.658],[47.6195,44.683],[47.504,44.695],[47.4055,44.681],[47.3105,44.6405],[47.165,44.6055],[47.1535,44.6435],[47.259,44.692],[47.3345,44.759],[47.4175,44.885],[47.5,44.9915],[47.558,45.1295],[47.6155,45.2065],[47.827,45.2625],[47.9665,45.274],[48.0335,45.292],[48.1145,45.337],[48.243,45.4505],[48.334,45.4615],[48.4935,45.422],[48.677,45.4025],[48.8485,45.4345],[48.9585,45.4985],[49.0155,45.577],[49.0325,45.641],[49.071,45.689],[49.1895,45.7575],[49.417,45.846],[49.5175,45.919],[49.614,45.975],[49.886,46.046],[49.7785,46.1185],[49.7015,46.1865],[49.6,46.1765],[49.5065,46.1935],[49.2625,46.294],[49.162,46.384],[49.045,46.3965],[48.986,46.433],[48.912,46.4505],[48.8785,46.4815],[48.8195,46.4855],[48.788,46.541],[48.7025,46.563],[48.545,46.5635],[48.5735,46.6065],[48.5605,46.642],[48.478,46.6705],[48.5485,46.711],[48.7135,46.721],[48.7505,46.6855],[48.92,46.6935],[48.996,46.743],[48.9155,46.8805],[48.71,47.096],[48.5195,47.412],[48.4515,47.4115],[48.319,47.5515],[48.2095,47.6915],[48.1115,47.745],[48.028,47.763],[47.9625,47.7515],[47.6525,47.758],[47.4155,47.8385],[47.412,47.7635],[47.385,47.6815],[47.1945,47.761],[47.122,47.8315],[47.181,47.854],[47.101,47.928],[47.05,47.998],[47.2005,48.055],[47.211,48.082],[47.091,48.103],[47.125,48.158],[47.1035,48.212],[47.123,48.2685],[46.8895,48.3275],[46.7475,48.3535],[46.493,48.4325],[46.779,48.9515],[46.8925,48.982],[46.972,49.0275],[47.0285,49.089],[47.0515,49.1655],[46.996,49.2325],[46.9145,49.283],[46.782,49.34],[46.8295,49.5655],[46.858,49.6435],[46.87,49.743],[46.9025,49.864],[46.9575,49.8715],[47.1825,49.935],[47.3535,50.0945],[47.3175,50.152],[47.2575,50.1915],[47.3355,50.238],[47.3005,50.303],[47.4065,50.3365],[47.4725,50.42],[47.5415,50.4605],[47.6125,50.464],[47.6635,50.4085],[47.7315,50.386],[47.7625,50.341],[47.8285,50.3275],[47.8855,50.2685],[47.939,50.2425],[48.009,50.142],[48.095,50.1],[48.1315,49.998],[48.269,49.8595],[48.4505,49.835],[48.7455,49.9215],[48.9035,50.0185],[48.848,50.0915],[48.7635,50.098],[48.809,50.1705],[48.7325,50.257],[48.659,50.528],[48.652,50.601],[48.5705,50.6325],[48.606,50.662],[48.689,50.6125],[48.8145,50.5955],[49.0095,50.682],[49.126,50.7845],[49.2625,50.823],[49.42,50.849],[49.4435,50.8775],[49.408,50.961],[49.3385,50.987],[49.3965,51.049],[49.393,51.09],[49.5545,51.11],[49.776,51.107],[49.911,51.201],[50.04,51.2515],[50.243,51.28],[50.373,51.3325],[50.338,51.382],[50.497,51.4275],[50.547,51.465],[50.5375,51.589],[50.5865,51.5905],[50.636,51.629],[50.7315,51.6285],[50.7675,51.575],[50.812,51.585],[50.8125,51.649],[50.768,51.7145],[50.768,51.773],[50.8715,51.7535],[50.8935,51.6815],[51.0845,51.6665],[51.204,51.6675],[51.272,51.685],[51.3035,51.641],[51.38,51.641],[51.3805,51.5635],[51.2855,51.532],[51.2865,51.488],[51.4315,51.475],[51.566,51.506],[51.64,51.5385],[51.656,51.4485],[51.8135,51.497],[51.7705,51.583],[51.894,51.6835],[51.9995,51.6835],[52.09,51.6585],[52.2385,51.724],[52.3355,51.7435],[52.416,51.64],[52.4505,51.6235],[52.5655,51.4635],[52.6475,51.4885],[52.747,51.4865],[52.7905,51.5135],[52.9045,51.4775],[53.0565,51.4895],[53.144,51.485],[53.255,51.512],[53.3925,51.491],[53.4815,51.444],[53.575,51.4245],[53.5955,51.306],[53.66,51.26],[53.6615,51.231],[53.7475,51.204],[53.9325,51.1935],[54.042,51.136],[54.137,51.108],[54.1995,50.964],[54.2885,50.949],[54.288,50.9025],[54.4415,50.8795],[54.518,50.8545],[54.5185,50.812],[54.4735,50.7785],[54.4135,50.6205],[54.466,50.5665],[54.545,50.5275],[54.614,50.5385],[54.73,50.6175],[54.718,50.69],[54.6785,50.718],[54.6975,50.8925],[54.658,50.915],[54.566,50.9155],[54.5685,51.0185],[54.7185,51.0285],[54.7915,50.9855],[54.898,50.958],[54.894,50.92],[55.01,50.9055],[55.071,50.924],[55.1295,50.8795],[55.056,50.8195],[55.1465,50.797],[55.3835,50.6535],[55.403,50.6815],[55.5025,50.67],[55.534,50.6135],[55.6115,50.5985],[55.6615,50.56],[55.7465,50.5795],[55.83,50.623],[55.916,50.6415],[56.0545,50.695],[56.137,50.7535],[56.133,50.8305],[56.1815,50.938],[56.298,50.902],[56.3615,50.9015],[56.3925,50.9455],[56.4605,50.965],[56.448,51.057],[56.51,51.0805],[56.634,50.985],[56.751,50.9815],[56.6995,51.075],[56.78,51.094],[56.8875,51.0605],[56.943,51.0875],[56.9845,51.0625],[57.1465,51.0875],[57.185,51.115],[57.246,51.026],[57.2825,51.0325],[57.3245,50.943],[57.415,50.8965],[57.507,50.876],[57.559,50.936],[57.736,50.9275],[57.7515,50.9395],[57.754,51.056],[57.772,51.1385],[57.867,51.0995],[57.9515,51.0865],[58.0735,51.123],[58.1025,51.07],[58.177,51.066],[58.206,51.1295],[58.3405,51.1455],[58.384,51.075],[58.5205,51.085],[58.5765,51.056],[58.6425,50.9875],[58.5785,50.94],[58.5935,50.8695],[58.688,50.8855],[58.6965,50.848],[58.6565,50.8085],[58.7745,50.8145],[58.8745,50.7],[59.114,50.683],[59.418,50.6365],[59.4795,50.6415],[59.569,50.5735],[59.463,50.5845],[59.4575,50.5435],[59.5165,50.5285],[59.776,50.541],[59.813,50.5355],[59.91,50.6365],[59.9975,50.675],[59.9725,50.7685],[60.015,50.822],[60.188,50.8385],[60.183,50.7785],[60.325,50.6705],[60.5575,50.661],[60.741,50.6655],[60.809,50.6565],[61.449,50.806],[61.486,50.9585],[61.568,51.238],[61.69,51.256],[61.5495,51.3235],[61.505,51.407],[61.391,51.42],[61.3535,51.439],[61.2565,51.441],[61.203,51.461],[60.9995,51.4675],[60.911,51.5505],[60.9355,51.6155],[60.538,51.618],[60.4255,51.647],[60.3545,51.6905],[60.4315,51.7195],[60.5175,51.7975],[60.428,51.7985],[60.162,51.892],[60.144,51.8705],[60.0325,51.9135],[60.002,51.9935],[60.1975,51.991],[60.3395,52.055],[60.511,52.1565],[60.7165,52.156],[60.772,52.208],[60.9455,52.2675],[61.0105,52.326],[61.067,52.347],[60.992,52.423],[60.9825,52.508],[60.842,52.5225],[60.843,52.641],[60.7615,52.6265],[60.7145,52.663],[60.712,52.7645],[60.8345,52.774],[60.987,52.8865],[61.076,52.928],[61.0495,52.981],[61.1215,52.9875],[61.251,53.036],[61.307,52.996],[61.3905,52.9925],[61.469,53.0365],[61.557,53.012],[61.632,52.954],[61.702,52.998],[61.834,52.993],[61.961,52.95],[62.0295,52.951],[62.1305,52.99],[62.1475,53.068],[62.1195,53.112],[62.0275,53.142],[61.916,53.142],[61.784,53.1845],[61.7325,53.239],[61.674,53.2655],[61.6305,53.223],[61.527,53.2155],[61.4,53.274],[61.226,53.2835],[61.177,53.312],[61.1555,53.4055],[61.2165,53.432],[61.229,53.484],[61.2995,53.5125],[61.4285,53.4555],[61.488,53.476],[61.565,53.5385],[61.553,53.593],[61.391,53.602],[61.358,53.5615],[61.2155,53.5635],[61.1265,53.59],[61.122,53.615],[60.9005,53.6285],[60.896,53.668],[60.9955,53.673],[61.0585,53.7215],[61.1145,53.721],[61.227,53.7955],[61.237,53.8415],[61.179,53.8525],[61.137,53.9045],[60.9935,53.907],[61.0295,53.955],[61.1,53.975],[61.1805,53.972],[61.205,53.919],[61.28,53.9285],[61.25,54.0235],[61.294,54.074],[61.48,54.083],[61.4805,54.03],[61.569,54.008],[61.7005,54.019],[61.782,53.9845],[61.8185,54.019],[61.893,54.009],[61.857,53.9625],[62.044,53.9485],[62.006,54.0375],[62.0835,54.049],[62.1755,54.0285],[62.339,54.0325],[62.3965,54.004],[62.4465,53.927],[62.5545,53.9335],[62.5975,53.995],[62.569,54.0465],[62.5965,54.072],[62.806,54.112],[62.936,54.1165],[63.0795,54.104],[63.1515,54.123],[63.151,54.1825],[63.337,54.201],[63.357,54.1885],[63.5185,54.207],[63.728,54.261],[63.8035,54.271],[63.8975,54.2165],[63.9615,54.2005],[64.0215,54.2305],[63.984,54.2715],[64.0985,54.311],[64.1845,54.309],[64.359,54.3365],[64.4195,54.3265],[64.4815,54.3665],[64.6,54.375],[64.7385,54.354],[64.975,54.424],[65.121,54.3305],[65.239,54.376],[65.2015,54.466],[65.2125,54.532],[65.306,54.5685],[65.495,54.5965],[65.4565,54.6245],[65.633,54.641],[65.7475,54.6075],[65.831,54.653],[65.8455,54.697],[65.982,54.717],[65.964,54.638],[65.9985,54.625],[66.3815,54.7035],[66.654,54.744],[66.738,54.7435],[66.782,54.7685],[66.994,54.7895],[67.282,54.8255],[67.3325,54.8735],[67.3985,54.8555],[67.5165,54.862],[67.563,54.8445],[67.7735,54.886],[67.8195,54.9715],[67.9105,54.9835],[68.0325,54.9505],[68.2345,54.969],[68.2375,55.054],[68.2715,55.085],[68.1895,55.1515],[68.282,55.205],[68.4445,55.1965],[68.6355,55.204],[68.635,55.2655],[68.697,55.289],[68.7085,55.348],[68.7485,55.379],[68.929,55.3185],[68.916,55.3655],[68.9325,55.442],[69.048,55.427],[69.1605,55.3905],[69.1995,55.33],[69.4165,55.3815],[69.442,55.342],[69.707,55.3515],[69.863,55.3],[69.9505,55.21],[70.057,55.209],[70.1135,55.172],[70.2275,55.148],[70.292,55.1925],[70.402,55.2155],[70.41,55.2535],[70.478,55.2725],[70.6205,55.2535],[70.695,55.2895],[70.789,55.2895],[70.9215,55.1305],[70.993,55.0735],[71.0085,54.9705],[70.9565,54.8885],[71.0195,54.852],[71.0305,54.7785],[71.0965,54.713],[71.2105,54.7],[71.293,54.668],[71.2915,54.606],[71.2145,54.61],[71.1675,54.5605],[71.1705,54.461],[71.2295,54.354],[71.1985,54.3115],[71.1125,54.335],[70.9985,54.336],[70.9965,54.2755],[71.073,54.275],[71.0715,54.2055],[71.138,54.162],[71.1225,54.1295],[71.179,54.095],[71.2745,54.147],[71.296,54.185],[71.491,54.1585],[71.488,54.101],[71.61,54.117],[71.7205,54.1065],[71.764,54.1475],[71.7265,54.1875],[71.751,54.254],[71.886,54.2315],[71.949,54.2435],[72.0735,54.1995],[72.101,54.1475],[72.1845,54.1205],[72.2185,54.182],[72.0855,54.219],[72.136,54.305],[72.0515,54.338],[72.0325,54.3665],[72.189,54.355],[72.2785,54.317],[72.2995,54.187],[72.4175,54.151],[72.44,54.12],[72.3965,54.0515],[72.4175,53.9985],[72.3695,53.9445],[72.4865,53.901],[72.543,53.976],[72.7145,53.951],[72.7195,53.9995],[72.68,54.045],[72.571,54.0515],[72.56,54.1165],[72.6085,54.137],[72.8255,54.1215],[72.8315,54.098],[72.994,54.0985],[72.9785,54.0495],[73.0595,54.0405],[73.067,53.9855],[73.1935,53.9705],[73.276,53.9425],[73.5045,53.939],[73.519,54.004],[73.609,54.0085],[73.657,54.0465],[73.7675,54.054],[73.691,53.856],[73.4525,53.876],[73.449,53.8075],[73.3555,53.766],[73.323,53.6755],[73.277,53.6395],[73.2385,53.5675],[73.3975,53.522],[73.3625,53.4675],[73.4385,53.4355],[73.645,53.5525],[73.6665,53.613],[73.7475,53.607],[73.821,53.5785],[73.901,53.651],[74.072,53.6295],[74.0515,53.566],[74.1465,53.555],[74.1615,53.6015],[74.2875,53.5605],[74.2915,53.4915],[74.3935,53.4595],[74.4865,53.5725],[74.421,53.594],[74.457,53.691],[74.635,53.668],[74.6675,53.756],[74.757,53.7445],[74.7885,53.828],[74.982,53.8225],[75.0465,53.7925],[75.377,53.9595],[75.441,53.9725],[75.4345,54.0095],[75.375,54.07],[75.529,54.115],[75.598,54.099],[76.203,54.26],[76.1865,54.3015],[76.2575,54.3595],[76.352,54.334],[76.476,54.318],[76.675,54.345],[76.73,54.418],[76.7955,54.419],[76.929,54.458],[76.9355,54.4155],[76.86,54.361],[76.8785,54.302],[76.8285,54.274],[76.828,54.225],[76.7565,54.162],[76.645,54.1245],[76.5825,54.149],[76.4405,54.1675],[76.435,54.1225],[76.4985,54.0715],[76.538,54.0],[76.829,53.8495],[77.339,53.595],[77.9065,53.2915],[78.0255,53.161],[78.267,52.924],[78.403,52.7815],[78.7175,52.429],[78.975,52.1505],[79.1875,51.9235],[79.4115,51.624],[79.7205,51.217],[80.076,50.738],[80.083,50.828],[80.185,50.8275],[80.193,50.9005],[80.2455,50.9185],[80.376,50.927],[80.421,50.962],[80.482,50.9685],[80.482,51.1095],[80.4365,51.119],[80.447,51.2085],[80.6345,51.207],[80.635,51.263],[80.6775,51.3165],[80.9405,51.2595],[80.92,51.219],[81.177,51.1615],[81.142,51.079],[81.103,51.0605],[81.06,50.948],[81.191,50.951],[81.289,50.974],[81.4125,50.9755],[81.4355,50.863],[81.486,50.824],[81.4495,50.756],[81.5825,50.7425],[81.797,50.772],[81.8855,50.8045],[81.997,50.788],[82.0785,50.751],[82.2225,50.737],[82.2705,50.77],[82.351,50.7755],[82.5675,50.747],[82.7015,50.812],[82.739,50.8505],[82.733,50.9275],[82.911,50.9075],[82.9795,50.884],[83.006,50.925],[83.1125,50.966],[83.121,51.011],[83.202,50.9935],[83.407,51.013],[83.4475,50.977],[83.5935,50.9495],[83.6485,50.955],[83.7115,50.8935],[83.8335,50.88],[83.9105,50.8165],[83.9645,50.801],[83.951,50.7465],[84.023,50.698],[84.0825,50.6335],[84.1455,50.603],[84.176,50.5515],[84.2485,50.5155],[84.204,50.469],[84.2685,50.3565],[84.2515,50.29],[84.326,50.2285],[84.41,50.209],[84.447,50.2465],[84.5305,50.206],[84.628,50.2105],[84.692,50.1885],[84.7575,50.137],[84.8565,50.092],[84.946,50.0965],[85.024,50.06],[85.038,50.009],[84.9835,49.984],[84.995,49.9055],[85.0595,49.885],[85.1505,49.7675],[85.1985,49.738],[85.2105,49.6275],[85.2575,49.5885],[85.3215,49.591],[85.3885,49.635],[85.49,49.598],[85.5835,49.6135],[85.6785,49.551],[85.757,49.577],[85.841,49.5475],[85.8835,49.567],[85.969,49.487],[86.037,49.522],[86.113,49.5245],[86.202,49.466],[86.267,49.5465],[86.2715,49.5825],[86.3715,49.5935],[86.4885,49.658],[86.5265,49.702],[86.5825,49.724],[86.6105,49.772],[86.5905,49.801],[86.6975,49.8085],[86.7715,49.764],[86.779,49.696],[86.7095,49.6875],[86.6025,49.6055],[86.618,49.5705],[86.7395,49.571],[86.827,49.5455],[86.8555,49.499],[86.831,49.47],[86.8685,49.435],[86.93,49.42],[86.9265,49.349],[87.006,49.299],[87.0255,49.2575],[87.1985,49.2525],[87.2695,49.2325],[87.3025,49.1695],[87.286,49.116],[87.4265,49.0695],[87.564,49.135],[87.695,49.1755],[87.8145,49.17],[87.9885,49.183],[88.018,49.2265],[88.169,49.2925],[88.1605,49.363],[88.1195,49.3915],[88.185,49.4185],[88.1655,49.4455],[88.222,49.4795],[88.2965,49.471],[88.331,49.4935],[88.411,49.4935],[88.495,49.472],[88.591,49.501],[88.733,49.4515],[88.88,49.478],[88.9325,49.517],[88.9845,49.4635],[89.201,49.523],[89.2325,49.5535],[89.1915,49.586],[89.189,49.6325],[89.241,49.645],[89.36,49.5845],[89.459,49.665],[89.661,49.7],[89.719,49.7285],[89.719,49.772],[89.629,49.8085],[89.6115,49.8635],[89.6295,49.919],[89.7335,49.948],[89.883,49.953],[90.0175,49.9905],[90.0025,50.054],[90.165,50.108],[90.285,50.111],[90.389,50.1925],[90.453,50.197],[90.504,50.235],[90.5805,50.244],[90.6735,50.2145],[90.731,50.2435],[90.7065,50.313],[90.8195,50.3545],[90.911,50.353],[90.903,50.4085],[90.9695,50.4295],[91.093,50.436],[91.16,50.42],[91.2935,50.4695],[91.4005,50.4765],[91.459,50.5085],[91.455,50.5485],[91.5345,50.5815],[91.65,50.586],[91.661,50.64],[91.7235,50.6975],[91.855,50.734],[92.0725,50.6965],[92.172,50.7065],[92.316,50.754],[92.313,50.8765],[92.373,50.88],[92.432,50.803],[92.477,50.7865],[92.571,50.798],[92.6185,50.768],[92.628,50.7035],[92.754,50.7275],[92.7875,50.8055],[92.854,50.81],[93.032,50.768],[93.023,50.716],[92.975,50.6695],[93.05,50.601],[93.249,50.594],[93.3975,50.6345],[93.5445,50.602],[93.636,50.607],[93.7035,50.586],[93.856,50.601],[93.941,50.59],[94.031,50.5965],[94.2405,50.588],[94.2835,50.572],[94.344,50.4915],[94.389,50.215],[94.4215,50.1915],[94.522,50.1725],[94.5435,50.1175],[94.615,50.0355],[94.7605,50.055],[94.999,50.0445],[95.0165,49.9805],[95.0815,49.963],[95.426,49.9575],[95.531,49.8975],[95.5725,49.9445],[95.666,49.9645],[95.7955,49.964],[95.7655,50.017],[95.844,50.0435],[95.9155,50.0215],[95.9675,49.971],[96.0175,50.0035],[96.2605,49.9765],[96.3485,49.9055],[96.417,49.877],[96.5145,49.943],[96.549,49.9435],[96.6005,49.8775],[96.71,49.9285],[96.741,49.9075],[96.999,49.8885],[97.01,49.8335],[97.117,49.82],[97.123,49.795],[97.231,49.743],[97.318,49.752],[97.3575,49.7815],[97.4215,49.7805],[97.5105,49.8145],[97.5805,49.8585],[97.555,49.9225],[97.7805,49.9935],[97.8295,49.923],[97.898,49.984],[97.982,50.019],[98.07,50.036],[98.0635,50.07],[98.1205,50.1],[98.11,50.1315],[98.1825,50.158],[98.1695,50.202],[98.2285,50.236],[98.253,50.2945],[98.308,50.3235],[98.2725,50.41],[98.3275,50.474],[98.274,50.538],[98.1465,50.575],[98.008,50.669],[97.9785,50.784],[98.0085,50.8365],[97.998,50.876],[97.858,50.9595],[97.828,51.019],[97.866,51.046],[97.899,51.1495],[97.9395,51.202],[97.962,51.276],[97.9535,51.334],[98.017,51.3895],[98.05,51.45],[98.126,51.4715],[98.224,51.4565],[98.2565,51.5115],[98.234,51.5475],[98.327,51.692],[98.3785,51.74],[98.4525,51.7365],[98.5215,51.7785],[98.6125,51.8065],[98.7645,51.87],[98.78,51.942],[98.809,51.958],[98.8065,52.0255],[98.8415,52.059],[98.8615,52.136],[98.969,52.1425],[98.9975,52.0865],[99.0715,52.0695],[99.1325,52.024],[99.1735,52.0355],[99.276,52.0185],[99.3055,51.9575],[99.4985,51.9515],[99.52,51.9685],[99.6255,51.8925],[99.801,51.9155],[99.8195,51.848],[99.9095,51.7455],[99.978,51.7535],[100.033,51.7335],[100.1035,51.7545],[100.201,51.743],[100.3535,51.7415],[100.4105,51.73],[100.529,51.75],[100.688,51.7115],[100.739,51.678],[100.93,51.607],[100.99,51.613],[101.1435,51.52],[101.243,51.5475],[101.37,51.456],[101.439,51.4555],[101.5,51.5035],[101.6225,51.469],[101.6205,51.4435],[101.749,51.4555],[101.8935,51.401],[101.928,51.4165],[102.1245,51.3615],[102.1685,51.3185],[102.1195,51.2795],[102.138,51.1765],[102.1235,51.139],[102.151,51.0575],[102.2035,51.0395],[102.241,50.9175],[102.211,50.873],[102.2095,50.817],[102.295,50.7795],[102.3405,50.7325],[102.3025,50.682],[102.3955,50.6445],[102.446,50.6455],[102.479,50.5975],[102.5255,50.5945],[102.547,50.527],[102.6105,50.5065],[102.6475,50.402],[102.727,50.408],[102.7585,50.379],[102.942,50.3055],[103.1765,50.3325],[103.238,50.322],[103.2765,50.285],[103.2565,50.193],[103.294,50.186],[103.425,50.2115],[103.514,50.199],[103.6085,50.144],[103.8005,50.1515],[103.8465,50.1975],[103.9455,50.179],[103.9985,50.1505],[104.108,50.1405],[104.2785,50.201],[104.3035,50.2445],[104.3655,50.265],[104.3965,50.3125],[104.557,50.307],[104.65,50.324],[104.699,50.367],[104.8115,50.341],[104.8765,50.375],[104.89,50.408],[105.008,50.387],[105.12,50.39],[105.198,50.4115],[105.2985,50.472],[105.3475,50.476],[105.4265,50.444],[105.525,50.458],[105.609,50.429],[105.705,50.4265],[105.844,50.4405],[105.899,50.4125],[105.989,50.416],[106.0745,50.3895],[106.084,50.3335],[106.183,50.3395],[106.261,50.298],[106.3955,50.32],[106.4475,50.3145],[106.51,50.3415],[106.5915,50.3385],[106.8155,50.298],[106.9795,50.209],[107.0,50.161],[107.072,50.0685],[107.211,50.0],[107.349,49.9995],[107.383,49.9775],[107.5985,49.97],[107.729,49.9825],[107.794,49.933],[107.969,49.9415],[107.919,49.8755],[107.9675,49.735],[107.9345,49.7115],[107.9685,49.655],[108.0125,49.667],[108.0515,49.6175],[108.161,49.562],[108.304,49.532],[108.3565,49.428],[108.426,49.4135],[108.431,49.372],[108.5175,49.3225],[108.608,49.3275],[108.6525,49.3475],[108.718,49.335],[108.9555,49.345],[109.058,49.323],[109.171,49.353],[109.326,49.329],[109.4125,49.3],[109.487,49.243],[109.559,49.221],[109.684,49.226],[109.8535,49.2015],[109.9025,49.2055],[110.226,49.158],[110.328,49.199],[110.3795,49.247],[110.476,49.2015],[110.6835,49.171],[110.8195,49.182],[111.0785,49.262],[111.277,49.3105],[111.375,49.3575],[111.515,49.323],[111.6685,49.38],[111.788,49.3845],[111.909,49.3765],[112.0075,49.3945],[112.1945,49.4465],[112.2785,49.4845],[112.4865,49.524],[112.689,49.504],[112.8065,49.515],[112.907,49.5685],[113.044,49.608],[113.2185,49.837],[113.474,49.9355],[113.489,49.9735],[113.652,49.9995],[113.7425,50.075],[113.8785,50.105],[114.1785,50.243],[114.339,50.2825],[114.4675,50.2335],[114.617,50.2455],[114.831,50.2235],[115.0075,50.164],[115.0675,50.094],[115.224,49.9885],[115.361,49.94],[115.5065,49.907],[115.6825,49.886],[115.7445,49.8875],[115.983,49.9705],[116.0725,50.0135],[116.233,50.036],[116.3435,49.996],[116.527,49.947],[116.6215,49.931],[116.7135,49.8455],[117.0615,49.6855],[117.2735,49.631],[117.4715,49.6265],[117.634,49.5645],[117.793,49.52],[117.881,49.5155],[117.891,49.5935],[117.974,49.62],[118.074,49.614],[118.1125,49.658],[118.2045,49.69],[118.2395,49.7375],[118.372,49.786],[118.555,49.922],[118.648,49.9475],[118.7345,49.945],[118.9375,49.9915],[118.9865,49.9765],[119.084,49.9845],[119.274,50.105],[119.3295,50.172],[119.31,50.2165],[119.351,50.3575],[119.247,50.3415],[119.145,50.388],[119.2425,50.4465],[119.274,50.6035],[119.3475,50.628],[119.3735,50.68],[119.442,50.694],[119.499,50.766],[119.5205,50.862],[119.5105,50.8975],[119.5835,50.975],[119.663,51.012],[119.754,51.0885],[119.7815,51.172],[119.7525,51.2125],[119.812,51.2315],[119.803,51.278],[119.8685,51.2935],[119.8755,51.333],[119.933,51.354],[119.9955,51.458],[119.978,51.502],[120.045,51.5525],[120.0505,51.6305],[120.1665,51.678],[120.3045,51.781],[120.356,51.7875],[120.3915,51.8305],[120.544,51.883],[120.5425,51.9075],[120.6475,51.9225],[120.712,52.007],[120.679,52.036],[120.755,52.103],[120.7795,52.1635],[120.739,52.2045],[120.7515,52.2555],[120.6215,52.3245],[120.618,52.361],[120.6835,52.429],[120.6865,52.5175],[120.6185,52.5705],[120.4775,52.629],[120.391,52.615],[120.282,52.622],[120.1845,52.577],[120.0785,52.583],[120.027,52.6355],[120.062,52.707],[120.0265,52.7765],[120.281,52.861],[120.3615,52.943],[120.5555,53.0805],[120.636,53.104],[120.6835,53.171],[120.816,53.241],[120.8155,53.2685],[120.941,53.2955],[121.1275,53.275],[121.276,53.289],[121.327,53.323],[121.41,53.317],[121.572,53.3475],[121.6945,53.3905],[121.8175,53.4155],[121.9485,53.429],[122.079,53.4205],[122.168,53.471],[122.2435,53.4635],[122.3495,53.5005],[122.4295,53.443],[122.596,53.464],[122.8495,53.4565],[123.06,53.5075],[123.1375,53.498],[123.223,53.5495],[123.3045,53.5595],[123.3755,53.5365],[123.4785,53.528],[123.622,53.5455],[123.6855,53.4985],[123.8795,53.4805],[124.0615,53.399],[124.11,53.3465],[124.2325,53.378],[124.3205,53.329],[124.37,53.2525],[124.4965,53.2035],[124.637,53.207],[124.717,53.187],[124.7115,53.1515],[124.824,53.143],[124.9755,53.196],[125.158,53.204],[125.3025,53.146],[125.498,53.0885],[125.5315,53.0545],[125.605,53.0815],[125.6725,53.0075],[125.734,52.9915],[125.7275,52.944],[125.6665,52.9225],[125.6555,52.869],[125.8295,52.8955],[125.909,52.8185],[125.9395,52.763],[126.0085,52.7925],[126.096,52.7755],[126.0375,52.7345],[126.053,52.676],[125.9835,52.673],[125.973,52.6085],[126.012,52.5785],[126.068,52.603],[126.2015,52.531],[126.181,52.4775],[126.2625,52.4735],[126.347,52.383],[126.311,52.329],[126.346,52.2625],[126.3005,52.206],[126.49,52.1595],[126.5565,52.12],[126.5125,52.053],[126.4385,52.0255],[126.465,51.9365],[126.538,51.885],[126.6175,51.7745],[126.671,51.727],[126.7155,51.727],[126.7215,51.6245],[126.7085,51.5685],[126.833,51.5345],[126.776,51.444],[126.801,51.4205],[126.896,51.408],[126.924,51.363],[126.8135,51.3295],[126.8435,51.253],[126.9155,51.2615],[126.8675,51.3095],[126.9455,51.3335],[126.973,51.298],[126.894,51.2035],[126.9175,51.057],[126.986,51.021],[127.0465,50.9605],[127.135,50.9125],[127.233,50.774],[127.2915,50.7545],[127.2875,50.664],[127.367,50.574],[127.311,50.524],[127.292,50.457],[127.353,50.4435],[127.3615,50.396],[127.3255,50.335],[127.3805,50.285],[127.6015,50.2375],[127.6035,50.181],[127.572,50.1285],[127.5015,50.0595],[127.499,49.972],[127.5415,49.917],[127.5255,49.819],[127.5565,49.7905],[127.6485,49.7765],[127.673,49.748],[127.687,49.667],[127.7815,49.622],[127.7985,49.5935],[127.885,49.5695],[127.959,49.5965],[128.0345,49.5555],[128.2015,49.538],[128.2345,49.5615],[128.336,49.542],[128.367,49.5835],[128.553,49.602],[128.6585,49.57],[128.802,49.5505],[128.747,49.513],[128.757,49.476],[128.8605,49.4895],[128.9835,49.457],[129.0735,49.3535],[129.126,49.3505],[129.1745,49.3885],[129.261,49.392],[129.309,49.357],[129.378,49.3755],[129.369,49.418],[129.4305,49.4415],[129.5365,49.398],[129.548,49.309],[129.6,49.276],[129.7105,49.291],[129.7535,49.252],[129.7525,49.204],[129.8425,49.177],[129.8585,49.1085],[129.9275,49.0735],[129.928,49.0325],[129.998,49.02],[130.0505,48.972],[130.1905,48.9],[130.2245,48.865],[130.294,48.869],[130.421,48.904],[130.5375,48.8535],[130.64,48.883],[130.682,48.8655],[130.607,48.775],[130.522,48.626],[130.6065,48.5735],[130.6085,48.502],[130.66,48.4905],[130.758,48.4975],[130.7305,48.444],[130.7665,48.3585],[130.8355,48.3015],[130.765,48.2475],[130.752,48.19],[130.6535,48.1075],[130.6905,48.04],[130.864,47.936],[130.9515,47.815],[130.9575,47.7205],[130.9935,47.696],[131.0895,47.683],[131.175,47.6955],[131.2545,47.735],[131.366,47.7265],[131.425,47.7435],[131.5475,47.7265],[131.5665,47.665],[131.622,47.657],[131.678,47.7025],[131.7305,47.7045],[131.812,47.667],[131.902,47.691],[131.9425,47.6615],[131.989,47.701],[132.253,47.708],[132.309,47.757],[132.3845,47.7505],[132.485,47.716],[132.5485,47.7145],[132.6035,47.742],[132.5985,47.8105],[132.6805,47.872],[132.652,47.9355],[132.719,47.962],[132.7805,47.925],[132.863,47.9915],[133.008,48.0405],[133.0495,48.105],[133.182,48.1085],[133.266,48.093],[133.3915,48.1235],[133.4645,48.109],[133.5565,48.1305],[133.58,48.1885],[133.7115,48.1875],[133.7465,48.257],[133.9495,48.3045],[134.0075,48.309],[134.0555,48.346],[134.14,48.338],[134.1885,48.3835],[134.337,48.379],[134.4085,48.388],[134.4705,48.422],[134.5805,48.407],[134.688,48.408],[134.768,48.3575],[134.717,48.28],[134.676,48.2575],[134.6675,48.152],[134.6275,48.097],[134.5445,48.0245],[134.544,47.985],[134.593,47.9475],[134.607,47.9015],[134.6705,47.8775],[134.6635,47.825],[134.775,47.74],[134.7435,47.6835],[134.6755,47.625],[134.6805,47.598],[134.5725,47.5185],[134.5705,47.4895],[134.486,47.443],[134.311,47.4335],[134.2555,47.3665],[134.1695,47.3185],[134.15,47.253],[134.22,47.187],[134.218,47.1075],[134.14,47.0925],[134.0965,47.009],[134.0555,46.975],[134.068,46.933],[134.0355,46.8855],[134.016,46.811],[134.0265,46.7545],[134.0135,46.666],[133.913,46.596],[133.883,46.521],[133.8455,46.481],[133.9275,46.4195],[133.9375,46.3795],[133.8705,46.3615],[133.9105,46.2635],[133.823,46.2235],[133.682,46.1375],[133.7355,46.054],[133.6755,45.9885],[133.67,45.9435],[133.61,45.9415],[133.5765,45.8665],[133.505,45.8885],[133.4615,45.8355],[133.476,45.7805],[133.435,45.703],[133.44,45.6485],[133.386,45.578],[133.213,45.5135],[133.141,45.434],[133.135,45.365],[133.1115,45.3505],[133.1045,45.2645],[133.1305,45.128],[133.0615,45.0945],[133.0375,45.0565],[132.9385,45.0285],[132.8645,45.0575],[132.004,45.252],[131.9235,45.2875],[131.91,45.337],[131.828,45.31],[131.781,45.244],[131.6745,45.2135],[131.643,45.157],[131.6875,45.13],[131.6245,45.0725],[131.5355,45.017],[131.427,44.965],[131.35,44.9875],[131.271,44.9215],[131.1985,44.916],[131.152,44.9445],[131.082,44.922],[131.0945,44.8945],[130.977,44.863],[131.0085,44.8105],[131.1045,44.7075],[131.303,44.044],[131.2555,44.0305],[131.2345,43.965],[131.261,43.9365],[131.2095,43.833],[131.233,43.6655],[131.203,43.582],[131.207,43.5265],[131.2695,43.493],[131.311,43.395],[131.273,43.3755],[131.2485,43.2675],[131.1985,43.234],[131.21,43.1485],[131.1145,43.067],[131.0955,43.021],[131.14,42.943],[131.1085,42.9135],[131.0105,42.9105],[131.0355,42.8605],[130.9415,42.8745],[130.882,42.8505],[130.828,42.8805],[130.776,42.8395],[130.659,42.845],[130.5565,42.813],[130.526,42.785],[130.4595,42.77],[130.4055,42.729],[130.4575,42.686],[130.5215,42.7015],[130.588,42.665],[130.627,42.5975],[130.5535,42.5185],[130.592,42.4845],[130.5925,42.446],[130.637,42.4185],[130.6695,42.378],[130.6545,42.325],[130.702,42.2925],[130.9245,42.1705],[130.9505,42.216],[131.071,42.2915],[131.341,42.376],[131.59,42.4635],[131.6685,42.5105],[131.741,42.596],[131.835,42.6645],[132.156,42.5815],[132.285,42.533],[132.403,42.5335],[132.5455,42.5835],[132.6965,42.568],[132.9315,42.489],[133.0545,42.4725],[133.244,42.4975],[133.447,42.571],[133.5425,42.5955],[133.7615,42.609],[133.8315,42.623],[134.025,42.6815],[134.1395,42.7375],[134.354,42.883],[134.5665,42.979],[134.8135,43.099],[134.916,43.136],[135.027,43.222],[135.1545,43.2925],[135.288,43.3345],[135.381,43.405],[135.4455,43.508],[135.6575,43.648],[135.7495,43.759],[135.78,43.8165],[135.874,43.9385],[135.92,44.0505],[136.032,44.1615],[136.065,44.2085],[136.1415,44.262],[136.309,44.333],[136.434,44.4355],[136.5415,44.579],[136.63,44.6195],[136.702,44.6845],[136.743,44.76],[136.896,44.9075],[136.99,44.9725],[137.123,45.112],[137.1865,45.158],[137.308,45.215],[137.3765,45.2645],[137.518,45.396],[137.5895,45.481],[137.6935,45.5345],[137.841,45.632],[137.9425,45.72],[138.0365,45.854],[138.1625,45.957],[138.346,46.1315],[138.387,46.1945],[138.403,46.299],[138.533,46.388],[138.5965,46.4435],[138.6455,46.566],[138.654,46.6645],[138.7415,46.7645],[138.829,46.9465],[138.9105,46.996],[139.0015,47.0845],[139.188,47.169],[139.258,47.234],[139.327,47.3355],[139.348,47.407],[139.4095,47.5135],[139.4745,47.5865],[139.5185,47.6755],[139.5835,47.745],[139.7525,47.8315],[139.8885,47.9475],[139.9565,48.025],[140.052,48.0885],[140.2715,48.2085],[140.438,48.3535],[140.474,48.435],[140.5085,48.721],[140.6235,48.821],[140.6885,48.938],[140.6885,49.016],[140.6605,49.1095],[140.729,49.273],[140.7355,49.328],[140.7945,49.413],[140.844,49.5145],[140.8535,49.577],[140.835,49.7575],[140.912,49.947],[140.9835,50.0305],[140.997,50.1075],[140.983,50.1545],[140.823,50.4125],[140.797,50.467],[140.805,50.7425],[140.9375,50.876],[140.9905,50.9975],[140.9995,51.058],[140.987,51.147],[141.0405,51.2045],[141.1455,51.283],[141.2065,51.3965],[141.212,51.4485],[141.2865,51.5045],[141.4145,51.5615],[141.5685,51.5285],[141.7235,51.4115],[141.837,51.2665],[141.931,51.078],[141.8385,50.9885],[141.741,50.8725],[141.718,50.795],[141.7435,50.651],[141.73,50.525],[141.763,50.423],[141.794,50.3775],[141.8565,50.1305],[141.829,50.042],[141.8215,49.9625],[141.844,49.8045],[141.8315,49.536],[141.774,49.4],[141.7645,49.28],[141.7335,49.1915],[141.7125,49.057],[141.559,48.8365],[141.548,48.74],[141.5775,48.632],[141.6135,48.5665],[141.7075,48.4425],[141.8395,48.2905],[141.867,48.163],[141.878,48.0525],[141.81,47.934],[141.787,47.8595],[141.731,47.7775],[141.675,47.6695],[141.6595,47.5385],[141.672,47.284],[141.691,47.227],[141.758,47.131],[141.717,46.9975],[141.649,46.885],[141.53,46.654],[141.521,46.528],[141.542,46.4325],[141.601,46.2765],[141.6325,46.0995],[141.638,46.0165],[141.677,45.94],[141.763,45.853],[141.7165,45.7425],[141.9005,45.7185],[142.292,45.656],[142.3495,45.8225],[142.4505,45.9615],[142.5205,46.1265],[142.5405,46.2425],[142.588,46.336],[142.7685,46.399],[142.934,46.4075],[143.0645,46.324],[143.118,46.2245],[143.11,46.089],[143.1385,45.9615],[143.1655,45.921],[143.239,45.863],[143.3365,45.8285],[143.445,45.8215],[143.525,45.8355],[143.616,45.8775],[143.666,45.923],[143.7435,46.047],[143.7725,46.1435],[143.8905,46.3095],[143.9055,46.3755],[143.8815,46.461],[143.831,46.531],[143.7995,46.7355],[143.7535,46.89],[143.717,46.939],[143.601,47.016],[143.335,47.1085],[143.335,47.1645],[143.296,47.3175],[143.185,47.4645],[143.15,47.495],[142.929,47.619],[142.873,47.689],[142.8235,47.796],[142.826,47.929],[142.869,48.0915],[143.0065,48.399],[143.032,48.4805],[143.135,48.667],[143.246,48.8025],[143.2745,48.8525],[143.286,48.967],[143.3105,49.067],[143.4445,49.1075],[143.598,49.116],[143.8945,49.077],[143.9535,49.0385],[144.116,48.8985],[144.253,48.8215],[144.339,48.7355],[144.373,48.6115],[144.3365,48.5545],[144.3265,48.4855],[144.343,48.435],[144.3945,48.3745],[144.504,48.319],[144.5935,48.3025],[144.6765,48.304],[144.8305,48.3535],[144.8865,48.3995],[144.935,48.4865],[145.0005,48.5345],[145.0465,48.6125],[145.043,48.6885],[145.0035,48.81],[144.988,48.901],[144.959,48.9545],[144.896,49.012],[144.729,49.1115],[144.659,49.165],[144.587,49.2915],[144.552,49.399],[144.5465,49.507],[144.5255,49.576],[144.4335,49.6875],[144.43,49.7785],[144.315,49.975],[144.293,50.08],[144.1305,50.301],[144.064,50.4415],[144.0175,50.5865],[144.014,50.6635],[143.986,50.759],[143.984,50.823],[143.955,50.898],[143.8455,51.246],[143.794,51.3655],[143.755,51.522],[143.649,51.764],[143.545,51.9255],[143.479,52.0685],[143.4675,52.2055],[143.593,52.389],[143.645,52.5895],[143.6635,52.8595],[143.591,53.1975],[143.532,53.362],[143.4625,53.492],[143.2595,53.8175],[143.24,53.8565],[143.3085,53.991],[143.336,54.074],[143.318,54.1615],[143.2815,54.2245],[143.148,54.3515],[143.001,54.54],[142.9025,54.592],[142.825,54.6135],[142.6985,54.626],[142.5675,54.612],[142.4765,54.5825],[142.1,54.487],[141.995,54.436],[141.928,54.3595],[141.918,54.282],[141.9665,54.198],[142.0295,54.153],[142.176,54.003],[142.296,53.916],[142.307,53.8435],[142.257,53.812],[142.0375,53.71],[141.759,53.6255],[141.6235,53.5995],[141.389,53.594],[141.2535,53.641],[141.006,53.704],[140.912,53.7355],[140.763,53.85],[140.6165,54.061],[140.453,54.187],[140.1875,54.308],[140.1115,54.447],[139.9995,54.5195],[139.8795,54.5495],[139.7785,54.558],[139.6775,54.5485],[139.5315,54.502],[139.3455,54.393],[139.2485,54.3825],[139.0765,54.4195],[138.8905,54.4855],[138.786,54.502],[138.5895,54.6915],[138.7625,55.013],[138.776,55.101],[138.7345,55.1805],[138.622,55.253],[138.491,55.2865],[138.335,55.2945],[137.832,55.3605],[137.7255,55.389],[137.5485,55.3905],[137.3395,55.337],[137.255,55.283],[137.1875,55.1975],[136.905,55.126],[136.723,55.104],[136.646,55.054],[136.411,55.016],[136.05,55.0045],[136.1915,55.076],[136.307,55.116],[136.4155,55.1705],[136.537,55.2715],[136.743,55.418],[136.9615,55.4815],[137.2835,55.637],[137.424,55.739],[137.548,55.7665],[137.655,55.811],[137.711,55.856],[137.9805,55.997],[138.133,56.12],[138.1765,56.171],[138.247,56.2105],[138.4,56.248],[138.545,56.3285],[138.613,56.4375],[138.628,56.5605],[138.8035,56.628],[139.152,56.8835],[139.303,57.005],[139.5065,57.1055],[139.647,57.145],[139.8045,57.228],[140.0615,57.3485],[140.1715,57.447],[140.202,57.501],[140.463,57.566],[140.5475,57.5945],[140.793,57.7275],[140.8535,57.793],[140.8745,57.8755],[140.9595,57.9955],[141.028,58.127],[141.0615,58.164],[141.193,58.2445],[141.545,58.3475],[141.787,58.4485],[141.985,58.564],[142.1805,58.7245],[142.2835,58.8355],[142.3885,58.912],[142.639,59.023],[142.9735,59.085],[143.1385,59.129],[143.521,59.121],[143.5965,59.1285],[143.891,59.1985],[144.048,59.2005],[144.382,59.1705],[145.0205,59.169],[145.263,59.185],[145.5785,59.089],[145.707,59.003],[145.836,58.959],[145.9345,58.9445],[146.127,58.946],[146.2725,58.977],[146.465,59.0015],[146.5995,59.05],[146.9125,59.1465],[147.054,59.128],[147.359,59.056],[147.47,59.0395],[147.619,59.04],[147.8405,59.0625],[148.3665,59.054],[148.4415,59.047],[148.8475,58.9255],[148.9265,58.915],[149.6765,58.862],[150.542,58.798],[150.6725,58.806],[150.9835,58.766],[151.0645,58.7005],[151.1905,58.654],[151.341,58.6375],[151.597,58.647],[151.69,58.642],[152.1935,58.6985],[152.267,58.7165],[152.5,58.742],[152.5835,58.721],[152.731,58.706],[152.8975,58.705],[153.0515,58.7215],[153.172,58.7615],[153.4245,58.886],[153.82,58.879],[154.0345,58.8475],[154.1725,58.859],[154.499,58.9175],[154.688,58.937],[154.81,58.9275],[154.9485,58.941],[155.109,58.9735],[155.5735,58.9975],[155.6955,59.015],[155.8225,59.063],[155.899,59.1285],[155.9815,59.2905],[155.971,59.383],[155.8945,59.4575],[155.736,59.5165],[155.392,59.556],[155.2895,59.62],[155.118,59.6805],[154.977,59.6985],[154.8805,59.9035],[154.903,59.9735],[154.983,60.051],[155.104,60.122],[155.1765,60.1855],[155.41,60.2625],[155.47,60.294],[155.6305,60.338],[155.776,60.396],[155.9625,60.4915],[156.1775,60.5535],[156.267,60.6115],[156.3085,60.6685],[156.328,60.765],[156.3655,60.8305],[156.4365,60.8925],[156.597,60.9615],[156.854,61.0215],[156.989,61.0835],[157.0615,61.158],[157.0635,61.2915],[157.177,61.391],[157.236,61.4175],[157.518,61.4965],[157.918,61.536],[158.1525,61.5345],[158.6415,61.604],[158.8265,61.6145],[159.15,61.5725],[159.2585,61.5145],[159.4245,61.3915],[159.362,61.341],[159.3315,61.2665],[159.406,61.0055],[159.3795,60.914],[159.4255,60.8385],[159.559,60.763],[159.733,60.6085],[159.747,60.534],[159.8285,60.4525],[159.945,60.402],[160.0915,60.3775],[160.235,60.3775],[160.397,60.4055],[160.497,60.4385],[161.1535,60.149],[160.88,59.9435],[160.776,59.891],[160.4935,59.7665],[160.3325,59.7375],[160.19,59.6805],[160.0995,59.5815],[159.8975,59.458],[159.743,59.352],[159.5735,59.263],[159.414,59.1255],[159.278,58.9145],[159.218,58.846],[158.898,58.6655],[158.771,58.5535],[158.3925,58.4],[158.1775,58.2885],[158.067,58.2195],[157.6345,58.2145],[157.4405,58.1715],[157.3295,58.1055],[156.8505,58.036],[156.623,57.9585],[156.525,57.8965],[156.4135,57.776],[156.405,57.698],[156.4885,57.596],[156.6175,57.5145],[156.627,57.4915],[156.3505,57.4235],[156.2105,57.363],[156.148,57.2925],[156.1365,57.234],[156.1635,57.115],[156.112,57.049],[155.914,56.9775],[155.8105,56.9195],[155.665,56.7725],[155.6065,56.679],[155.532,56.4895],[155.2965,55.9795],[155.299,55.8205],[155.2815,55.667],[155.222,55.4015],[155.215,55.2325],[155.257,55.057],[155.289,54.8155],[155.339,54.609],[155.393,54.415],[155.487,54.15],[155.566,53.9485],[155.654,53.629],[155.672,53.5275],[155.7465,53.237],[155.7995,52.9],[155.8145,52.8365],[155.924,52.606],[156.0535,52.365],[156.106,52.245],[156.148,52.0905],[156.1745,51.839],[156.175,51.4625],[156.2215,51.2835],[156.2455,51.223],[156.3435,51.104],[156.272,51.0325],[156.11,50.9695],[155.741,51.096],[155.608,51.1185],[155.414,51.111],[155.252,51.056],[155.185,50.995],[155.144,50.9095],[155.1545,50.8235],[155.2065,50.7385],[155.294,50.6725],[155.391,50.556],[155.1005,50.4835],[154.7635,50.3615],[154.679,50.2955],[154.6485,50.2425],[154.657,50.138],[154.492,50.0145],[154.4165,50.0165],[154.2815,49.992],[154.211,49.9625],[154.113,49.879],[154.081,49.799],[154.0825,49.7165],[154.127,49.644],[154.1965,49.593],[154.316,49.4185],[154.218,49.294],[153.9425,49.1725],[153.875,49.1675],[153.489,49.193],[153.3545,49.175],[153.2555,49.13],[153.184,49.0535],[153.165,48.9945],[153.1815,48.91],[153.244,48.8395],[153.3175,48.7995],[153.275,48.5005],[153.125,48.4855],[153.0305,48.444],[152.9515,48.361],[152.869,48.1455],[152.863,48.0885],[152.7905,47.9315],[152.7175,47.86],[152.634,47.704],[152.59,47.67],[152.385,47.5735],[152.238,47.5215],[151.945,47.269],[151.8365,47.15],[151.754,47.1035],[151.538,47.01],[151.4355,46.915],[151.4155,46.819],[151.435,46.7645],[151.511,46.6655],[151.5715,46.6235],[151.6725,46.5845],[151.748,46.5715],[151.872,46.575],[151.962,46.601],[152.127,46.7055],[152.2125,46.746],[152.355,46.85],[152.5375,47.025],[152.793,47.2165],[153.017,47.352],[153.1405,47.474],[153.277,47.5925],[153.3255,47.652],[153.3525,47.719],[153.5395,47.935],[153.5915,48.0315],[153.597,48.0935],[153.569,48.2845],[153.704,48.3505],[153.785,48.3315],[153.928,48.3355],[154.0065,48.3595],[154.1135,48.431],[154.2495,48.586],[154.438,48.7165],[154.483,48.767],[154.6885,48.8995],[154.799,48.946],[155.009,49.1355],[155.0845,49.1895],[155.145,49.2925],[155.189,49.484],[155.2225,49.564],[155.221,49.6595],[155.403,49.7945],[155.4875,49.806],[155.6035,49.8505],[155.7565,49.972],[155.9635,50.023],[156.138,50.1275],[156.234,50.24],[156.382,50.3605],[156.624,50.4885],[156.6955,50.5485],[156.776,50.6495],[156.9475,50.755],[157.2015,50.9315],[157.3885,51.033],[157.5475,51.1085],[157.755,51.2665],[157.9225,51.3555],[157.9965,51.4095],[158.151,51.4965],[158.265,51.6145],[158.3525,51.6785],[158.425,51.7585],[158.5245,51.835],[158.5805,51.8975],[158.6235,51.9875],[158.6935,52.068],[158.8205,52.18],[158.8815,52.2785],[158.898,52.3965],[158.8585,52.5315],[158.9185,52.6385],[158.94,52.7085],[159.0995,52.8585],[159.2845,52.9435],[159.7225,52.9695],[159.8525,52.919],[160.0575,52.8975],[160.1955,52.9225],[160.3325,53.0085],[160.3875,53.1095],[160.3895,53.1715],[160.361,53.2945],[160.299,53.389],[160.2915,53.572],[160.2735,53.685],[160.1945,53.829],[160.294,53.989],[160.4105,54.0875],[160.572,54.172],[160.6575,54.2395],[160.7565,54.292],[160.899,54.349],[161.049,54.3295],[161.1475,54.294],[161.2515,54.28],[161.544,54.268],[161.65,54.273],[161.8805,54.3235],[161.9765,54.372],[162.1135,54.471],[162.407,54.627],[162.4825,54.7305],[162.503,54.836],[162.4865,54.9095],[162.277,55.1175],[162.143,55.2305],[162.093,55.387],[162.0915,55.4565],[162.1335,55.5905],[162.249,55.7125],[162.296,55.8],[162.394,55.926],[162.439,55.959],[162.5335,55.9415],[162.745,55.846],[162.9985,55.806],[163.2075,55.8265],[163.322,55.8755],[163.4965,55.9885],[163.64,56.0585],[163.6955,56.1115],[163.7195,56.1995],[163.7055,56.3665],[163.6375,56.497],[163.649,56.673],[163.634,56.747],[163.555,56.8445],[163.216,57.0565],[163.143,57.213],[163.223,57.291],[163.3165,57.323],[163.4065,57.3745],[163.5365,57.4805],[163.6695,57.651],[163.694,57.708],[163.872,58.1885],[163.9745,58.464],[164.28,58.593],[164.603,58.6645],[164.802,58.6885],[164.9195,58.734],[165.0125,58.796],[165.0765,58.8725],[165.1165,59.005],[165.104,59.1015],[164.9975,59.2575],[165.8945,59.6325],[166.052,59.603],[166.1365,59.6],[166.404,59.6195],[166.5705,59.669],[166.6735,59.731],[166.7765,59.82],[166.8695,59.865],[167.0575,59.9785],[167.1575,60.0695],[167.2825,60.133],[167.5585,60.1985],[167.658,60.2305],[167.932,60.2835],[168.3265,60.3855],[168.6225,60.3465],[168.779,60.344],[168.9625,60.362],[169.1165,60.3515],[169.2455,60.311],[169.3825,60.231],[169.629,59.9595],[169.8705,59.8185],[170.089,59.7495],[170.1665,59.7345],[170.342,59.7285],[170.4675,59.745],[170.6225,59.7835],[170.7475,59.8505],[170.8095,59.916],[170.8625,60.077],[171.032,60.2045],[171.1225,60.3015],[171.2085,60.335],[171.3775,60.3755],[171.6165,60.447],[172.058,60.634],[172.1715,60.6435],[172.379,60.6995],[172.5185,60.8055],[172.743,60.885],[172.9515,61.026],[173.179,61.1055],[173.452,61.2455],[173.5575,61.288],[173.7465,61.3835],[174.0565,61.475],[174.302,61.566],[174.435,61.605],[174.6455,61.6205],[174.922,61.6785],[175.139,61.7745],[175.4135,61.832],[175.513,61.866],[175.652,61.9365],[175.8145,61.99],[176.184,62.0715],[176.4705,62.114],[176.6295,62.169],[176.759,62.2395],[177.0835,62.325],[177.1885,62.342],[177.5525,62.36],[177.827,62.3455],[178.1525,62.2765],[178.6815,62.182],[178.9255,62.0915],[179.087,62.0745],[179.3325,62.1035],[179.4305,62.135],[179.5315,62.1935],[179.7075,62.352],[179.938,62.5015],[180.0,62.5545],[180.0,62.876],[179.944,62.9385],[179.8865,63.15],[179.8085,63.235],[179.643,63.336],[179.383,63.438],[179.2685,63.565],[179.23,63.765],[179.1305,64.0135],[179.091,64.0845],[178.985,64.1735],[178.846,64.323],[178.936,64.429],[179.066,64.4685],[179.3005,64.561],[179.439,64.59],[179.6565,64.6055],[179.806,64.641],[180.0,64.7465],[180.0,65.0385],[180.0,65.3115],[180.0,65.3245],[180.0,65.33],[180.0,65.3605],[180.0,65.3825],[180.0,65.386],[180.0,65.4455],[180.0,65.454],[180.0,65.479],[180.0,65.8035],[180.0,65.8635],[180.0,65.866],[180.0,66.6405],[180.0,66.894],[180.0,66.914],[180.0,66.9295],[180.0,67.168],[180.0,67.552],[180.0,67.927],[180.0,68.3565],[180.0,68.513],[180.0,68.5505],[180.0,68.91],[180.0,68.9255],[180.0,68.93],[180.0,68.959],[180.0,68.97],[180.0,68.9755],[180.0,68.977],[180.0,69.2895],[179.7395,69.392],[179.219,69.537],[179.037,69.58],[178.735,69.632],[178.417,69.658],[178.221,69.655],[177.7995,69.759],[177.571,69.7915],[177.3055,69.8025],[177.15,69.823],[176.793,69.898],[176.564,70.0055],[176.341,70.059],[176.1935,70.074],[175.717,70.0775],[175.1115,70.04],[174.636,70.044],[174.4755,70.073],[174.214,70.1015],[173.5685,70.1305],[172.8885,70.1785],[172.753,70.183],[172.467,70.1545],[172.061,70.1795],[171.591,70.244],[171.3305,70.2565],[171.1005,70.2835],[170.9,70.2865],[170.662,70.3205],[170.4675,70.3225],[170.2085,70.299],[170.022,70.255],[169.9285,70.2145],[169.843,70.126],[169.535,70.1045],[169.4115,70.1245],[168.9515,70.1665],[168.8185,70.2015],[168.5695,70.23],[168.291,70.224],[167.932,70.1825],[167.6925,70.1245],[167.4555,70.0285],[167.3185,69.944],[167.2335,69.8695],[166.839,69.7075],[166.674,69.7125],[166.469,69.697],[166.322,69.704],[166.1735,69.7345],[165.827,69.7705],[165.528,69.7695],[165.2945,69.7885],[165.099,69.784],[164.89,69.761],[164.715,69.794],[164.55,69.885],[164.3975,69.9245],[164.1815,69.948],[163.854,69.946],[163.5665,69.8855],[163.2535,69.904],[163.0175,69.8885],[162.8185,69.854],[162.6985,69.867],[162.3005,69.8765],[161.7935,69.878],[161.1905,69.889],[160.6545,69.9225],[160.5375,70.099],[160.6645,70.195],[160.676,70.296],[160.6125,70.4095],[160.4285,70.603],[160.8045,70.5985],[161.0715,70.6305],[161.1985,70.5345],[161.366,70.487],[161.507,70.4675],[161.7605,70.461],[161.924,70.4745],[162.0845,70.441],[162.3685,70.4205],[162.653,70.437],[162.8755,70.4785],[163.0625,70.567],[163.094,70.681],[163.0075,70.7515],[162.8815,70.7975],[162.7115,70.83],[162.515,70.8475],[162.2745,70.8505],[162.126,70.9795],[161.988,71.0445],[161.7615,71.108],[161.509,71.1295],[161.2915,71.122],[161.0625,71.0865],[160.966,71.0575],[160.716,71.1],[160.5015,71.113],[160.156,71.0895],[159.906,71.0095],[159.823,70.894],[159.308,71.028],[158.916,71.104],[158.6745,71.1425],[157.972,71.218],[157.259,71.2665],[157.062,71.275],[156.407,71.267],[156.157,71.276],[155.754,71.2665],[155.3985,71.2325],[154.9705,71.205],[154.3325,71.1505],[153.6115,71.0635],[153.3835,71.0555],[152.9845,71.0185],[152.812,71.0175],[152.2775,71.364],[151.675,71.6805],[151.3995,71.7475],[150.832,71.816],[150.726,71.95],[150.598,72.0555],[150.484,72.122],[150.1945,72.2335],[149.995,72.2935],[149.7365,72.352],[149.4985,72.392],[149.044,72.4445],[148.803,72.484],[148.4435,72.518],[147.9465,72.5165],[147.615,72.5265],[147.199,72.5115],[146.5195,72.6315],[145.721,72.716],[145.131,72.769],[144.3815,72.8405],[144.3205,73.2205],[144.308,73.4295],[144.246,73.516],[144.0335,73.641],[143.77,73.748],[143.35,73.868],[143.0955,73.959],[142.8345,74.0115],[142.6505,74.032],[141.8375,74.195],[141.802,74.26],[142.956,74.622],[144.1215,74.7045],[144.285,74.722],[144.51,74.7715],[145.3855,75.0425],[145.4905,75.111],[145.837,75.01],[146.281,74.9435],[146.5795,74.866],[146.774,74.8285],[147.189,74.784],[147.638,74.639],[147.966,74.588],[148.606,74.5395],[148.9375,74.529],[149.2945,74.531],[149.731,74.551],[150.0605,74.5825],[150.3625,74.6285],[150.9455,74.6795],[151.215,74.732],[151.3815,74.811],[151.465,74.944],[151.6765,75.045],[151.7415,75.142],[151.682,75.207],[151.5685,75.2575],[151.2415,75.323],[150.328,75.3985],[150.0465,75.41],[149.6035,75.4545],[148.587,75.594],[148.187,75.599],[147.1235,75.718],[146.9205,75.7595],[146.5845,75.787],[146.112,75.767],[145.8235,75.712],[145.4155,75.789],[145.242,75.839],[144.8035,75.895],[144.6525,75.9355],[144.3045,75.996],[143.948,76.0355],[143.4475,76.05],[143.1045,76.026],[142.9415,76.087],[142.736,76.1385],[142.461,76.231],[142.262,76.2745],[141.814,76.3465],[140.7585,76.4775],[140.3035,76.496],[140.1175,76.4875],[138.947,76.3995],[138.587,76.3915],[138.1005,76.3345],[137.6985,76.2655],[137.5015,76.2085],[137.119,76.142],[136.853,76.0675],[136.6355,75.945],[136.522,75.9295],[136.3555,76.003],[135.98,76.062],[135.6725,76.071],[135.4705,76.061],[135.209,76.027],[135.029,75.978],[134.876,75.8775],[134.863,75.793],[134.7975,75.6855],[134.8395,75.5915],[134.7005,75.4455],[134.7435,75.3415],[134.912,75.2485],[135.0925,75.2055],[135.355,75.1745],[135.8485,75.1665],[136.2155,75.196],[136.5475,75.0195],[136.7705,74.926],[137.2,74.8285],[137.314,74.7485],[137.5595,74.641],[137.8785,74.573],[138.351,74.534],[138.6615,74.4675],[139.4485,74.136],[139.445,74.0755],[139.5105,73.9805],[139.1575,73.5005],[139.078,73.404],[139.115,73.3405],[139.219,73.2675],[139.481,73.179],[139.923,72.9005],[139.508,72.748],[139.21,72.668],[138.739,72.4745],[138.5985,72.393],[138.2915,72.1225],[138.0455,72.059],[137.843,71.9685],[137.2645,71.812],[136.2335,71.8515],[135.697,71.843],[135.1955,71.8],[134.8605,71.753],[134.56,71.6875],[134.232,71.5755],[133.9205,71.628],[133.7785,71.6685],[133.706,71.7155],[133.8925,71.7985],[133.9565,71.857],[133.9595,71.919],[133.8615,72.0],[133.7035,72.0735],[133.5375,72.1255],[133.232,72.165],[133.032,72.1685],[132.573,72.138],[132.2375,72.0745],[131.973,71.9515],[131.6905,71.7385],[131.5765,71.612],[131.458,71.407],[131.378,71.3],[130.814,71.192],[130.7475,71.202],[130.668,71.485],[130.665,71.5885],[130.53,71.7045],[130.2445,71.791],[130.287,72.2275],[130.331,72.372],[130.2455,73.004],[130.1655,73.09],[130.078,73.1295],[129.205,73.424],[128.648,73.6085],[128.2575,73.689],[127.814,73.7245],[126.3125,73.863],[125.464,74.037],[125.278,74.08],[124.96,74.127],[124.581,74.135],[124.331,74.1185],[123.3685,74.188],[123.0215,74.1825],[122.7835,74.152],[122.551,74.0835],[122.427,73.9875],[122.3935,73.8245],[122.5705,73.716],[122.4605,73.5325],[122.494,73.412],[122.573,73.312],[122.4125,73.2915],[121.893,73.26],[121.701,73.238],[121.4115,73.1605],[121.1165,73.138],[121.0415,73.2105],[120.941,73.263],[120.68,73.3265],[120.2935,73.3735],[120.048,73.3835],[119.438,73.649],[119.1575,73.7115],[118.715,73.76],[118.1175,73.7785],[117.793,73.7715],[117.5575,73.778],[116.7865,73.863],[116.3645,73.866],[115.7555,73.9065],[115.3355,73.9075],[114.987,73.881],[114.542,73.789],[114.318,73.798],[114.025,73.787],[113.8755,73.916],[113.7425,73.992],[113.584,74.039],[113.7755,74.0845],[113.968,74.1945],[114.113,74.2555],[114.211,74.376],[114.1505,74.4895],[113.7095,74.737],[113.5665,74.894],[114.219,75.1235],[114.385,75.2125],[114.4635,75.2845],[114.5535,75.4395],[114.4915,75.5515],[114.5745,75.6475],[114.706,75.751],[114.7395,75.8425],[114.7115,75.9465],[114.636,76.001],[114.3655,76.139],[114.296,76.4235],[114.027,76.5445],[113.6485,76.627],[113.4155,76.6555],[113.1145,76.7595],[112.8735,76.8035],[111.9425,76.883],[111.616,76.945],[111.3635,76.968],[110.938,76.986],[109.695,76.9565],[108.9925,77.0955],[108.6185,77.384],[108.481,77.448],[108.4355,77.9435],[108.6315,78.0145],[108.729,78.084],[108.74,78.169],[108.577,78.2625],[108.4645,78.294],[107.235,78.535],[106.4355,78.648],[106.3685,78.7845],[106.1415,78.9065],[105.297,79.1535],[104.6685,79.312],[104.0105,79.438],[103.7465,79.505],[103.4965,79.547],[103.048,79.5965],[101.2895,79.8065],[100.862,80.002],[100.2505,80.167],[99.869,80.219],[99.0645,80.27],[98.707,80.3035],[99.345,80.607],[99.405,80.6925],[99.305,80.79],[99.099,80.862],[97.8885,81.098],[97.3375,81.282],[97.0615,81.351],[96.5015,81.4375],[95.8245,81.4735],[95.4445,81.4715],[94.874,81.4465],[94.3235,81.3965],[92.6205,81.288],[92.2175,81.3555],[91.475,81.417],[90.92,81.4255],[90.198,81.399],[89.3475,81.331],[89.0115,81.2625],[88.8835,81.197],[88.8935,81.093],[89.036,81.0195],[89.213,80.9725],[89.6225,80.91],[90.1045,80.8665],[90.5555,80.842],[91.029,80.8355],[91.5405,80.8495],[90.438,80.494],[90.31,80.4165],[90.3045,80.302],[89.8795,80.1245],[89.8265,80.059],[90.0405,79.7855],[89.518,79.6245],[89.456,79.5745],[89.499,79.4975],[89.6375,79.4435],[89.9935,79.3785],[90.559,79.319],[91.4215,79.0115],[91.7445,78.9495],[92.371,78.88],[92.759,78.862],[94.66,78.841],[95.402,78.8],[96.5715,78.7105],[96.8665,78.6495],[96.9745,78.306],[97.1015,78.219],[97.242,78.1805],[98.394,77.937],[96.83,77.558],[95.989,77.349],[95.543,77.258],[95.224,77.229],[94.9795,77.19],[94.605,77.1025],[93.821,76.8895],[93.2465,76.712],[93.193,76.6815],[92.4275,76.47],[91.674,76.259],[91.3215,76.248],[91.0285,76.2245],[89.869,76.041],[88.955,75.8945],[88.7185,75.903],[87.889,75.901],[87.655,75.8835],[87.4535,75.8495],[87.2185,75.7545],[85.612,75.4295],[85.448,75.38],[84.6205,75.01],[83.6315,74.785],[83.5295,74.741],[83.462,74.669],[83.491,74.5615],[82.3295,74.331],[82.055,74.287],[81.8825,74.236],[81.7535,74.1735],[81.069,73.992],[80.204,73.7595],[79.0645,73.7045],[78.0625,73.6555],[77.0605,73.607],[76.6175,73.679],[76.287,73.714],[75.7705,73.7205],[75.3005,73.6865],[75.0505,73.6335],[74.764,73.5145],[74.682,73.435],[74.3855,73.309],[74.0805,73.2915],[73.8955,73.2625],[72.2105,73.338],[71.8685,73.5125],[71.675,73.574],[71.47,73.6175],[71.0715,73.6675],[70.753,73.676],[70.082,73.6275],[69.6185,73.5475],[69.377,73.464],[68.7285,72.9925],[68.6125,72.862],[68.447,72.794],[68.321,72.683],[68.2925,72.5835],[68.189,72.447],[68.135,72.2925],[68.02,72.082],[67.895,71.892],[67.7145,71.7595],[67.476,71.6525],[67.2235,71.59],[67.0355,71.5205],[66.815,71.47],[66.51,71.4185],[66.3735,71.3575],[66.229,71.212],[66.155,71.1695],[65.9645,70.9965],[65.853,70.851],[65.8155,70.705],[65.8335,70.612],[65.895,70.5155],[66.095,70.37],[66.321,70.2855],[66.3865,70.11],[65.7425,69.8645],[64.965,69.564],[64.702,69.642],[64.4045,69.7065],[63.939,69.784],[63.289,69.8735],[62.8565,69.9125],[62.18,69.933],[62.0135,69.946],[61.617,70.0055],[61.382,70.052],[60.6695,70.123],[60.4565,70.235],[60.2435,70.282],[60.078,70.333],[59.8395,70.4695],[59.6805,70.522],[59.624,70.559],[59.4265,70.625],[58.851,70.7305],[57.907,70.923],[57.498,71.0075],[57.113,71.149],[56.6875,71.3375],[56.648,71.3885],[56.3405,71.6385],[56.188,71.829],[56.1785,71.891],[56.1075,71.9675],[56.109,72.0265],[56.21,72.126],[56.214,72.2345],[56.257,72.3825],[56.8345,72.759],[57.074,72.953],[57.3325,73.133],[57.6575,73.258],[58.2015,73.5005],[58.5305,73.68],[58.89,73.8895],[59.309,74.0835],[59.9675,74.227],[60.184,74.3045],[60.497,74.4905],[60.893,74.622],[61.2085,74.744],[61.6225,74.919],[62.105,75.087],[62.6435,75.2375],[62.975,75.3055],[63.3265,75.3625],[64.045,75.422],[64.3095,75.459],[64.632,75.535],[64.968,75.5605],[65.2495,75.6005],[65.559,75.6235],[66.0285,75.7225],[66.282,75.75],[66.4935,75.7895],[66.8075,75.8095],[67.0495,75.8515],[67.563,75.9115],[67.9385,75.982],[68.3625,76.0495],[68.738,76.0775],[69.0465,76.155],[69.705,76.4415],[69.7895,76.4955],[69.9395,76.6625],[69.944,76.7385],[69.799,76.8635],[69.613,76.956],[69.2425,77.092],[68.869,77.1525],[68.1225,77.2255],[67.7965,77.243],[67.491,77.234],[67.1795,77.2035],[66.5045,77.096],[65.456,76.8885],[65.203,76.8155],[65.076,76.755],[64.8595,76.7015],[64.5725,76.6635],[63.921,76.6105],[63.626,76.573],[63.3045,76.507],[62.699,76.464],[61.768,76.4965],[60.828,76.4775],[60.626,76.4595],[59.7405,76.336],[58.815,76.255],[58.4405,76.189],[58.26,76.122],[58.0525,75.9845],[57.908,75.923],[56.5215,75.533],[56.252,75.479],[56.0445,75.4105],[55.5225,75.3445],[55.167,75.2445],[55.0445,75.168],[54.978,75.076],[54.983,75.024],[55.066,74.941],[55.043,74.817],[54.6725,74.4805],[54.2945,74.236],[53.7835,74.0505],[53.1455,73.918],[52.9795,73.801],[52.963,73.7365],[53.0385,73.647],[53.2955,73.5595],[52.67,73.273],[51.993,72.9575],[51.4005,72.4965],[50.993,72.172],[50.7885,71.9205],[50.796,71.6345],[51.661,71.128],[52.5625,70.796],[53.2955,70.522],[54.7145,70.3545],[55.289,70.286],[56.6385,70.244],[57.2085,70.22],[57.897,70.071],[58.147,69.926],[58.3595,69.8355],[58.6685,69.7155],[59.153,69.5665],[59.358,69.5235],[59.626,69.4855],[59.7135,69.413],[60.081,69.1915],[60.108,69.081],[60.206,69.0035],[60.311,68.9585],[60.23,68.899],[60.075,68.9605],[60.0745,69.0435],[59.9715,69.1435],[59.866,69.197],[59.8115,69.2955],[59.678,69.3785],[59.4915,69.439],[59.2375,69.49],[59.0965,69.5445],[58.965,69.6175],[58.7715,69.664],[58.6585,69.6755],[58.396,69.678],[58.095,69.626],[57.9655,69.562],[57.9085,69.495],[57.909,69.4265],[58.016,69.3395],[58.1335,69.2995],[58.3575,69.1915],[58.5185,69.138],[58.129,69.0595],[57.6295,68.9675],[57.218,68.9295],[56.6275,69.0115],[55.9245,69.0555],[55.771,69.0955],[55.5045,69.131],[54.722,69.1835],[54.4485,69.1895],[53.959,69.173],[53.706,69.15],[53.311,69.0965],[53.059,69.0475],[52.53,68.9185],[52.1645,68.819],[51.907,68.741],[51.589,68.686],[51.2215,68.6895],[51.058,68.683],[50.755,68.6375],[50.542,68.574],[50.256,68.435],[49.94,68.3215],[49.5265,68.1855],[49.2315,68.102],[49.0205,68.0515],[48.9175,68.083],[48.7235,68.111],[48.5255,68.1115],[47.1115,68.033],[47.074,68.062],[47.0695,68.1645],[46.87,68.4105],[46.7075,68.498],[46.1955,68.623],[45.7375,68.703],[45.374,68.7355],[44.766,68.752],[44.188,68.735],[44.0205,68.787],[43.62,68.857],[43.4925,68.8675],[43.243,68.8655],[43.07,68.8475],[42.2115,68.726],[41.3125,68.5975],[40.5795,68.4925],[39.7595,68.3745],[39.1775,68.4725],[38.902,68.547],[38.777,68.5645],[38.1955,68.826],[37.7455,68.965],[37.6465,68.9865],[36.9705,69.1575],[36.589,69.266],[36.0895,69.378],[35.964,69.3965],[35.377,69.448],[34.653,69.5165],[34.4465,69.559],[34.2745,69.5805],[33.619,69.827],[33.461,69.8945],[33.2485,69.9425],[32.372,70.115],[32.0535,70.15],[31.727,70.1385],[31.441,70.0985],[31.1045,69.9795],[30.8175,69.795],[30.9455,69.6775],[30.939,69.5605],[30.818,69.529],[30.516,69.5405],[30.4175,69.59],[30.224,69.653],[30.138,69.6435],[30.188,69.566],[30.1195,69.5135],[30.115,69.4685],[29.9705,69.408],[29.8685,69.424],[29.7245,69.3895],[29.573,69.3185],[29.3905,69.322],[29.286,69.294],[29.313,69.2295],[29.242,69.113],[29.0565,69.015],[28.9295,69.052],[28.4955,68.93],[28.468,68.8855],[28.661,68.8865],[28.8015,68.8695],[28.707,68.7325],[28.434,68.5395],[28.646,68.1965],[29.327,68.0745],[29.6595,67.803],[30.0155,67.673],[29.9305,67.523],[29.717,67.394],[29.527,67.311],[29.5155,67.281],[29.0735,66.996],[29.033,66.9255],[29.061,66.8525],[29.1185,66.8015],[29.3505,66.644],[29.5245,66.4905],[29.7095,66.268],[29.924,66.126],[29.9975,65.979],[30.068,65.895],[30.1385,65.6685],[30.017,65.6965],[29.7225,65.637],[29.864,65.5605],[29.733,65.4725],[29.7465,65.3475],[29.602,65.26],[29.635,65.2315],[29.764,65.229],[29.8935,65.193],[29.8195,65.1445],[29.897,65.105],[29.7325,65.0915],[29.627,65.0605],[29.5995,64.995],[29.645,64.8665],[29.7395,64.79],[30.043,64.793],[30.0415,64.741],[30.136,64.649],[29.9895,64.5875],[29.987,64.534],[30.0585,64.451],[30.045,64.402],[30.11,64.365],[30.21,64.3505],[30.4825,64.2625],[30.4665,64.2045],[30.5535,64.132],[30.528,64.049],[30.447,63.983],[30.321,63.9095],[30.2605,63.822],[29.972,63.757],[30.244,63.6075],[30.3845,63.5455],[30.484,63.4665],[30.7875,63.4055],[30.85,63.3685],[30.9345,63.3555],[30.979,63.308],[31.1485,63.2615],[31.24,63.218],[31.2655,63.115],[31.463,63.0245],[31.5865,62.9085],[31.439,62.7855],[31.3455,62.6405],[31.222,62.4985],[31.1385,62.442],[30.7205,62.209],[30.6565,62.2085],[30.602,62.1415],[30.423,62.0225],[30.305,61.964],[30.1555,61.858],[30.0705,61.8175],[30.0385,61.765],[29.8185,61.6555],[29.7405,61.5735],[29.503,61.4615],[29.3345,61.355],[29.2335,61.2685],[28.9855,61.1735],[28.957,61.1515],[28.819,61.1215],[28.714,61.0445],[28.658,60.951],[28.5245,60.957],[28.1355,60.741],[27.8735,60.6045],[27.7735,60.5335],[27.747,60.451],[27.686,60.4335],[27.725,60.3915],[27.455,60.2235],[27.294,60.2005],[26.8725,60.1155],[26.44,59.9385],[26.915,59.6315],[27.857,59.582],[28.0425,59.4715],[28.191,59.4005],[28.21,59.3705],[28.1295,59.291],[27.954,59.2705],[27.9015,59.2385],[27.865,59.161],[27.8085,59.1295],[27.792,59.07],[27.745,59.027],[27.731,58.973],[27.357,58.7875],[27.553,58.366],[27.487,58.3035],[27.4915,58.2245],[27.602,58.1175],[27.6215,58.005],[27.6565,57.9555],[27.813,57.86],[27.741,57.8275],[27.6155,57.839],[27.5415,57.823],[27.5055,57.7665],[27.5215,57.716],[27.4475,57.717],[27.3765,57.668],[27.405,57.6125],[27.3305,57.5705],[27.3515,57.518],[27.5005,57.54],[27.547,57.5195],[27.5525,57.469],[27.5175,57.427],[27.6715,57.395],[27.8675,57.297],[27.817,57.1425],[27.757,57.131],[27.748,57.053],[27.7695,57.005],[27.7375,56.92],[27.686,56.9115],[27.6595,56.8345],[27.8475,56.88],[27.8985,56.838],[27.9765,56.812],[27.901,56.76],[27.981,56.707],[28.0115,56.6365],[28.062,56.586],[28.125,56.5825],[28.0975,56.511],[28.1945,56.4425],[28.1655,56.3745],[28.24,56.2715],[28.1515,56.17],[28.32,56.0595],[28.458,56.1025],[28.5485,56.116],[28.62,56.0995],[28.723,55.9785],[28.829,55.9635],[28.858,55.9885],[29.088,56.034],[29.156,56.0265],[29.214,55.989],[29.3055,55.9915],[29.426,55.9505],[29.3605,55.7985],[29.4,55.7535],[29.5155,55.7015],[29.623,55.78],[29.755,55.8005],[29.8065,55.7835],[29.895,55.8625],[29.976,55.8715],[30.107,55.8315],[30.239,55.872],[30.2865,55.8315],[30.3695,55.8085],[30.4815,55.8125],[30.5335,55.7505],[30.595,55.747],[30.648,55.71],[30.6705,55.6395],[30.734,55.6545],[30.757,55.5945],[30.8095,55.5905],[30.872,55.63],[30.9325,55.617],[30.959,55.501],[30.901,55.4735],[30.9345,55.4],[30.83,55.3345],[30.9235,55.2005],[30.996,55.1445],[30.9905,55.077],[31.028,55.0625],[30.9085,54.949],[30.8345,54.9485],[30.835,54.9075],[30.7515,54.8095],[30.9535,54.743],[31.0255,54.706],[31.012,54.6745],[31.0955,54.6615],[31.178,54.67],[31.2105,54.632],[31.093,54.5295],[31.1085,54.492],[31.236,54.464],[31.3225,54.3405],[31.3135,54.2485],[31.383,54.2325],[31.4925,54.1615],[31.6765,54.1175],[31.7745,54.113],[31.898,54.083],[31.854,54.0045],[31.892,53.9665],[31.769,53.8355],[31.867,53.7825],[32.1,53.8145],[32.208,53.7995],[32.22,53.7705],[32.3205,53.758],[32.358,53.7205],[32.4605,53.7375],[32.499,53.681],[32.426,53.6075],[32.478,53.549],[32.757,53.458],[32.723,53.374],[32.7335,53.3325],[32.6265,53.3305],[32.5735,53.298],[32.5225,53.3065],[32.4925,53.2535],[32.4145,53.186],[32.339,53.172],[32.302,53.1275],[32.2545,53.1315],[32.095,53.076],[32.018,53.091],[31.9345,53.083],[31.887,53.1145],[31.831,53.1105],[31.7865,53.1795],[31.6455,53.2195],[31.5435,53.191],[31.5005,53.2045],[31.4185,53.1875],[31.335,53.091],[31.3305,53.042],[31.3725,52.9335],[31.441,52.881],[31.5895,52.7915],[31.6005,52.746],[31.567,52.708],[31.504,52.6985],[31.5695,52.6345],[31.564,52.5945],[31.652,52.545],[31.6025,52.4235],[31.6325,52.398],[31.6145,52.3275],[31.6965,52.258],[31.711,52.215],[31.781,52.1855],[31.7815,52.112],[31.8565,52.111],[31.954,52.0815],[31.9175,52.053],[32.1255,52.046],[32.29,52.103],[32.3445,52.1825],[32.3245,52.2235],[32.3955,52.2465],[32.3515,52.3165],[32.55,52.327],[32.685,52.2665],[32.763,52.2565],[32.8335,52.279],[32.996,52.2725],[33.073,52.325],[33.208,52.376],[33.289,52.3575],[33.519,52.355],[33.5045,52.306],[33.56,52.3025],[33.6065,52.3345],[33.7855,52.3665],[33.8365,52.3615],[33.875,52.3065],[34.0545,52.1715],[34.1165,52.141],[34.0625,52.073],[34.094,52.011],[34.1895,51.969],[34.2035,51.941],[34.4155,51.826],[34.4325,51.7295],[34.1155,51.6845],[34.308,51.5155],[34.2225,51.4285],[34.2855,51.375],[34.337,51.3655],[34.2375,51.2925],[34.2445,51.2635],[34.3315,51.24],[34.384,51.274],[34.4865,51.2445],[34.6625,51.2475],[34.669,51.1975],[34.818,51.169],[34.951,51.2265],[35.1505,51.2235],[35.1235,51.165],[35.168,51.1265],[35.166,51.0775],[35.2135,51.0465],[35.305,51.076],[35.406,51.048],[35.33,50.996],[35.333,50.9345],[35.388,50.9235],[35.419,50.844],[35.4105,50.808],[35.4885,50.777],[35.4595,50.689],[35.392,50.642],[35.4255,50.603],[35.436,50.53],[35.4755,50.489],[35.5865,50.448],[35.584,50.3955],[35.6285,50.3545],[35.738,50.3545],[35.8315,50.434],[35.932,50.431],[36.068,50.451],[36.1605,50.4315],[36.284,50.3355],[36.297,50.292],[36.3665,50.288],[36.4735,50.313],[36.575,50.275],[36.5605,50.251],[36.6485,50.2175],[36.693,50.269],[36.9375,50.3505],[37.1055,50.352],[37.1815,50.363],[37.2925,50.4015],[37.333,50.437],[37.465,50.431],[37.485,50.357],[37.6265,50.2935],[37.615,50.217],[37.639,50.1795],[37.755,50.0785],[37.797,50.084],[37.9265,50.034],[37.962,49.983],[38.056,49.923],[38.171,49.942],[38.222,49.979],[38.186,50.023],[38.178,50.08],[38.3295,50.0855],[38.3505,50.007],[38.4885,49.963],[38.5825,49.9775],[38.724,49.927],[38.745,49.8975],[38.848,49.8645],[38.901,49.8695],[38.9095,49.8195],[38.9515,49.797],[39.067,49.8155],[39.18,49.889],[39.2275,49.8075],[39.285,49.7555],[39.379,49.7385],[39.4785,49.7565],[39.5915,49.7185],[39.6605,49.615],[39.7505,49.5975],[39.806,49.558],[39.891,49.5585],[39.952,49.597],[40.1355,49.617],[40.1695,49.569],[40.039,49.5205],[40.0295,49.4535],[40.1145,49.3855],[40.196,49.3445],[40.186,49.281],[40.0775,49.187],[40.031,49.18],[39.9385,49.083],[39.805,49.06],[39.7665,49.04],[39.693,49.0505],[39.6635,49.001],[39.7495,48.978],[39.7765,48.9205],[39.8525,48.8905],[40.0585,48.904],[40.08,48.87],[39.9755,48.793],[39.807,48.8385],[39.779,48.7855],[39.7255,48.7525],[39.7185,48.6875],[39.668,48.621],[39.688,48.587],[39.7875,48.5935],[39.8555,48.561],[39.853,48.4665],[39.898,48.4475],[39.946,48.352],[39.8455,48.333],[39.841,48.31],[39.9365,48.291],[39.9905,48.317],[40.021,48.254],[39.942,48.2285],[39.937,48.181],[39.8765,48.119],[39.8835,48.0415],[39.7765,48.04],[39.816,48.0],[39.7935,47.9195],[39.7385,47.828],[39.5655,47.8365],[39.521,47.8255],[39.478,47.8605],[39.412,47.831],[39.3875,47.871],[39.2415,47.867],[39.1495,47.8425],[39.083,47.8695],[38.884,47.875],[38.8375,47.8645],[38.789,47.816],[38.7725,47.6855],[38.669,47.699],[38.6165,47.6445],[38.4565,47.644],[38.457,47.617],[38.3505,47.6165],[38.2845,47.5445],[38.3025,47.393],[38.2575,47.373],[38.221,47.3055],[38.336,47.3065],[38.3245,47.257],[38.2605,47.2385],[38.236,47.1985],[38.23,47.1195],[38.338,46.981],[38.141,46.861],[37.62,46.83],[37.4375,46.716]]],[[[35.4205,80.1595],[35.4845,80.0925],[35.7815,80.0105],[36.2645,79.9505],[36.7085,79.933],[37.0955,79.942],[37.4975,79.977],[37.86,80.0555],[37.9645,80.117],[37.9135,80.2135],[37.6615,80.29],[37.275,80.3435],[36.891,80.3645],[36.3365,80.362],[35.871,80.3235],[35.558,80.258],[35.4205,80.1595]]],[[[43.6835,80.5925],[43.8105,80.5065],[44.035,80.4535],[44.7615,80.358],[45.55,80.2675],[45.8625,80.11],[46.1245,80.0395],[46.404,80.0015],[46.9855,79.8935],[47.3905,79.8555],[47.793,79.847],[48.259,79.863],[48.786,79.802],[49.3665,79.791],[49.6775,79.759],[50.0935,79.736],[50.9605,79.715],[51.4325,79.7145],[51.8835,79.7365],[52.253,79.7805],[52.5735,79.861],[52.9815,79.8195],[53.2935,79.809],[53.888,79.828],[54.231,79.872],[54.4365,79.925],[54.5395,79.9975],[54.7365,80.011],[54.87,79.9705],[55.35,79.9065],[55.806,79.826],[56.1505,79.8045],[56.4975,79.802],[57.015,79.8315],[57.22,79.86],[57.5015,79.79],[57.535,79.7235],[57.83,79.6315],[58.2335,79.5845],[58.7545,79.5725],[59.058,79.5855],[59.357,79.616],[59.698,79.692],[60.0955,79.693],[60.6575,79.737],[61.029,79.829],[61.1035,79.902],[61.0175,80.0005],[61.248,80.0485],[61.41,80.1395],[62.149,80.2105],[62.5605,80.29],[62.673,80.4],[62.9895,80.441],[63.308,80.442],[64.8945,80.5295],[65.2785,80.5685],[65.8965,80.6555],[66.356,80.771],[66.555,80.851],[66.6445,81.015],[66.5255,81.1525],[66.384,81.215],[65.9405,81.3085],[65.572,81.351],[65.0695,81.383],[64.362,81.392],[63.9725,81.376],[63.518,81.334],[63.065,81.265],[62.793,81.1905],[62.4695,81.252],[61.8925,81.302],[61.872,81.3255],[62.499,81.3325],[63.073,81.381],[63.5635,81.3785],[64.263,81.4165],[64.725,81.476],[64.9685,81.535],[65.132,81.622],[65.112,81.682],[64.8545,81.78],[64.594,81.8265],[64.0285,81.885],[63.4465,81.907],[62.565,81.903],[61.9485,81.8845],[61.4615,81.8485],[60.995,81.787],[60.712,81.7895],[60.703,81.8595],[60.4945,81.9495],[59.9405,82.029],[59.132,82.0585],[58.438,82.0495],[57.3565,81.999],[56.754,81.925],[56.545,81.864],[56.549,81.735],[56.14,81.714],[55.766,81.671],[55.444,81.577],[55.085,81.5935],[54.6435,81.592],[53.997,81.5455],[53.505,81.521],[53.0415,81.4545],[52.8745,81.394],[52.891,81.295],[53.127,81.2145],[52.9595,81.1595],[52.917,81.077],[53.134,80.9725],[52.9405,80.9265],[52.772,80.8395],[52.427,80.8925],[52.146,80.955],[52.0775,81.0125],[52.297,81.1185],[52.266,81.1815],[51.999,81.2765],[51.6115,81.333],[51.2195,81.365],[50.387,81.3795],[49.899,81.357],[49.5215,81.31],[49.2085,81.235],[49.082,81.143],[49.1105,81.0785],[48.6785,81.0065],[48.246,81.014],[47.9525,81.041],[47.341,81.0555],[46.579,81.0225],[46.2095,80.9785],[45.934,80.899],[45.5035,80.8415],[44.5815,80.801],[44.276,80.775],[43.9115,80.7165],[43.725,80.651],[43.6835,80.5925]]],[[[47.655,68.923],[47.6725,68.8025],[47.7105,68.734],[47.874,68.6235],[48.105,68.554],[48.2485,68.5285],[48.5955,68.4965],[49.1755,68.4565],[49.6325,68.4725],[49.915,68.5195],[50.079,68.5775],[50.59,68.851],[50.802,68.9975],[50.8715,69.124],[50.824,69.22],[50.737,69.293],[50.6105,69.3655],[50.4355,69.432],[50.123,69.5125],[49.683,69.649],[49.3805,69.694],[49.16,69.7015],[48.843,69.6905],[48.707,69.675],[48.394,69.618],[48.092,69.507],[47.845,69.3685],[47.7575,69.2695],[47.6785,69.119],[47.655,68.923]]],[[[74.987,79.659],[75.0345,79.593],[75.276,79.4965],[75.624,79.4115],[76.2755,79.311],[76.7475,79.284],[77.4715,79.279],[77.7485,79.285],[78.158,79.318],[78.3975,79.359],[78.5955,79.424],[78.671,79.496],[78.591,79.5785],[78.479,79.618],[78.13,79.683],[77.214,79.8035],[76.5545,79.864],[75.9045,79.8705],[75.587,79.8475],[75.187,79.7765],[75.042,79.7205],[74.987,79.659]]],[[[77.577,80.793],[77.6235,80.7225],[77.8325,80.65],[78.192,80.592],[78.4505,80.5685],[79.078,80.545],[79.827,80.55],[80.537,80.59],[80.866,80.635],[81.1545,80.694],[81.3655,80.774],[81.4265,80.843],[81.2295,80.9535],[80.921,81.0115],[80.1395,81.0815],[79.8385,81.0975],[79.3495,81.1005],[78.5375,81.0525],[78.189,81.0135],[77.759,80.9205],[77.577,80.793]]],[[[78.461,74.588],[78.51,74.504],[78.73,74.4095],[79.0335,74.3505],[79.5055,74.314],[79.808,74.3255],[80.054,74.364],[80.258,74.442],[80.363,74.5415],[80.361,74.616],[80.2155,74.7025],[79.8095,74.7985],[79.578,74.831],[79.2075,74.839],[78.8125,74.795],[78.6285,74.7415],[78.5255,74.687],[78.461,74.588]]],[[[80.811,75.312],[80.851,75.2185],[80.9995,75.126],[81.181,75.056],[81.529,74.943],[81.78,74.89],[82.0325,74.8695],[82.425,74.8825],[82.7595,74.9435],[82.8875,75.0],[82.939,75.0645],[82.9655,75.186],[83.138,75.2985],[83.131,75.3685],[83.0315,75.4505],[83.0685,75.5205],[83.01,75.586],[82.866,75.6435],[82.713,75.6765],[82.965,75.7185],[83.3035,75.7195],[83.6275,75.742],[83.856,75.7835],[84.072,75.8865],[84.0865,75.9645],[84.041,76.0155],[83.897,76.0775],[83.5365,76.138],[82.9315,76.1745],[82.584,76.1865],[81.999,76.1505],[81.776,76.1565],[81.451,76.1395],[81.2115,76.1005],[80.9555,76.02],[80.852,75.957],[80.8745,75.8555],[81.0035,75.795],[81.2065,75.7485],[81.669,75.6745],[81.4055,75.6315],[81.1515,75.5615],[80.9405,75.4795],[80.833,75.3975],[80.811,75.312]]],[[[81.3055,77.5345],[81.3755,77.435],[81.554,77.36],[81.751,77.321],[82.248,77.279],[82.7945,77.266],[83.13,77.2895],[83.3975,77.3405],[83.556,77.4095],[83.5455,77.5225],[83.2895,77.6165],[83.066,77.67],[82.7765,77.7055],[82.435,77.7245],[81.974,77.7205],[81.5455,77.6615],[81.3965,77.609],[81.3055,77.5345]]],[[[87.806,77.078],[87.8565,76.9975],[88.1385,76.8735],[88.412,76.8175],[88.6565,76.7895],[88.939,76.775],[89.405,76.79],[89.6215,76.773],[89.9115,76.77],[90.2505,76.795],[90.5505,76.8555],[90.6995,76.9315],[91.003,77.0125],[91.0915,77.0845],[91.3715,77.121],[91.529,77.161],[91.6745,77.2465],[91.6415,77.372],[91.735,77.4035],[92.2035,77.4035],[92.5305,77.436],[92.919,77.533],[93.013,77.593],[93.01,77.6625],[92.8775,77.7335],[92.515,77.8145],[92.158,77.852],[91.721,77.8545],[91.2705,77.8095],[91.0345,77.7475],[90.9195,77.658],[90.969,77.566],[90.8755,77.5185],[90.5805,77.52],[90.307,77.5035],[90.0695,77.471],[89.692,77.4995],[89.3745,77.5005],[89.1045,77.487],[88.6615,77.4225],[88.452,77.3545],[88.356,77.297],[87.989,77.2165],[87.831,77.129],[87.806,77.078]]],[[[92.6775,78.183],[92.785,78.076],[93.0615,77.999],[93.393,77.9565],[93.678,77.942],[94.1545,77.957],[94.4805,78.001],[94.6195,78.0355],[94.78,78.121],[94.772,78.2345],[94.6665,78.3135],[94.489,78.3635],[94.259,78.3985],[93.892,78.4215],[93.509,78.4145],[93.0925,78.3665],[92.8245,78.2965],[92.6775,78.183]]],[[[115.212,74.2995],[115.278,74.2135],[115.5195,74.125],[115.787,74.087],[116.139,74.079],[116.39,74.1015],[116.6395,74.147],[116.8385,74.2145],[116.895,74.254],[116.9275,74.339],[116.8215,74.441],[116.6535,74.5025],[116.287,74.5595],[116.0385,74.567],[115.749,74.5495],[115.5135,74.5045],[115.2705,74.393],[115.212,74.2995]]],[[[134.7045,74.2395],[134.7225,74.138],[134.8625,74.0355],[135.4505,73.8095],[135.762,73.717],[136.029,73.684],[136.328,73.6835],[136.5195,73.702],[136.7115,73.74],[136.919,73.8145],[137.013,73.8885],[136.962,74.041],[136.618,74.203],[136.1565,74.3535],[135.9085,74.4035],[135.4575,74.441],[135.098,74.4185],[134.8695,74.3685],[134.733,74.2985],[134.7045,74.2395]]],[[[138.897,54.639],[138.9075,54.558],[138.9745,54.486],[139.0855,54.4365],[139.222,54.417],[139.3605,54.432],[139.474,54.4765],[139.5475,54.5435],[139.569,54.622],[139.535,54.699],[139.451,54.7615],[139.357,54.794],[139.25,54.808],[139.141,54.801],[139.0415,54.7745],[138.9445,54.7155],[138.897,54.639]]],[[[140.922,46.2525],[140.947,46.15],[141.0155,46.083],[141.075,46.052],[141.189,46.0235],[141.276,46.024],[141.362,46.044],[141.463,46.088],[141.514,46.1305],[141.549,46.187],[141.5565,46.299],[141.538,46.344],[141.479,46.4065],[141.3685,46.4705],[141.223,46.493],[141.1195,46.4765],[141.054,46.452],[140.9725,46.395],[140.9395,46.348],[140.922,46.2525]]],[[[143.0465,56.413],[143.0665,56.3415],[143.1335,56.279],[143.2955,56.223],[143.3915,56.216],[143.5385,56.2355],[143.6555,56.288],[143.721,56.3635],[143.721,56.447],[143.656,56.522],[143.5385,56.5745],[143.3915,56.5935],[143.263,56.5805],[143.1525,56.542],[143.077,56.483],[143.0465,56.413]]],[[[145.6375,44.3015],[145.514,44.132],[145.32,43.9405],[145.2765,43.8395],[145.279,43.7355],[145.4475,43.6235],[145.7595,43.502],[145.8425,43.381],[146.0085,43.2255],[146.0595,43.1775],[146.1175,43.1775],[146.2925,43.2345],[146.5555,43.3965],[146.777,43.5215],[146.9745,43.5925],[147.1035,43.67],[147.162,43.73],[147.1955,43.8215],[147.1935,43.8645],[147.146,43.9565],[147.178,44.303],[147.375,44.4065],[147.4925,44.486],[147.56,44.574],[147.6445,44.615],[147.7885,44.7035],[147.9045,44.759],[148.057,44.793],[148.182,44.8615],[148.2605,44.93],[148.409,45.0115],[148.6195,45.075],[148.9715,45.158],[149.073,45.22],[149.146,45.3265],[149.408,45.3735],[149.519,45.371],[149.631,45.385],[149.812,45.4515],[149.8615,45.478],[150.018,45.594],[150.201,45.669],[150.324,45.7445],[150.418,45.816],[150.5355,45.9435],[150.6765,46.0295],[150.8225,46.102],[151.0715,46.3215],[151.151,46.409],[151.1985,46.4825],[151.199,46.584],[151.1675,46.6375],[150.9765,46.8535],[150.8485,46.921],[150.711,46.939],[150.621,46.927],[150.5275,46.8885],[150.4655,46.8395],[150.432,46.788],[150.421,46.6955],[150.457,46.623],[150.4765,46.522],[150.365,46.4275],[150.196,46.4055],[150.0645,46.357],[149.7555,46.1855],[149.6445,46.081],[149.508,46.0175],[149.4245,45.961],[149.331,45.8675],[149.201,45.7575],[148.9635,45.7295],[148.896,45.746],[148.731,45.756],[148.582,45.732],[148.464,45.6875],[148.348,45.611],[148.048,45.6185],[147.899,45.6375],[147.798,45.618],[147.6845,45.5585],[147.6195,45.4945],[147.52,45.3425],[147.375,45.2945],[147.302,45.2575],[147.241,45.1975],[147.191,45.075],[147.1395,45.0495],[146.984,45.003],[146.8675,44.9275],[146.725,44.7515],[146.5195,44.666],[146.338,44.6765],[146.2195,44.7135],[146.1245,44.715],[146.0245,44.6945],[145.9475,44.66],[145.839,44.574],[145.728,44.4385],[145.6375,44.3015]]],[[[147.618,76.6425],[147.7465,76.534],[148.001,76.472],[148.296,76.442],[148.5955,76.4365],[148.897,76.4445],[149.308,76.4325],[149.657,76.4615],[149.841,76.497],[150.0475,76.5665],[150.111,76.6145],[150.273,76.668],[150.351,76.722],[150.3535,76.79],[150.241,76.86],[150.007,76.918],[149.5675,76.955],[149.246,76.95],[148.4875,76.9075],[148.2555,76.873],[147.925,76.803],[147.687,76.7185],[147.618,76.6425]]],[[[151.6385,75.7],[151.705,75.6325],[151.9085,75.5615],[152.2785,75.514],[152.558,75.511],[152.9725,75.5555],[153.1345,75.599],[153.26,75.6715],[153.2675,75.749],[153.081,75.8485],[152.8685,75.8915],[153.11,75.9055],[153.476,75.9705],[153.6225,76.038],[153.6785,76.1565],[153.6595,76.229],[153.5325,76.3005],[153.3535,76.3455],[153.132,76.3745],[152.71,76.3845],[152.4005,76.372],[152.129,76.3405],[151.974,76.306],[151.803,76.2385],[151.7085,76.1515],[151.739,76.092],[151.913,76.0105],[152.3115,75.9155],[152.0695,75.894],[151.858,75.8525],[151.7325,75.807],[151.6385,75.7]]],[[[155.5795,77.0745],[155.7115,76.971],[155.9625,76.9105],[156.339,76.8755],[156.6225,76.875],[157.0635,76.9025],[157.3115,76.948],[157.518,77.0445],[157.53,77.0945],[157.441,77.1875],[157.274,77.244],[157.033,77.2855],[156.647,77.31],[156.4105,77.307],[156.096,77.2815],[155.889,77.247],[155.6575,77.1705],[155.5795,77.0745]]],[[[157.155,76.7845],[157.202,76.733],[157.352,76.67],[157.5445,76.6255],[157.992,76.5835],[158.3625,76.5915],[158.5465,76.61],[158.814,76.6635],[159.002,76.7595],[158.9965,76.836],[158.9115,76.889],[158.653,76.956],[158.375,76.986],[157.9275,76.996],[157.6615,76.9795],[157.437,76.946],[157.2595,76.8945],[157.155,76.7845]]],[[[165.404,55.291],[165.4535,55.1585],[165.5555,55.064],[165.8075,54.933],[165.8865,54.849],[166.1135,54.6915],[166.458,54.52],[166.594,54.484],[166.6965,54.48],[166.8555,54.5105],[166.957,54.5675],[167.0235,54.6845],[167.015,54.79],[166.988,54.898],[166.893,55.015],[166.688,55.1515],[166.6475,55.3265],[166.5985,55.4055],[166.4455,55.497],[166.3265,55.5245],[166.029,55.5625],[165.8975,55.5615],[165.7635,55.5285],[165.549,55.449],[165.45,55.3855],[165.404,55.291]]],[[[167.0735,54.891],[167.0865,54.8295],[167.1585,54.714],[167.288,54.6255],[167.5235,54.501],[167.8345,54.3615],[167.9745,54.308],[168.1105,54.2965],[168.223,54.311],[168.31,54.3395],[168.3945,54.3945],[168.4455,54.4885],[168.4255,54.571],[168.384,54.621],[168.288,54.6885],[168.184,54.793],[168.0665,54.8455],[167.96,54.9125],[167.737,55.0095],[167.6015,55.0575],[167.4735,55.0825],[167.3635,55.082],[167.2365,55.0545],[167.1375,55.001],[167.0915,54.949],[167.0735,54.891]]],[[[178.011,71.065],[178.03,70.976],[178.1605,70.846],[178.207,70.7275],[178.311,70.6635],[178.551,70.602],[178.7485,70.5865],[179.0395,70.602],[179.3035,70.6575],[179.4765,70.652],[179.721,70.6675],[180.0,70.7165],[180.0,70.976],[180.0,71.0255],[180.0,71.749],[179.649,71.7025],[179.007,71.5395],[178.7235,71.417],[178.4735,71.3635],[178.301,71.2675],[178.125,71.1895],[178.023,71.1095],[178.011,71.065]]]]},"properties":{"flag":"https://upload.wikimedia.org/wikipedia/commons/f/f3/Flag_of_Russia.svg","name:en":"Russia","wikidata":"Q159","ISO3166-1:alpha2":"RU","ISO3166-1:alpha3":"RUS","ISO3166-1:numeric":"643"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[28.214,45.467],[28.1605,45.511],[28.165,45.546],[28.0925,45.598],[28.1735,45.6475],[28.151,45.745],[28.109,45.8305],[28.133,45.8765],[28.086,46.009],[28.09,46.072],[28.13,46.1255],[28.1415,46.1855],[28.112,46.2325],[28.1365,46.28],[28.2065,46.317],[28.1975,46.37],[28.258,46.425],[28.2235,46.4955],[28.2225,46.543],[28.2515,46.621],[28.235,46.6745],[28.1865,46.725],[28.1555,46.7915],[28.1075,46.842],[28.1125,46.921],[28.0805,46.991],[27.9175,47.067],[27.801,47.138],[27.7865,47.2175],[27.7055,47.2895],[27.634,47.3025],[27.569,47.373],[27.5695,47.4655],[27.476,47.485],[27.405,47.583],[27.3335,47.6275],[27.2675,47.712],[27.2855,47.7445],[27.2165,47.8165],[27.194,47.9065],[27.152,47.921],[27.1615,47.996],[27.1235,47.993],[27.027,48.0875],[27.0205,48.1385],[26.933,48.171],[26.8085,48.2545],[26.667,48.2465],[26.63,48.2595],[26.529,48.21],[26.46,48.2145],[26.335,48.1835],[26.2725,48.082],[26.214,48.0525],[26.1845,47.9945],[25.952,47.971],[25.861,47.9705],[25.775,47.9395],[25.6255,47.949],[25.5925,47.9385],[25.311,47.9145],[25.237,47.8955],[25.2215,47.859],[25.113,47.756],[25.0405,47.7305],[24.883,47.7245],[24.836,47.7795],[24.8275,47.8205],[24.753,47.83],[24.6725,47.8635],[24.674,47.8965],[24.58,47.967],[24.502,47.9525],[24.436,47.9705],[24.345,47.9145],[24.217,47.903],[24.01,47.9665],[23.882,47.9435],[23.8195,47.983],[23.751,47.9985],[23.6655,47.9835],[23.6375,48.0025],[23.539,48.0115],[23.495,47.968],[23.295,48.0445],[23.2695,48.086],[23.1465,48.0995],[23.1055,48.066],[23.091,48.006],[22.8975,47.954],[22.8475,47.908],[22.7685,47.878],[22.68,47.788],[22.549,47.772],[22.481,47.811],[22.426,47.75],[22.2645,47.731],[22.188,47.609],[22.045,47.54],[22.0075,47.4755],[22.0355,47.4275],[22.012,47.376],[21.9385,47.373],[21.852,47.239],[21.858,47.187],[21.8175,47.173],[21.773,47.109],[21.726,47.0985],[21.6825,47.0455],[21.6795,46.998],[21.595,46.8605],[21.519,46.836],[21.484,46.765],[21.5295,46.721],[21.4165,46.6425],[21.33,46.6315],[21.321,46.583],[21.2605,46.502],[21.318,46.4505],[21.2755,46.4065],[21.2065,46.403],[21.1995,46.348],[21.168,46.298],[21.1155,46.3015],[21.066,46.243],[21.0245,46.2665],[20.922,46.262],[20.8735,46.288],[20.749,46.251],[20.715,46.167],[20.632,46.128],[20.503,46.1895],[20.459,46.143],[20.3555,46.1695],[20.2645,46.1265],[20.273,46.0985],[20.346,46.0475],[20.3485,45.9995],[20.4055,45.965],[20.4835,45.9535],[20.516,45.892],[20.6605,45.829],[20.7005,45.7505],[20.8025,45.738],[20.805,45.657],[20.766,45.6125],[20.8325,45.536],[20.769,45.5],[20.879,45.4545],[20.9605,45.367],[21.0165,45.3245],[21.0635,45.332],[21.0965,45.296],[21.18,45.3125],[21.203,45.2655],[21.483,45.192],[21.5275,45.138],[21.4785,45.1215],[21.4475,45.0575],[21.36,45.0205],[21.4065,44.9785],[21.5465,44.9305],[21.56,44.889],[21.4855,44.868],[21.3715,44.8665],[21.3585,44.8215],[21.425,44.7735],[21.537,44.7725],[21.608,44.7335],[21.642,44.66],[21.842,44.6555],[21.9955,44.632],[22.037,44.542],[22.1295,44.474],[22.184,44.482],[22.2735,44.623],[22.333,44.676],[22.371,44.6725],[22.4295,44.71],[22.4845,44.707],[22.5695,44.637],[22.6695,44.619],[22.7545,44.565],[22.699,44.5165],[22.619,44.552],[22.5645,44.5355],[22.5505,44.4825],[22.475,44.4805],[22.5235,44.3405],[22.558,44.309],[22.6835,44.2715],[22.6745,44.216],[22.7545,44.194],[22.7915,44.164],[22.922,44.1],[23.001,44.095],[23.0485,44.0625],[23.0035,44.0105],[22.8795,43.981],[22.841,43.882],[22.881,43.832],[23.0465,43.7965],[23.1405,43.8055],[23.268,43.846],[23.433,43.8495],[23.5115,43.838],[23.6255,43.7915],[23.705,43.806],[23.8725,43.753],[23.967,43.7445],[24.0865,43.702],[24.1745,43.682],[24.3495,43.698],[24.407,43.7365],[24.501,43.7625],[24.623,43.742],[24.746,43.6845],[24.8015,43.709],[25.007,43.7245],[25.115,43.6845],[25.1895,43.697],[25.252,43.683],[25.3435,43.6305],[25.391,43.6185],[25.5085,43.647],[25.5655,43.646],[25.6805,43.691],[25.7855,43.7135],[25.8525,43.759],[25.9525,43.8595],[26.06,43.91],[26.106,43.968],[26.198,43.9855],[26.3075,44.027],[26.4345,44.0355],[26.5535,44.058],[26.601,44.0525],[26.774,44.0805],[26.9015,44.1325],[27.042,44.1445],[27.135,44.1405],[27.229,44.1155],[27.2915,44.075],[27.356,44.0585],[27.3995,44.012],[27.469,44.0225],[27.612,44.012],[27.6735,44.03],[27.71,43.9595],[27.843,43.966],[27.917,44.0085],[27.944,43.9845],[27.9945,43.843],[28.236,43.7585],[28.3485,43.752],[28.4465,43.7335],[28.579,43.7385],[28.8555,43.7385],[28.941,43.936],[28.9725,44.075],[29.1315,44.4375],[29.224,44.5415],[29.5735,44.5805],[29.6715,44.605],[29.7285,44.6355],[29.851,44.7265],[29.882,44.7695],[30.0455,45.0865],[29.9955,45.1145],[29.952,45.1695],[29.6905,45.1915],[29.651,45.3395],[29.589,45.39],[29.4295,45.4425],[29.294,45.428],[29.238,45.4325],[29.179,45.4],[29.044,45.3605],[28.9145,45.2875],[28.8165,45.3365],[28.7925,45.2435],[28.714,45.2235],[28.569,45.248],[28.349,45.3205],[28.2855,45.3995],[28.2865,45.4335],[28.214,45.467]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/73/Flag_of_Romania.svg","name:en":"Romania","wikidata":"Q218","ISO3166-1:alpha2":"RO","ISO3166-1:alpha3":"ROU","ISO3166-1:numeric":"642"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[26.63,48.2595],[26.667,48.2465],[26.8085,48.2545],[26.933,48.171],[27.0205,48.1385],[27.027,48.0875],[27.1235,47.993],[27.1615,47.996],[27.152,47.921],[27.194,47.9065],[27.2165,47.8165],[27.2855,47.7445],[27.2675,47.712],[27.3335,47.6275],[27.405,47.583],[27.476,47.485],[27.5695,47.4655],[27.569,47.373],[27.634,47.3025],[27.7055,47.2895],[27.7865,47.2175],[27.801,47.138],[27.9175,47.067],[28.0805,46.991],[28.1125,46.921],[28.1075,46.842],[28.1555,46.7915],[28.1865,46.725],[28.235,46.6745],[28.2515,46.621],[28.2225,46.543],[28.2235,46.4955],[28.258,46.425],[28.1975,46.37],[28.2065,46.317],[28.1365,46.28],[28.112,46.2325],[28.1415,46.1855],[28.13,46.1255],[28.09,46.072],[28.086,46.009],[28.133,45.8765],[28.109,45.8305],[28.151,45.745],[28.1735,45.6475],[28.0925,45.598],[28.165,45.546],[28.1605,45.511],[28.214,45.467],[28.305,45.547],[28.421,45.513],[28.515,45.5],[28.491,45.571],[28.544,45.58],[28.516,45.6655],[28.4805,45.678],[28.5195,45.736],[28.5895,45.729],[28.6245,45.766],[28.7095,45.78],[28.697,45.818],[28.786,45.8325],[28.755,45.9275],[28.7765,45.9705],[28.979,46.0035],[29.0055,46.0495],[28.9505,46.0935],[29.067,46.1955],[28.9525,46.2595],[28.9865,46.343],[28.931,46.4575],[29.0255,46.4625],[29.0385,46.506],[29.164,46.5155],[29.162,46.5445],[29.236,46.5585],[29.2295,46.4585],[29.2465,46.416],[29.2965,46.417],[29.308,46.465],[29.387,46.4465],[29.4485,46.4985],[29.502,46.4615],[29.507,46.423],[29.566,46.4165],[29.5805,46.361],[29.6775,46.361],[29.686,46.426],[29.7765,46.4545],[29.805,46.3855],[29.8865,46.3715],[29.9385,46.4005],[30.0915,46.383],[30.11,46.4315],[30.0235,46.442],[29.9925,46.501],[29.9265,46.502],[29.9685,46.595],[29.946,46.6485],[29.971,46.685],[29.9755,46.7555],[29.906,46.8205],[29.884,46.886],[29.7495,46.861],[29.731,46.9195],[29.648,46.9195],[29.596,46.9635],[29.612,47.1],[29.58,47.136],[29.552,47.2515],[29.5985,47.2575],[29.574,47.3705],[29.4875,47.356],[29.4855,47.306],[29.3985,47.3005],[29.3835,47.345],[29.3375,47.3705],[29.3175,47.449],[29.2425,47.462],[29.182,47.449],[29.117,47.5545],[29.2175,47.6125],[29.231,47.684],[29.2045,47.719],[29.2485,47.7515],[29.2195,47.7985],[29.1755,47.994],[29.093,47.983],[29.0865,47.9465],[29.026,47.9455],[28.9615,47.979],[28.926,47.96],[28.8395,48.035],[28.8555,48.108],[28.8335,48.129],[28.6775,48.143],[28.5445,48.169],[28.4635,48.074],[28.383,48.175],[28.361,48.2415],[28.303,48.2415],[28.2315,48.21],[28.1825,48.2555],[28.0825,48.2375],[28.0935,48.2935],[28.042,48.326],[27.964,48.325],[27.882,48.373],[27.869,48.407],[27.7475,48.458],[27.6435,48.442],[27.581,48.491],[27.465,48.4475],[27.447,48.409],[27.3825,48.411],[27.3195,48.4435],[27.2865,48.3715],[27.1925,48.395],[27.124,48.379],[27.087,48.413],[26.9955,48.3595],[26.8945,48.3835],[26.8515,48.413],[26.775,48.418],[26.765,48.3535],[26.824,48.3445],[26.7945,48.291],[26.696,48.327],[26.6485,48.305],[26.63,48.2595]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/27/Flag_of_Moldova.svg","name:en":"Moldova","wikidata":"Q217","ISO3166-1:alpha2":"MD","ISO3166-1:alpha3":"MDA","ISO3166-1:numeric":"498"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[23.515,53.9565],[23.6305,53.928],[23.7545,53.9275],[23.908,53.967],[23.983,53.924],[24.103,53.9405],[24.2015,53.97],[24.352,53.897],[24.448,53.904],[24.5075,53.9605],[24.626,54.0105],[24.7265,53.967],[24.839,54.021],[24.779,54.1],[24.866,54.147],[24.953,54.17],[25.016,54.1375],[25.0945,54.1505],[25.2025,54.221],[25.215,54.256],[25.3535,54.264],[25.513,54.3085],[25.5845,54.2345],[25.5155,54.1755],[25.65,54.1285],[25.7105,54.169],[25.781,54.1595],[25.7855,54.2335],[25.677,54.324],[25.5525,54.315],[25.562,54.366],[25.629,54.417],[25.626,54.4565],[25.6825,54.5365],[25.7625,54.577],[25.734,54.677],[25.7495,54.7645],[25.734,54.8025],[25.853,54.8965],[25.858,54.9275],[25.9555,54.951],[26.081,54.9545],[26.121,54.9895],[26.186,54.9915],[26.271,55.0775],[26.2295,55.1075],[26.3575,55.1505],[26.461,55.1285],[26.6765,55.159],[26.675,55.1975],[26.7315,55.245],[26.8355,55.2855],[26.792,55.321],[26.6685,55.3385],[26.6025,55.321],[26.4415,55.3475],[26.554,55.389],[26.557,55.4635],[26.524,55.51],[26.628,55.5725],[26.6305,55.6805],[26.531,55.677],[26.443,55.709],[26.3755,55.7095],[26.35,55.7425],[26.2685,55.7675],[26.2035,55.859],[26.1205,55.8795],[26.0455,55.945],[25.974,55.9815],[25.8605,55.9995],[25.82,56.055],[25.6885,56.0865],[25.6505,56.135],[25.48,56.143],[25.404,56.158],[25.2795,56.1585],[25.092,56.186],[25.056,56.266],[24.973,56.3105],[24.969,56.366],[24.9025,56.418],[24.699,56.3975],[24.587,56.3315],[24.5645,56.289],[24.454,56.258],[24.342,56.295],[24.1215,56.249],[24.019,56.33],[23.795,56.339],[23.7805,56.3675],[23.63,56.36],[23.545,56.332],[23.439,56.3515],[23.388,56.379],[23.177,56.3685],[23.184,56.3375],[23.0935,56.305],[23.027,56.331],[23.0025,56.4065],[22.8405,56.3655],[22.6165,56.3825],[22.423,56.41],[22.3455,56.394],[22.241,56.4],[22.1255,56.4325],[21.9945,56.4135],[21.9195,56.3685],[21.8355,56.3715],[21.722,56.3135],[21.618,56.3245],[21.5025,56.2955],[21.4525,56.2485],[21.3225,56.215],[21.2005,56.0765],[20.7095,56.0455],[20.6895,55.9155],[20.722,55.725],[20.754,55.6355],[20.7485,55.565],[20.7055,55.4555],[20.6555,55.384],[20.9545,55.281],[21.0985,55.2565],[21.271,55.245],[21.3845,55.2935],[21.4495,55.2215],[21.5075,55.1855],[21.5725,55.198],[21.649,55.181],[21.758,55.124],[21.9665,55.074],[22.101,55.024],[22.1575,55.0555],[22.287,55.0645],[22.466,55.0445],[22.589,55.07],[22.6035,55.0135],[22.726,54.9635],[22.855,54.888],[22.8735,54.789],[22.7415,54.7285],[22.743,54.627],[22.6865,54.591],[22.6805,54.533],[22.7035,54.453],[22.7365,54.443],[22.792,54.3635],[22.8365,54.4065],[22.984,54.3895],[23.092,54.2985],[23.2025,54.2885],[23.234,54.261],[23.3375,54.2515],[23.4245,54.1775],[23.486,54.153],[23.5285,54.066],[23.482,54.0],[23.515,53.9565]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/11/Flag_of_Lithuania.svg","name:en":"Lithuania","wikidata":"Q37","ISO3166-1:alpha2":"LT","ISO3166-1:alpha3":"LTU","ISO3166-1:numeric":"440"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[99.4585,6.3065],[99.5175,6.1995],[99.5365,6.0965],[99.58,6.0315],[99.645,5.9885],[99.796,5.9555],[99.856,5.9675],[99.893,5.9125],[99.958,5.869],[100.035,5.854],[100.101,5.867],[100.0795,5.77],[100.099,5.659],[100.0335,5.6155],[99.99,5.551],[99.975,5.4745],[99.99,5.354],[99.976,5.283],[99.9915,5.2065],[100.035,5.1415],[100.1,5.0985],[100.1595,5.086],[100.175,5.0095],[100.2395,4.865],[100.342,4.725],[100.3805,4.6995],[100.392,4.6405],[100.3815,4.5605],[100.3895,4.497],[100.373,4.3865],[100.35,4.3425],[100.328,4.2285],[100.349,4.1255],[100.327,3.998],[100.342,3.9215],[100.3855,3.8565],[100.451,3.813],[100.5335,3.799],[100.5565,3.765],[100.6265,3.7145],[100.671,3.64],[100.7525,3.5935],[100.7925,3.534],[100.9015,3.463],[100.912,3.41],[100.998,3.295],[101.0035,3.2665],[101.0805,3.177],[101.0185,3.081],[101.001,2.946],[101.0395,2.8305],[101.2015,2.6915],[101.775,2.2565],[102.2235,1.92],[102.5835,1.6865],[103.065,1.325],[103.433,1.2365],[103.5665,1.1955],[103.5765,1.2545],[103.603,1.264],[103.618,1.3215],[103.674,1.428],[103.714,1.4575],[103.7605,1.4485],[103.8125,1.4785],[103.898,1.428],[104.003,1.4205],[104.041,1.444],[104.0765,1.431],[104.0795,1.3575],[104.125,1.276],[104.189,1.2725],[104.3155,1.2835],[104.376,1.252],[104.416,1.256],[104.4725,1.2955],[104.5215,1.357],[104.523,1.3935],[104.389,1.317],[104.349,1.333],[104.3785,1.4105],[104.4635,1.4995],[104.508,1.5015],[104.4245,1.747],[104.2715,2.0995],[104.322,2.1625],[104.351,2.251],[104.3885,2.278],[104.491,2.232],[104.5525,2.227],[104.644,2.2635],[104.721,2.3475],[104.744,2.397],[104.7505,2.4685],[104.7285,2.519],[104.663,2.607],[104.6285,2.638],[104.5185,2.679],[104.477,2.735],[104.4005,2.7885],[104.4015,2.861],[104.38,2.9265],[104.311,3.04],[104.2515,3.0815],[104.107,3.1125],[104.049,3.104],[103.9775,3.0725],[103.9245,3.015],[103.8955,2.902],[103.808,2.886],[103.7325,2.8515],[103.647,2.9325],[103.635,2.9795],[103.644,3.205],[103.6295,3.343],[103.676,3.46],[103.6705,3.5825],[103.5525,3.7345],[103.5735,3.819],[103.627,3.906],[103.6385,3.9585],[103.6135,4.081],[103.6855,4.2705],[103.6875,4.37],[103.6665,4.429],[103.6775,4.5405],[103.6575,4.5915],[103.705,4.594],[103.8345,4.6565],[103.8935,4.787],[103.8785,4.8765],[103.8165,4.9685],[103.729,5.009],[103.6295,5.007],[103.565,4.978],[103.47,5.141],[103.469,5.261],[103.4345,5.336],[103.399,5.385],[103.3235,5.4255],[103.287,5.4735],[103.325,5.5005],[103.365,5.611],[103.336,5.7265],[103.1965,5.922],[103.1105,5.9875],[103.0045,6.018],[102.9435,6.009],[102.9015,6.065],[102.856,6.0985],[102.7535,6.151],[102.6755,6.1645],[102.582,6.152],[102.538,6.2345],[102.477,6.317],[102.444,6.343],[102.1665,6.4585],[102.0805,6.2255],[102.0875,6.141],[102.057,6.0855],[102.0035,6.0505],[101.9425,5.981],[101.9305,5.9095],[101.944,5.8725],[101.88,5.832],[101.7935,5.7505],[101.7535,5.797],[101.691,5.756],[101.658,5.817],[101.6625,5.8685],[101.5815,5.9345],[101.5405,5.919],[101.4845,5.87],[101.397,5.8725],[101.343,5.8095],[101.276,5.8115],[101.2495,5.7625],[101.26,5.7115],[101.215,5.6615],[101.136,5.613],[101.111,5.681],[101.06,5.7445],[101.034,5.738],[100.985,5.81],[101.037,5.918],[101.087,5.911],[101.107,5.9905],[101.1005,6.0495],[101.119,6.1145],[101.0585,6.1415],[101.0755,6.171],[101.1265,6.1925],[101.0945,6.2605],[101.015,6.247],[100.984,6.279],[100.904,6.234],[100.835,6.2965],[100.8125,6.442],[100.7395,6.492],[100.6625,6.451],[100.618,6.46],[100.564,6.495],[100.5255,6.4875],[100.496,6.5215],[100.3665,6.5395],[100.316,6.604],[100.326,6.6595],[100.29,6.689],[100.1715,6.694],[100.1705,6.6075],[100.154,6.5575],[100.159,6.4805],[100.1335,6.429],[100.076,6.406],[100.014,6.442],[99.917,6.5235],[99.812,6.5125],[99.689,6.4715],[99.5565,6.5035],[99.5115,6.4815],[99.4585,6.3065]]],[[[99.8995,3.9765],[99.915,3.9],[99.9585,3.835],[100.0235,3.7915],[100.1005,3.7765],[100.177,3.7915],[100.242,3.835],[100.2855,3.9],[100.301,3.9765],[100.2855,4.0535],[100.242,4.1185],[100.177,4.1615],[100.1005,4.177],[100.0235,4.1615],[99.9585,4.1185],[99.915,4.0535],[99.8995,3.9765]]],[[[116.8375,7.6665],[116.871,7.65],[117.0465,7.4805],[116.7925,7.4165],[116.559,7.1585],[116.3645,6.804],[116.1195,6.4805],[115.897,6.2115],[115.8535,5.9175],[115.6445,5.835],[115.555,5.76],[115.435,5.5785],[115.216,5.46],[115.115,5.387],[115.0605,5.264],[115.1585,5.025],[115.1635,4.988],[115.1425,4.9155],[115.1505,4.872],[115.2375,4.796],[115.248,4.7015],[115.289,4.604],[115.268,4.5275],[115.2725,4.4475],[115.3495,4.352],[115.3225,4.2965],[115.296,4.338],[115.246,4.341],[115.1555,4.383],[115.1125,4.3725],[115.099,4.4685],[115.0225,4.75],[115.027,4.807],[115.0495,4.8595],[115.052,4.8795],[115.025,4.8965],[114.9875,4.871],[114.9715,4.808],[114.8955,4.8155],[114.8585,4.7975],[114.775,4.696],[114.8025,4.6755],[114.8185,4.4765],[114.8295,4.4295],[114.8775,4.4265],[114.88,4.3705],[114.8575,4.2695],[114.794,4.1635],[114.8075,4.1495],[114.7185,4.0435],[114.649,4.006],[114.553,4.084],[114.5455,4.125],[114.496,4.1465],[114.454,4.242],[114.382,4.265],[114.3165,4.2635],[114.333,4.349],[114.307,4.366],[114.307,4.4185],[114.26,4.507],[114.162,4.5735],[114.077,4.5835],[114.084,4.645],[113.833,4.659],[113.5655,4.547],[112.7205,3.461],[111.1365,3.112],[110.7625,1.9435],[109.718,2.055],[109.645,2.0815],[109.6205,1.983],[109.56,1.971],[109.54,1.9265],[109.5515,1.859],[109.576,1.844],[109.5865,1.792],[109.623,1.8035],[109.6855,1.782],[109.6675,1.733],[109.662,1.6175],[109.7805,1.509],[109.8015,1.4695],[109.838,1.4755],[109.8375,1.422],[109.9305,1.425],[109.9635,1.403],[109.9795,1.299],[110.064,1.263],[110.097,1.199],[110.165,1.196],[110.2125,1.151],[110.2105,1.1185],[110.2795,1.0525],[110.2985,0.9955],[110.3965,0.9975],[110.4055,0.953],[110.4905,0.8765],[110.5955,0.8575],[110.6975,0.884],[110.762,0.9235],[110.8855,0.978],[110.907,1.029],[110.998,1.0295],[111.0695,1.0495],[111.1385,1.0505],[111.2315,1.086],[111.385,1.015],[111.445,1.014],[111.4885,1.0355],[111.551,0.9565],[111.596,1.009],[111.6675,1.043],[111.7175,1.007],[111.772,1.016],[111.818,0.9865],[111.8595,1.004],[111.887,1.0785],[111.9355,1.122],[112.057,1.1355],[112.143,1.135],[112.181,1.306],[112.2025,1.314],[112.2325,1.379],[112.212,1.4495],[112.2875,1.4735],[112.445,1.5395],[112.501,1.5835],[112.544,1.574],[112.6575,1.5715],[112.677,1.5545],[112.7745,1.564],[112.812,1.5405],[112.8845,1.5865],[112.9335,1.5675],[112.9895,1.579],[113.064,1.5525],[113.0315,1.4915],[112.986,1.4525],[113.015,1.405],[113.097,1.443],[113.1335,1.4015],[113.183,1.377],[113.241,1.3925],[113.356,1.357],[113.37,1.3235],[113.4275,1.286],[113.5375,1.3225],[113.5745,1.3065],[113.5965,1.264],[113.6645,1.2245],[113.7035,1.2655],[113.8035,1.3005],[113.8295,1.3345],[113.8145,1.3665],[113.8675,1.3875],[113.9745,1.45],[114.1425,1.466],[114.213,1.4105],[114.2495,1.451],[114.416,1.511],[114.5265,1.442],[114.586,1.4465],[114.611,1.501],[114.615,1.575],[114.649,1.5895],[114.711,1.6715],[114.718,1.728],[114.6945,1.8105],[114.7435,1.8695],[114.786,1.8485],[114.815,1.889],[114.8785,1.9145],[114.8485,1.972],[114.8805,2.0175],[114.8565,2.0455],[114.8065,2.0245],[114.792,2.06],[114.8105,2.1005],[114.78,2.1445],[114.7995,2.2495],[114.864,2.272],[114.906,2.257],[114.9485,2.3115],[114.9505,2.3515],[115.0215,2.368],[115.047,2.408],[115.095,2.411],[115.1395,2.4775],[115.1935,2.4715],[115.2375,2.506],[115.172,2.6055],[115.128,2.5825],[115.088,2.603],[115.1165,2.647],[115.093,2.694],[115.141,2.7445],[115.1425,2.8025],[115.114,2.8335],[115.151,2.872],[115.15,2.909],[115.2095,2.9555],[115.249,2.9665],[115.2855,3.0495],[115.335,2.9735],[115.3975,2.979],[115.429,3.017],[115.4835,3.0195],[115.5195,3.0585],[115.514,3.093],[115.564,3.171],[115.525,3.188],[115.516,3.261],[115.538,3.362],[115.574,3.422],[115.6345,3.4555],[115.6145,3.5445],[115.5765,3.6095],[115.5795,3.7475],[115.6185,3.8425],[115.582,3.888],[115.581,3.9415],[115.616,3.938],[115.65,3.9885],[115.6775,4.0905],[115.6755,4.127],[115.7075,4.1995],[115.731,4.1945],[115.766,4.2505],[115.8295,4.241],[115.864,4.3225],[115.878,4.391],[115.929,4.355],[116.006,4.348],[116.039,4.295],[116.079,4.2765],[116.1195,4.3385],[116.162,4.342],[116.1805,4.3825],[116.2595,4.364],[116.3485,4.3915],[116.4325,4.3255],[116.441,4.2885],[116.5025,4.3245],[116.5355,4.323],[116.536,4.3755],[116.612,4.3765],[116.6195,4.336],[116.782,4.3575],[116.84,4.327],[116.899,4.367],[116.9735,4.345],[117.044,4.3465],[117.1055,4.3335],[117.1515,4.354],[117.1965,4.336],[117.2425,4.374],[117.289,4.316],[117.342,4.291],[117.426,4.233],[117.4415,4.1935],[117.5295,4.1615],[117.5625,4.179],[117.642,4.1655],[117.899,4.166],[117.9585,4.1905],[118.265,4.097],[118.9615,4.093],[118.8375,4.4525],[119.0,4.443],[119.0,4.7],[119.3335,5.025],[119.5835,5.2665],[118.833,6.0],[118.3335,6.0],[117.9665,6.2835],[117.967,6.867],[117.425,7.4125],[117.0,7.6665],[116.8375,7.6665]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/66/Flag_of_Malaysia.svg","name:en":"Malaysia","wikidata":"Q833","ISO3166-1:alpha2":"MY","ISO3166-1:alpha3":"MYS","ISO3166-1:numeric":"458"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[100.0845,20.3525],[100.1235,20.4055],[100.1185,20.4335],[100.1755,20.6355],[100.2235,20.714],[100.2965,20.78],[100.3615,20.825],[100.4235,20.8325],[100.494,20.815],[100.608,20.841],[100.637,20.898],[100.544,20.8705],[100.5095,20.888],[100.546,21.0245],[100.596,21.029],[100.6405,21.0665],[100.642,21.0955],[100.696,21.1735],[100.7005,21.222],[100.732,21.316],[100.8105,21.2935],[100.8475,21.3045],[100.8825,21.3465],[100.949,21.3795],[100.996,21.3855],[101.059,21.455],[101.0845,21.459],[101.1635,21.5275],[101.1475,21.562],[101.1695,21.5955],[101.1525,21.6705],[101.115,21.6935],[101.11,21.751],[101.0875,21.776],[101.014,21.71],[100.8865,21.688],[100.801,21.604],[100.7225,21.5125],[100.6855,21.505],[100.572,21.451],[100.538,21.471],[100.4795,21.459],[100.431,21.5415],[100.3455,21.5285],[100.2915,21.4795],[100.247,21.4665],[100.205,21.513],[100.1685,21.486],[100.126,21.5115],[100.1115,21.603],[100.169,21.6665],[100.1185,21.706],[100.0725,21.694],[99.991,21.7065],[99.9435,21.825],[99.997,21.977],[99.9625,22.0525],[99.904,22.0695],[99.862,22.0595],[99.8545,22.026],[99.756,22.0735],[99.67,22.077],[99.642,22.106],[99.5155,22.104],[99.4735,22.136],[99.4085,22.0995],[99.2725,22.101],[99.1675,22.1495],[99.174,22.189],[99.2355,22.2505],[99.233,22.297],[99.278,22.362],[99.296,22.4145],[99.381,22.5045],[99.358,22.537],[99.385,22.5755],[99.3605,22.664],[99.3145,22.7395],[99.3855,22.765],[99.3995,22.8285],[99.4515,22.861],[99.441,22.9425],[99.496,22.912],[99.5605,22.909],[99.56,22.9515],[99.5225,22.9905],[99.522,23.077],[99.384,23.102],[99.3445,23.1375],[99.249,23.08],[99.146,23.109],[99.103,23.0915],[99.0475,23.123],[99.058,23.165],[99.0025,23.163],[98.9225,23.1885],[98.8855,23.183],[98.931,23.269],[98.9385,23.314],[98.9195,23.355],[98.921,23.4205],[98.8755,23.4865],[98.829,23.4785],[98.8035,23.542],[98.891,23.6175],[98.8515,23.6345],[98.83,23.7285],[98.789,23.784],[98.698,23.787],[98.704,23.8445],[98.6785,23.913],[98.6785,23.9575],[98.754,23.983],[98.866,24.069],[98.897,24.1075],[98.887,24.143],[98.758,24.1275],[98.7165,24.1305],[98.679,24.102],[98.6175,24.091],[98.543,24.1315],[98.4175,24.122],[98.3595,24.099],[98.2225,24.117],[98.1975,24.102],[98.109,24.095],[98.0395,24.0735],[97.985,24.035],[97.901,24.0175],[97.84,23.9635],[97.7685,23.936],[97.7035,23.867],[97.631,23.8815],[97.528,23.9295],[97.5725,23.986],[97.6285,24.008],[97.632,24.038],[97.7285,24.1185],[97.7525,24.172],[97.7295,24.23],[97.768,24.263],[97.74,24.291],[97.6665,24.3],[97.661,24.339],[97.7135,24.3775],[97.6755,24.409],[97.6645,24.453],[97.532,24.4375],[97.556,24.4965],[97.571,24.6035],[97.568,24.7235],[97.547,24.742],[97.6515,24.7955],[97.707,24.8385],[97.772,24.831],[97.797,24.8515],[97.73,24.915],[97.72,25.0215],[97.779,25.126],[97.8395,25.2725],[97.8755,25.2615],[97.904,25.2185],[97.939,25.217],[98.0065,25.2805],[98.0705,25.315],[98.1365,25.386],[98.157,25.459],[98.155,25.5275],[98.205,25.62],[98.2645,25.601],[98.3025,25.552],[98.4095,25.611],[98.4175,25.673],[98.4605,25.6915],[98.475,25.783],[98.5315,25.8465],[98.6325,25.8],[98.708,25.86],[98.6815,25.9415],[98.619,25.975],[98.6035,26.0535],[98.573,26.122],[98.6305,26.151],[98.667,26.122],[98.7095,26.121],[98.736,26.19],[98.7115,26.2365],[98.672,26.2455],[98.682,26.31],[98.734,26.3545],[98.757,26.501],[98.754,26.5655],[98.782,26.606],[98.743,26.713],[98.7625,26.802],[98.731,26.8545],[98.758,26.8855],[98.7335,27.006],[98.7665,27.052],[98.712,27.078],[98.7135,27.143],[98.6915,27.2],[98.7295,27.25],[98.741,27.332],[98.7075,27.3645],[98.6845,27.4885],[98.7045,27.522],[98.6745,27.586],[98.647,27.5685],[98.5865,27.593],[98.5645,27.6405],[98.475,27.6375],[98.43,27.6585],[98.4305,27.5535],[98.3765,27.511],[98.317,27.523],[98.311,27.5865],[98.284,27.6565],[98.2345,27.6925],[98.227,27.7965],[98.1705,27.854],[98.206,27.892],[98.184,27.9435],[98.132,27.986],[98.1605,28.0895],[98.1385,28.1465],[98.097,28.167],[98.0765,28.2065],[98.0115,28.208],[98.0205,28.258],[97.909,28.3685],[97.8705,28.3675],[97.842,28.3285],[97.797,28.3295],[97.7785,28.3705],[97.7355,28.3985],[97.7375,28.466],[97.6635,28.5385],[97.612,28.5185],[97.566,28.5465],[97.502,28.495],[97.5235,28.447],[97.478,28.3795],[97.504,28.315],[97.425,28.298],[97.3955,28.2415],[97.346,28.2145],[97.361,28.1655],[97.3445,28.117],[97.3585,28.063],[97.3945,28.0185],[97.3675,27.977],[97.379,27.892],[97.36,27.873],[97.293,27.9135],[97.255,27.894],[97.1125,27.7695],[97.026,27.7355],[96.995,27.671],[96.9,27.6085],[96.9335,27.508],[96.9145,27.461],[97.002,27.3455],[97.088,27.245],[97.1015,27.2145],[97.1765,27.14],[97.1455,27.0925],[97.076,27.0955],[97.0105,27.145],[96.89,27.1765],[96.858,27.215],[96.855,27.267],[96.8295,27.312],[96.7765,27.356],[96.7155,27.376],[96.6765,27.3355],[96.606,27.363],[96.582,27.314],[96.5265,27.289],[96.4335,27.305],[96.4095,27.292],[96.314,27.2935],[96.091,27.2235],[96.022,27.18],[96.0075,27.14],[95.946,27.052],[95.831,27.008],[95.804,27.0155],[95.7555,26.951],[95.7555,26.91],[95.7135,26.883],[95.658,26.8915],[95.6095,26.814],[95.546,26.8295],[95.5035,26.8065],[95.485,26.7485],[95.4395,26.7025],[95.315,26.6645],[95.208,26.6475],[95.149,26.6155],[95.154,26.583],[95.1085,26.543],[95.114,26.518],[95.073,26.4725],[95.1325,26.3775],[95.1225,26.3065],[95.12,26.0995],[95.185,26.075],[95.1565,26.0195],[95.081,25.947],[95.029,25.935],[95.02,25.872],[95.0515,25.798],[95.038,25.74],[94.9405,25.672],[94.918,25.6145],[94.845,25.5605],[94.808,25.4955],[94.6865,25.466],[94.5855,25.268],[94.5775,25.216],[94.6035,25.1845],[94.7255,25.134],[94.745,25.063],[94.737,25.001],[94.697,24.961],[94.7135,24.93],[94.6845,24.882],[94.633,24.835],[94.6295,24.754],[94.607,24.711],[94.5465,24.707],[94.541,24.6425],[94.5095,24.5925],[94.4555,24.5695],[94.4085,24.44],[94.2605,24.164],[94.2555,24.081],[94.238,24.033],[94.169,23.9275],[94.156,23.847],[94.117,23.838],[94.095,23.8855],[94.0465,23.8925],[94.022,23.926],[93.9735,23.9235],[93.894,23.9515],[93.815,23.924],[93.7565,24.006],[93.7225,23.999],[93.6265,24.0115],[93.5945,23.962],[93.564,23.9785],[93.51,23.9465],[93.4655,23.972],[93.3985,24.0855],[93.344,24.092],[93.3255,24.0465],[93.352,23.947],[93.3925,23.924],[93.385,23.8765],[93.3945,23.754],[93.436,23.6875],[93.4175,23.6365],[93.4185,23.5415],[93.3975,23.5105],[93.388,23.4205],[93.4025,23.3895],[93.3555,23.3535],[93.3875,23.216],[93.3645,23.121],[93.32,23.029],[93.2945,23.008],[93.235,23.011],[93.2105,23.0475],[93.14,23.054],[93.123,23.0075],[93.146,22.9275],[93.0955,22.807],[93.1065,22.746],[93.092,22.7095],[93.108,22.6355],[93.141,22.594],[93.113,22.5595],[93.1315,22.468],[93.1855,22.428],[93.1855,22.339],[93.202,22.2645],[93.142,22.2445],[93.1575,22.187],[93.0715,22.2115],[93.0385,22.1955],[93.042,22.1365],[92.9895,22.061],[92.9225,21.994],[92.8655,22.0255],[92.8545,22.0595],[92.806,22.104],[92.7185,22.1595],[92.678,22.0995],[92.6795,22.027],[92.601,21.982],[92.6215,21.8755],[92.5995,21.7945],[92.5985,21.66],[92.618,21.4695],[92.643,21.408],[92.674,21.2835],[92.5945,21.2495],[92.564,21.372],[92.5175,21.3885],[92.475,21.3545],[92.4275,21.381],[92.425,21.433],[92.381,21.4795],[92.3425,21.4665],[92.3245,21.4195],[92.27,21.4305],[92.2585,21.363],[92.1975,21.328],[92.218,21.2355],[92.176,21.16],[92.2585,21.0775],[92.2535,21.021],[92.272,20.947],[92.34,20.819],[92.3715,20.7025],[92.343,20.649],[92.363,20.5895],[92.4285,20.565],[92.4245,20.4815],[92.3985,20.385],[92.424,20.365],[92.361,20.308],[92.3295,20.236],[92.3295,20.158],[92.361,20.086],[92.402,20.045],[92.861,19.6805],[93.0755,19.3055],[93.236,18.806],[93.283,18.7285],[93.7605,18.238],[94.1125,17.441],[94.188,16.674],[94.151,16.645],[94.0935,16.559],[94.0925,16.4885],[94.065,16.3335],[94.029,16.1985],[94.0105,16.058],[94.037,15.966],[94.0935,15.874],[94.159,15.841],[94.264,15.8375],[94.286,15.7565],[94.323,15.69],[94.4,15.6825],[94.4865,15.719],[94.528,15.7655],[94.7675,15.626],[94.915,15.565],[95.0475,15.5705],[95.195,15.5905],[95.287,15.567],[95.445,15.569],[95.5785,15.6335],[95.679,15.7125],[95.8275,15.8865],[95.886,16.0295],[95.998,16.1025],[96.0915,16.1335],[96.2125,16.2195],[96.386,16.3025],[96.49,16.3625],[96.6685,16.421],[96.792,16.5215],[96.8575,16.6485],[96.8595,16.6725],[96.921,16.7445],[96.9135,16.8465],[96.9665,16.912],[97.036,16.898],[97.0705,16.727],[97.1215,16.6125],[97.184,16.552],[97.1975,16.471],[97.2545,16.411],[97.2975,16.3985],[97.3095,16.2885],[97.3005,16.269],[97.3645,16.1555],[97.4155,16.086],[97.4015,16.017],[97.4075,15.965],[97.437,15.9015],[97.478,15.8525],[97.536,15.807],[97.531,15.7465],[97.5595,15.602],[97.562,15.467],[97.5725,15.393],[97.5445,15.3285],[97.555,15.23],[97.5855,15.156],[97.6235,15.098],[97.6275,15.03],[97.616,14.9035],[97.569,14.2635],[97.5755,14.148],[97.5535,13.5255],[97.5445,13.271],[97.524,12.699],[97.536,12.6245],[97.579,12.507],[97.4505,12.144],[97.2725,11.905],[97.238,11.8285],[97.2385,11.7445],[97.3105,11.425],[97.4905,10.6255],[97.477,10.4895],[97.485,10.411],[97.6755,9.8075],[97.7105,9.6395],[97.7245,9.6005],[97.8535,9.5815],[98.042,9.4835],[98.106,9.44],[98.1185,9.4415],[98.2175,9.5655],[98.275,9.7505],[98.3215,9.832],[98.3345,9.891],[98.3665,9.943],[98.471,9.9565],[98.519,9.926],[98.567,9.9435],[98.5795,9.9865],[98.6705,10.1705],[98.7075,10.231],[98.743,10.3245],[98.7555,10.393],[98.795,10.445],[98.817,10.4985],[98.81,10.5815],[98.774,10.6165],[98.7795,10.678],[98.8615,10.7785],[98.913,10.81],[98.959,10.812],[99.0115,10.8565],[98.9975,10.911],[99.024,10.9705],[99.077,10.954],[99.104,11.013],[99.1585,11.034],[99.19,11.084],[99.229,11.113],[99.2635,11.215],[99.3245,11.282],[99.3125,11.3165],[99.367,11.3725],[99.402,11.3885],[99.3935,11.461],[99.4405,11.4795],[99.4745,11.533],[99.4675,11.615],[99.5165,11.637],[99.5655,11.6265],[99.5835,11.6795],[99.638,11.7325],[99.659,11.829],[99.625,11.8315],[99.58,11.9065],[99.592,11.9925],[99.538,12.014],[99.534,12.0655],[99.5705,12.145],[99.515,12.1475],[99.4715,12.2345],[99.434,12.343],[99.442,12.4005],[99.401,12.46],[99.4355,12.566],[99.4125,12.6155],[99.348,12.6395],[99.2945,12.678],[99.2895,12.715],[99.2325,12.74],[99.218,12.8285],[99.1885,12.847],[99.1725,12.943],[99.18,12.9935],[99.0995,13.0725],[99.1275,13.103],[99.139,13.2055],[99.2105,13.2075],[99.187,13.291],[99.207,13.3805],[99.1725,13.563],[99.167,13.726],[99.1215,13.7735],[99.077,13.895],[99.021,13.9455],[98.9985,14.0195],[98.972,14.033],[98.968,14.0825],[98.8785,14.1235],[98.835,14.172],[98.756,14.2145],[98.631,14.316],[98.597,14.3275],[98.5495,14.4345],[98.524,14.4555],[98.487,14.536],[98.436,14.6085],[98.3235,14.7105],[98.2945,14.77],[98.246,14.831],[98.257,14.8635],[98.2335,14.9665],[98.2085,14.9805],[98.2315,15.0445],[98.1835,15.0965],[98.2125,15.178],[98.198,15.2345],[98.252,15.225],[98.306,15.3135],[98.394,15.2625],[98.4175,15.2665],[98.3945,15.3425],[98.471,15.371],[98.4895,15.3935],[98.5375,15.371],[98.5805,15.3755],[98.5855,15.471],[98.553,15.6015],[98.5485,15.685],[98.577,15.8],[98.609,15.8735],[98.584,15.9185],[98.6085,15.9725],[98.5605,16.034],[98.599,16.0735],[98.625,16.0425],[98.7,16.136],[98.8055,16.1045],[98.8515,16.1365],[98.852,16.214],[98.9035,16.2545],[98.9315,16.329],[98.9135,16.393],[98.8825,16.4225],[98.8285,16.4135],[98.817,16.385],[98.7355,16.338],[98.676,16.2805],[98.6575,16.3785],[98.637,16.4255],[98.658,16.455],[98.579,16.557],[98.574,16.6205],[98.5115,16.649],[98.5115,16.701],[98.464,16.7325],[98.4855,16.7895],[98.536,16.8295],[98.5345,16.873],[98.49,16.9685],[98.4375,17.037],[98.3935,17.059],[98.347,17.048],[98.2795,17.1155],[98.2875,17.1565],[98.248,17.2085],[98.1755,17.2585],[98.113,17.319],[98.1105,17.3855],[98.083,17.3905],[98.0575,17.438],[97.9925,17.51],[97.923,17.5395],[97.9065,17.5835],[97.794,17.6795],[97.718,17.8195],[97.672,17.8825],[97.6875,17.925],[97.7315,17.9525],[97.7365,17.9865],[97.677,18.115],[97.676,18.1615],[97.6135,18.225],[97.6395,18.2875],[97.62,18.318],[97.551,18.334],[97.5365,18.286],[97.502,18.2675],[97.444,18.3405],[97.452,18.39],[97.4265,18.408],[97.387,18.5055],[97.346,18.546],[97.393,18.561],[97.426,18.5465],[97.444,18.495],[97.4905,18.51],[97.535,18.491],[97.5495,18.531],[97.605,18.537],[97.6505,18.577],[97.768,18.578],[97.774,18.689],[97.739,18.8285],[97.737,18.887],[97.697,18.9045],[97.6905,18.9485],[97.7385,18.9785],[97.736,19.0405],[97.77,19.0735],[97.8355,19.093],[97.8415,19.225],[97.822,19.329],[97.792,19.395],[97.876,19.5065],[97.8555,19.531],[97.8635,19.577],[97.975,19.602],[97.983,19.6405],[98.026,19.6375],[98.0245,19.722],[98.036,19.801],[98.0835,19.81],[98.1355,19.788],[98.238,19.708],[98.2485,19.6765],[98.3205,19.697],[98.3715,19.69],[98.474,19.695],[98.519,19.715],[98.608,19.709],[98.6725,19.752],[98.7775,19.752],[98.83,19.796],[98.9315,19.774],[98.9845,19.739],[99.034,19.8555],[99.0235,19.9195],[99.045,19.9485],[99.044,20.0355],[99.077,20.101],[99.1085,20.098],[99.159,20.133],[99.242,20.114],[99.3445,20.105],[99.4,20.086],[99.499,20.118],[99.5375,20.147],[99.5625,20.207],[99.523,20.233],[99.507,20.326],[99.4555,20.363],[99.472,20.394],[99.5195,20.3615],[99.6115,20.3325],[99.647,20.3415],[99.6815,20.316],[99.73,20.35],[99.81,20.3365],[99.862,20.4435],[99.9655,20.4625],[99.9905,20.4235],[100.047,20.3915],[100.0845,20.3525]]],[[[93.239,13.7725],[93.2945,13.7845],[93.369,13.8375],[93.504,13.888],[93.5595,13.977],[93.5825,14.101],[93.5645,14.2455],[93.5415,14.313],[93.504,14.3455],[93.4205,14.3805],[93.3305,14.388],[93.2565,14.367],[93.2085,14.3295],[93.165,14.226],[93.098,14.175],[93.0455,14.1185],[93.0245,14.0735],[93.0115,13.948],[93.035,13.882],[93.1345,13.8195],[93.239,13.7725]]],[[[93.341,14.811],[93.364,14.697],[93.3945,14.644],[93.43,14.6165],[93.515,14.587],[93.6115,14.5945],[93.6635,14.612],[93.7545,14.6735],[93.806,14.7565],[93.849,14.853],[93.86,14.92],[93.84,15.021],[93.8015,15.077],[93.7255,15.1135],[93.654,15.1295],[93.589,15.1285],[93.5355,15.107],[93.4785,15.0615],[93.452,14.9945],[93.384,14.9345],[93.357,14.885],[93.341,14.811]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/8c/Flag_of_Myanmar.svg","name:en":"Myanmar","wikidata":"Q836","ISO3166-1:alpha2":"MM","ISO3166-1:alpha3":"MMR","ISO3166-1:numeric":"104"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[131.0685,3.007],[131.1135,2.949],[131.1695,2.974],[131.1825,3.027],[131.163,3.057],[131.108,3.064],[131.0685,3.007]]],[[[131.6735,2.822],[131.6805,2.788],[131.723,2.752],[131.788,2.755],[131.854,2.815],[131.8945,2.935],[131.865,3.032],[131.8,3.061],[131.7585,3.028],[131.7405,2.981],[131.695,2.931],[131.6735,2.822]]],[[[131.8965,4.668],[131.9035,4.64],[131.9745,4.611],[132.0155,4.641],[132.0145,4.702],[131.9905,4.727],[131.938,4.729],[131.8965,4.668]]],[[[132.168,5.33],[132.182,5.29],[132.2275,5.268],[132.258,5.279],[132.2805,5.358],[132.2555,5.401],[132.217,5.407],[132.183,5.384],[132.168,5.33]]],[[[132.258,4.314],[132.2875,4.263],[132.358,4.285],[132.3625,4.336],[132.3235,4.373],[132.276,4.359],[132.258,4.314]]],[[[134.0715,6.89],[134.1135,6.84],[134.189,6.872],[134.2015,6.929],[134.253,6.932],[134.332,6.999],[134.3485,7.037],[134.4095,7.084],[134.4345,7.126],[134.4445,7.201],[134.486,7.216],[134.502,7.252],[134.613,7.308],[134.6365,7.333],[134.6415,7.407],[134.679,7.461],[134.697,7.635],[134.694,7.713],[134.6615,7.789],[134.6215,7.809],[134.569,7.772],[134.5585,7.668],[134.5055,7.637],[134.4635,7.572],[134.4135,7.458],[134.4215,7.418],[134.385,7.391],[134.361,7.323],[134.2805,7.34],[134.235,7.31],[134.251,7.24],[134.1985,7.23],[134.178,7.193],[134.1765,7.128],[134.2035,7.059],[134.175,7.026],[134.1695,6.982],[134.1145,6.971],[134.076,6.936],[134.0715,6.89]]],[[[134.5915,8.172],[134.623,8.124],[134.6625,8.124],[134.6935,8.166],[134.6625,8.218],[134.6225,8.218],[134.5915,8.172]]],[[[134.643,8.04],[134.6835,7.991],[134.7315,8.001],[134.756,8.031],[134.7705,8.095],[134.7295,8.142],[134.676,8.126],[134.643,8.04]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/48/Flag_of_Palau.svg","name:en":"Palau","wikidata":"Q695","ISO3166-1:alpha2":"PW","ISO3166-1:alpha3":"PLW","ISO3166-1:numeric":"585"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[160.595,9.848],[160.611,9.766],[160.6505,9.706],[160.698,9.669],[160.8055,9.627],[160.861,9.592],[160.9825,9.566],[161.0535,9.577],[161.139,9.633],[161.19,9.738],[161.1895,9.816],[161.1425,9.915],[161.0935,9.962],[160.9875,10.033],[160.8785,10.064],[160.797,10.064],[160.743,10.045],[160.65,9.982],[160.614,9.93],[160.595,9.848]]],[[[161.884,11.455],[161.8955,11.391],[161.9215,11.341],[162.0355,11.212],[162.1215,11.165],[162.2,11.145],[162.315,11.136],[162.4045,11.153],[162.503,11.227],[162.55,11.298],[162.587,11.397],[162.595,11.463],[162.5875,11.535],[162.5025,11.719],[162.467,11.763],[162.335,11.848],[162.2895,11.864],[162.1915,11.876],[162.1165,11.861],[162.019,11.801],[161.968,11.747],[161.942,11.683],[161.942,11.597],[161.8985,11.531],[161.884,11.455]]],[[[165.015,11.577],[165.024,11.517],[165.063,11.446],[165.17,11.359],[165.298,11.3],[165.369,11.296],[165.493,11.325],[165.5655,11.314],[165.656,11.336],[165.7265,11.393],[165.7575,11.444],[165.7745,11.534],[165.7505,11.684],[165.6965,11.771],[165.623,11.819],[165.469,11.892],[165.258,11.903],[165.156,11.862],[165.113,11.831],[165.0715,11.776],[165.015,11.577]]],[[[165.3195,9.217],[165.3385,9.133],[165.3855,9.062],[165.3885,9.012],[165.441,8.909],[165.525,8.853],[165.5805,8.796],[165.7,8.735],[165.7835,8.727],[165.879,8.762],[165.9395,8.825],[165.9615,8.873],[165.9695,8.945],[165.936,9.043],[165.8775,9.136],[165.8295,9.235],[165.7965,9.28],[165.6545,9.374],[165.5885,9.41],[165.528,9.42],[165.456,9.409],[165.4035,9.382],[165.3525,9.329],[165.326,9.269],[165.3195,9.217]]],[[[165.7175,10.182],[165.752,10.042],[165.8065,9.94],[165.8605,9.883],[165.9215,9.842],[165.9835,9.82],[166.094,9.827],[166.176,9.88],[166.223,9.962],[166.2305,10.06],[166.217,10.116],[166.216,10.212],[166.1845,10.287],[166.141,10.333],[166.048,10.373],[165.9005,10.38],[165.817,10.353],[165.751,10.291],[165.7175,10.182]]],[[[166.0135,8.958],[166.0155,8.904],[166.039,8.826],[166.109,8.751],[166.2025,8.719],[166.28,8.72],[166.359,8.74],[166.433,8.797],[166.471,8.871],[166.4775,8.931],[166.456,9.013],[166.39,9.099],[166.297,9.15],[166.1815,9.157],[166.109,9.127],[166.06,9.085],[166.029,9.034],[166.0135,8.958]]],[[[166.084,11.144],[166.0965,11.074],[166.119,11.03],[166.1835,10.97],[166.2975,10.928],[166.377,10.917],[166.514,10.916],[166.6245,10.946],[166.676,10.983],[166.823,10.95],[166.9535,10.964],[167.0085,10.99],[167.0595,11.038],[167.0995,11.131],[167.1035,11.185],[167.189,11.266],[167.2385,11.214],[167.293,11.185],[167.384,11.111],[167.4435,11.096],[167.539,11.107],[167.6065,11.144],[167.646,11.185],[167.71,11.303],[167.725,11.359],[167.71,11.461],[167.678,11.512],[167.6105,11.567],[167.529,11.601],[167.412,11.607],[167.2785,11.572],[167.2415,11.551],[167.192,11.619],[167.093,11.669],[166.969,11.658],[166.9035,11.683],[166.84,11.686],[166.7445,11.665],[166.691,11.672],[166.6135,11.661],[166.52,11.596],[166.4705,11.492],[166.4655,11.449],[166.4265,11.371],[166.4045,11.362],[166.2955,11.379],[166.2225,11.368],[166.148,11.323],[166.1135,11.28],[166.0885,11.216],[166.084,11.144]]],[[[166.614,9.328],[166.6375,9.237],[166.707,9.16],[166.857,9.09],[166.9755,9.072],[167.0035,9.024],[167.0495,8.982],[167.185,8.914],[167.2585,8.895],[167.368,8.906],[167.3775,8.807],[167.4025,8.747],[167.454,8.674],[167.5825,8.561],[167.704,8.517],[167.803,8.529],[167.8775,8.575],[167.9155,8.621],[167.9445,8.707],[167.943,8.78],[167.976,8.875],[167.965,8.973],[167.944,9.014],[167.945,9.076],[167.917,9.172],[167.888,9.223],[167.838,9.268],[167.741,9.3],[167.728,9.358],[167.6635,9.492],[167.625,9.542],[167.537,9.592],[167.4305,9.599],[167.354,9.569],[167.274,9.507],[167.1375,9.544],[166.91,9.52],[166.8425,9.531],[166.759,9.523],[166.6645,9.463],[166.6285,9.404],[166.614,9.328]]],[[[167.17,8.316],[167.182,8.2355],[167.226,8.1765],[167.2985,8.1185],[167.387,8.1065],[167.446,8.1115],[167.5535,8.1845],[167.5835,8.2675],[167.582,8.347],[167.5615,8.414],[167.52,8.469],[167.464,8.501],[167.363,8.518],[167.3015,8.503],[167.2445,8.472],[167.185,8.405],[167.17,8.316]]],[[[167.77,8.199],[167.779,8.137],[167.8465,8.019],[167.9075,7.959],[167.9555,7.937],[168.0225,7.878],[168.009,7.794],[168.0205,7.72],[168.0585,7.648],[168.108,7.599],[168.201,7.557],[168.2685,7.554],[168.361,7.584],[168.444,7.657],[168.5055,7.789],[168.5065,7.859],[168.49,7.913],[168.424,8.035],[168.383,8.082],[168.3875,8.184],[168.3575,8.263],[168.275,8.34],[168.2135,8.361],[168.1245,8.359],[168.084,8.378],[167.9695,8.4],[167.8725,8.374],[167.822,8.334],[167.785,8.275],[167.77,8.199]]],[[[167.887,5.639],[167.911,5.525],[167.952,5.459],[168.026,5.408],[168.0915,5.392],[168.1645,5.396],[168.227,5.422],[168.274,5.462],[168.3095,5.52],[168.331,5.59],[168.334,5.662],[168.303,5.744],[168.253,5.796],[168.2115,5.819],[168.1125,5.839],[168.0535,5.837],[167.976,5.806],[167.9145,5.741],[167.887,5.639]]],[[[168.344,7.442],[168.369,7.32],[168.4145,7.245],[168.4875,7.183],[168.624,7.107],[168.7255,7.081],[168.859,7.076],[168.9235,7.099],[168.969,7.134],[169.061,7.272],[169.077,7.323],[169.118,7.385],[169.168,7.538],[169.1605,7.662],[169.182,7.752],[169.1655,7.834],[169.138,7.88],[169.0455,7.945],[168.972,7.956],[168.8825,7.934],[168.8195,7.884],[168.786,7.827],[168.7725,7.726],[168.655,7.685],[168.5535,7.682],[168.494,7.658],[168.413,7.6],[168.358,7.518],[168.344,7.442]]],[[[168.4445,4.638],[168.463,4.554],[168.551,4.435],[168.6105,4.395],[168.664,4.378],[168.7305,4.375],[168.844,4.415],[168.9275,4.486],[168.9525,4.527],[168.972,4.603],[168.9595,4.721],[168.9165,4.801],[168.868,4.845],[168.7795,4.876],[168.667,4.88],[168.553,4.837],[168.4805,4.756],[168.4555,4.704],[168.4445,4.638]]],[[[168.731,14.568],[168.7645,14.457],[168.8545,14.383],[168.96,14.367],[169.0225,14.383],[169.1295,14.45],[169.182,14.504],[169.2165,14.579],[169.2235,14.667],[169.195,14.763],[169.1135,14.845],[169.029,14.872],[168.924,14.856],[168.8435,14.796],[168.804,14.719],[168.7595,14.667],[168.731,14.568]]],[[[168.786,10.052],[168.8095,9.955],[168.8105,9.869],[168.852,9.785],[168.916,9.733],[168.9755,9.663],[169.041,9.623],[169.1005,9.605],[169.2085,9.606],[169.2545,9.621],[169.3405,9.623],[169.4185,9.652],[169.471,9.695],[169.5115,9.77],[169.5135,9.877],[169.625,9.902],[169.686,9.953],[169.7235,10.028],[169.73,10.086],[169.8405,10.038],[169.9765,10.015],[170.018,10.018],[170.101,10.052],[170.164,10.121],[170.188,10.189],[170.19,10.265],[170.168,10.473],[170.1305,10.562],[170.0615,10.636],[169.968,10.669],[169.899,10.665],[169.8225,10.63],[169.7605,10.556],[169.738,10.454],[169.669,10.365],[169.6565,10.269],[169.642,10.246],[169.586,10.273],[169.502,10.281],[169.395,10.238],[169.3415,10.173],[169.3195,10.098],[169.2755,10.118],[169.189,10.199],[169.144,10.218],[169.0115,10.251],[168.955,10.249],[168.8645,10.209],[168.8165,10.156],[168.786,10.052]]],[[[168.9125,5.64],[168.922,5.58],[168.974,5.496],[169.048,5.451],[169.1235,5.44],[169.1865,5.451],[169.2715,5.505],[169.323,5.591],[169.3295,5.675],[169.2805,5.781],[169.197,5.837],[169.131,5.849],[169.031,5.825],[168.97,5.781],[168.9265,5.714],[168.9125,5.64]]],[[[169.205,6.266],[169.2265,6.17],[169.2565,6.124],[169.2215,5.948],[169.236,5.878],[169.2715,5.82],[169.3415,5.768],[169.3905,5.698],[169.4595,5.639],[169.559,5.592],[169.6275,5.588],[169.7085,5.614],[169.752,5.648],[169.8035,5.733],[169.819,5.803],[169.8605,5.837],[169.9035,5.903],[169.9205,5.959],[169.9225,6.051],[169.871,6.153],[169.7715,6.233],[169.7255,6.286],[169.665,6.382],[169.5785,6.467],[169.5275,6.492],[169.4255,6.506],[169.3095,6.465],[169.253,6.418],[169.223,6.368],[169.205,6.266]]],[[[169.346,11.197],[169.3595,11.127],[169.416,11.047],[169.4485,10.972],[169.5185,10.91],[169.5745,10.887],[169.637,10.88],[169.7215,10.899],[169.8155,10.972],[169.848,11.017],[169.964,11.052],[170.023,11.105],[170.059,11.18],[170.063,11.252],[170.0205,11.353],[169.931,11.457],[169.888,11.489],[169.808,11.514],[169.7315,11.508],[169.647,11.462],[169.599,11.397],[169.5295,11.398],[169.455,11.376],[169.3775,11.305],[169.346,11.197]]],[[[169.623,9.529],[169.6395,9.449],[169.71,9.363],[169.7215,9.277],[169.701,9.187],[169.73,9.085],[169.7745,9.034],[169.8375,8.988],[169.8635,8.919],[169.8935,8.88],[169.952,8.838],[170.008,8.818],[170.096,8.812],[170.15,8.825],[170.207,8.858],[170.286,8.962],[170.3055,9.032],[170.297,9.146],[170.2755,9.193],[170.3575,9.248],[170.401,9.308],[170.445,9.427],[170.436,9.525],[170.3945,9.596],[170.2855,9.706],[170.187,9.752],[170.0905,9.747],[170.0355,9.722],[169.8,9.729],[169.7215,9.7],[169.643,9.615],[169.623,9.529]]],[[[169.9,12.195],[169.9115,12.125],[169.9695,12.038],[170.055,11.992],[170.1465,11.988],[170.235,12.026],[170.292,12.092],[170.331,12.17],[170.349,12.258],[170.3375,12.318],[170.2955,12.387],[170.198,12.445],[170.121,12.451],[170.059,12.435],[169.985,12.384],[169.915,12.267],[169.9,12.195]]],[[[170.6375,8.904],[170.642,8.86],[170.6915,8.686],[170.7505,8.595],[170.819,8.546],[170.867,8.474],[170.8375,8.383],[170.8405,8.327],[170.8135,8.226],[170.822,8.17],[170.8565,8.104],[170.892,8.069],[171.0855,7.955],[171.137,7.937],[171.2235,7.938],[171.321,7.991],[171.3665,8.06],[171.381,8.126],[171.3605,8.25],[171.3685,8.33],[171.3575,8.377],[171.382,8.42],[171.397,8.514],[171.387,8.564],[171.43,8.643],[171.4385,8.731],[171.4055,8.82],[171.324,8.908],[171.26,8.954],[171.1905,9.022],[171.136,9.053],[170.993,9.095],[170.886,9.106],[170.758,9.086],[170.706,9.053],[170.6525,8.978],[170.6375,8.904]]],[[[170.6595,10.276],[170.6885,10.168],[170.7345,10.115],[170.778,10.088],[170.875,10.068],[170.939,10.079],[171.0245,10.134],[171.0635,10.196],[171.0805,10.268],[171.079,10.326],[171.051,10.402],[170.9765,10.473],[170.9115,10.496],[170.852,10.498],[170.7885,10.481],[170.7145,10.426],[170.675,10.362],[170.6595,10.276]]],[[[170.8255,7.146],[170.839,7.074],[170.9115,6.978],[171.015,6.917],[171.1615,6.864],[171.2535,6.854],[171.4265,6.89],[171.522,6.814],[171.622,6.793],[171.6835,6.766],[171.7585,6.757],[171.809,6.764],[171.8825,6.801],[171.9465,6.888],[172.016,6.916],[172.0735,6.967],[172.125,7.051],[172.1375,7.127],[172.1275,7.183],[172.0935,7.246],[172.034,7.298],[171.9435,7.33],[171.858,7.322],[171.8095,7.42],[171.756,7.462],[171.6935,7.484],[171.609,7.486],[171.558,7.47],[171.4995,7.429],[171.4665,7.385],[171.4395,7.31],[171.3375,7.344],[171.224,7.36],[171.165,7.401],[171.0865,7.421],[170.983,7.406],[170.9195,7.365],[170.8695,7.299],[170.835,7.208],[170.8255,7.146]]],[[[171.515,6.203],[171.5275,6.121],[171.5275,6.039],[171.543,5.993],[171.5835,5.936],[171.6415,5.896],[171.7285,5.856],[171.7895,5.84],[171.872,5.846],[171.955,5.832],[172.024,5.738],[172.1,5.689],[172.166,5.677],[172.259,5.697],[172.321,5.743],[172.371,5.847],[172.3585,5.967],[172.3145,6.041],[172.293,6.215],[172.248,6.326],[172.1905,6.385],[172.1355,6.411],[172.041,6.42],[171.9855,6.447],[171.895,6.458],[171.6515,6.404],[171.566,6.345],[171.5215,6.261],[171.515,6.203]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/2e/Flag_of_the_Marshall_Islands.svg","name:en":"Marshall Islands","wikidata":"Q709","ISO3166-1:alpha2":"MH","ISO3166-1:alpha3":"MHL","ISO3166-1:numeric":"584"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-62.6405,-22.236],[-62.63,-22.303],[-62.571,-22.327],[-62.51,-22.3805],[-62.4655,-22.383],[-62.4505,-22.4195],[-62.336,-22.4815],[-62.2875,-22.4775],[-62.2815,-22.5245],[-62.228,-22.5445],[-62.2545,-22.6115],[-62.2025,-22.6705],[-62.1985,-22.706],[-62.158,-22.76],[-62.099,-22.806],[-62.036,-22.8785],[-61.997,-22.9485],[-61.9975,-23.0075],[-61.85,-23.1045],[-61.7905,-23.1645],[-61.753,-23.173],[-61.736,-23.2345],[-61.627,-23.2775],[-61.5235,-23.3675],[-61.501,-23.4105],[-61.445,-23.42],[-61.417,-23.4485],[-61.3255,-23.4665],[-61.2195,-23.5575],[-61.1735,-23.5605],[-61.096,-23.613],[-61.0805,-23.691],[-61.0125,-23.7615],[-60.99,-23.818],[-60.9215,-23.823],[-60.8595,-23.8695],[-60.732,-23.872],[-60.7095,-23.892],[-60.595,-23.9175],[-60.572,-23.96],[-60.4875,-23.951],[-60.436,-23.9665],[-60.309,-24.038],[-60.197,-24.045],[-60.039,-24.0115],[-59.7555,-24.1705],[-59.4745,-24.3315],[-59.4215,-24.409],[-59.373,-24.423],[-59.354,-24.478],[-59.239,-24.5465],[-59.186,-24.5635],[-59.13,-24.617],[-59.0565,-24.6315],[-59.0235,-24.66],[-58.9705,-24.663],[-58.8785,-24.7335],[-58.804,-24.7735],[-58.726,-24.7745],[-58.6725,-24.832],[-58.573,-24.821],[-58.4405,-24.8795],[-58.393,-24.9515],[-58.341,-24.9965],[-58.236,-24.927],[-58.194,-24.968],[-58.153,-24.973],[-58.1345,-25.01],[-58.077,-25.038],[-58.0105,-25.038],[-57.9885,-25.0815],[-57.8825,-25.076],[-57.8215,-25.142],[-57.7765,-25.1555],[-57.752,-25.2275],[-57.695,-25.2885],[-57.6985,-25.3165],[-57.6365,-25.3855],[-57.601,-25.398],[-57.555,-25.4485],[-57.5795,-25.569],[-57.6085,-25.6065],[-57.677,-25.6055],[-57.6705,-25.6505],[-57.7405,-25.6505],[-57.7675,-25.6995],[-57.733,-25.725],[-57.774,-25.758],[-57.8275,-25.759],[-57.7965,-25.825],[-57.8585,-25.858],[-57.842,-25.921],[-57.8935,-25.964],[-57.8555,-26.009],[-57.926,-26.037],[-57.9495,-26.0675],[-58.0415,-26.1125],[-58.0845,-26.112],[-58.1195,-26.1525],[-58.1065,-26.2375],[-58.158,-26.2605],[-58.1655,-26.3575],[-58.2105,-26.3775],[-58.2235,-26.4635],[-58.2085,-26.505],[-58.2225,-26.5425],[-58.193,-26.5945],[-58.197,-26.6535],[-58.2635,-26.6515],[-58.246,-26.7425],[-58.282,-26.7915],[-58.337,-26.817],[-58.325,-26.8695],[-58.4935,-26.94],[-58.4685,-26.9865],[-58.5195,-27.004],[-58.555,-27.058],[-58.555,-27.109],[-58.6315,-27.115],[-58.6625,-27.1625],[-58.6565,-27.192],[-58.5925,-27.23],[-58.599,-27.3],[-58.489,-27.2735],[-58.4135,-27.2865],[-58.3035,-27.274],[-58.26,-27.257],[-58.198,-27.2735],[-58.0595,-27.2625],[-57.9145,-27.2655],[-57.8775,-27.2785],[-57.8145,-27.334],[-57.702,-27.3245],[-57.667,-27.356],[-57.4875,-27.444],[-57.4125,-27.425],[-57.3115,-27.418],[-57.232,-27.4665],[-57.1425,-27.4835],[-57.0425,-27.481],[-56.9655,-27.502],[-56.942,-27.5585],[-56.8495,-27.6065],[-56.796,-27.587],[-56.747,-27.605],[-56.689,-27.579],[-56.6765,-27.5505],[-56.721,-27.468],[-56.6495,-27.4605],[-56.6065,-27.4315],[-56.542,-27.4385],[-56.463,-27.5695],[-56.401,-27.599],[-56.3565,-27.567],[-56.339,-27.5245],[-56.2935,-27.494],[-56.2945,-27.42],[-56.243,-27.4045],[-56.176,-27.3435],[-56.0875,-27.3085],[-56.037,-27.3125],[-55.9845,-27.353],[-55.8965,-27.3425],[-55.8395,-27.407],[-55.787,-27.4405],[-55.737,-27.445],[-55.675,-27.373],[-55.599,-27.3375],[-55.6005,-27.279],[-55.581,-27.235],[-55.6155,-27.21],[-55.5745,-27.169],[-55.549,-27.098],[-55.481,-27.1115],[-55.423,-26.9955],[-55.3705,-26.9645],[-55.306,-26.9625],[-55.238,-26.9425],[-55.195,-26.9685],[-55.135,-26.9485],[-55.1435,-26.8685],[-55.0525,-26.7975],[-54.9635,-26.7875],[-54.9485,-26.702],[-54.9165,-26.6685],[-54.86,-26.656],[-54.8125,-26.668],[-54.7845,-26.605],[-54.807,-26.554],[-54.7815,-26.51],[-54.7155,-26.4595],[-54.698,-26.382],[-54.649,-26.312],[-54.6795,-26.2645],[-54.6185,-26.209],[-54.6695,-26.164],[-54.647,-26.075],[-54.677,-26.017],[-54.655,-25.979],[-54.6085,-25.955],[-54.6225,-25.9145],[-54.5915,-25.818],[-54.6225,-25.7835],[-54.658,-25.6875],[-54.6495,-25.6635],[-54.581,-25.658],[-54.5935,-25.593],[-54.602,-25.4855],[-54.6185,-25.4515],[-54.5835,-25.3955],[-54.499,-25.313],[-54.4275,-25.1505],[-54.4555,-25.0975],[-54.4355,-24.9475],[-54.3955,-24.803],[-54.3245,-24.6605],[-54.3175,-24.5815],[-54.336,-24.515],[-54.3215,-24.4545],[-54.2605,-24.3705],[-54.277,-24.2965],[-54.3155,-24.258],[-54.3455,-24.147],[-54.282,-24.0735],[-54.325,-24.021],[-54.415,-23.958],[-54.44,-23.9045],[-54.515,-23.885],[-54.589,-23.839],[-54.6685,-23.8175],[-54.7055,-23.8645],[-54.7935,-23.868],[-54.8595,-23.905],[-54.927,-23.9245],[-54.9275,-23.9615],[-54.999,-23.9575],[-55.0625,-23.993],[-55.1295,-23.984],[-55.229,-24.014],[-55.3175,-23.963],[-55.336,-23.993],[-55.4195,-23.9575],[-55.4465,-23.917],[-55.43,-23.79],[-55.464,-23.712],[-55.475,-23.6505],[-55.5305,-23.628],[-55.527,-23.5675],[-55.5465,-23.5225],[-55.5445,-23.4485],[-55.5045,-23.379],[-55.556,-23.3165],[-55.542,-23.227],[-55.5235,-23.1985],[-55.542,-23.156],[-55.596,-23.1195],[-55.634,-22.992],[-55.6655,-22.852],[-55.651,-22.7925],[-55.62,-22.765],[-55.6135,-22.694],[-55.6255,-22.628],[-55.7235,-22.552],[-55.753,-22.4755],[-55.735,-22.459],[-55.749,-22.3835],[-55.7895,-22.3845],[-55.853,-22.2805],[-55.9695,-22.292],[-56.1235,-22.2715],[-56.2135,-22.2755],[-56.243,-22.2355],[-56.316,-22.218],[-56.3575,-22.176],[-56.368,-22.137],[-56.419,-22.0795],[-56.4715,-22.08],[-56.517,-22.108],[-56.5565,-22.195],[-56.61,-22.223],[-56.638,-22.258],[-56.7055,-22.2365],[-56.7445,-22.241],[-56.8445,-22.3005],[-56.8835,-22.2475],[-56.946,-22.256],[-57.0605,-22.2305],[-57.1785,-22.233],[-57.237,-22.251],[-57.304,-22.224],[-57.362,-22.234],[-57.389,-22.205],[-57.4835,-22.1925],[-57.5205,-22.1695],[-57.5805,-22.1755],[-57.5865,-22.145],[-57.637,-22.106],[-57.6895,-22.0885],[-57.733,-22.0995],[-57.8005,-22.1505],[-57.93,-22.119],[-58.009,-22.0395],[-57.974,-22.0155],[-57.9165,-21.8765],[-57.93,-21.823],[-57.9085,-21.773],[-57.946,-21.741],[-57.896,-21.7045],[-57.941,-21.6355],[-57.9205,-21.581],[-57.9685,-21.529],[-57.9275,-21.4705],[-57.9095,-21.4065],[-57.8525,-21.32],[-57.92,-21.2765],[-57.8515,-21.2245],[-57.8645,-21.14],[-57.8465,-21.098],[-57.867,-21.04],[-57.8165,-20.9795],[-57.8815,-20.914],[-57.927,-20.893],[-57.856,-20.848],[-57.935,-20.7455],[-57.87,-20.7255],[-57.9155,-20.6685],[-57.9515,-20.702],[-57.988,-20.6845],[-57.9705,-20.6425],[-58.0135,-20.6055],[-57.995,-20.5615],[-58.016,-20.527],[-57.9965,-20.448],[-58.022,-20.4005],[-58.075,-20.3865],[-58.1025,-20.3225],[-58.0915,-20.26],[-58.1435,-20.272],[-58.166,-20.231],[-58.128,-20.183],[-58.1695,-20.167],[-58.138,-20.124],[-58.1655,-20.0645],[-58.197,-19.925],[-58.202,-19.8625],[-58.164,-19.827],[-58.236,-19.775],[-58.6965,-19.5055],[-59.069,-19.2875],[-59.5,-19.291],[-59.978,-19.2945],[-60.6045,-19.4565],[-60.9985,-19.519],[-61.735,-19.6345],[-61.9225,-20.089],[-62.2665,-20.563],[-62.258,-20.9895],[-62.261,-21.0585],[-62.458,-21.6725],[-62.6405,-22.236]],[[-57.593,-27.392],[-57.4885,-27.406],[-57.4925,-27.4385],[-57.593,-27.392]],[[-57.032,-27.481],[-56.9225,-27.4225],[-56.885,-27.4245],[-56.77,-27.502],[-56.726,-27.5125],[-56.749,-27.5615],[-56.833,-27.596],[-56.8735,-27.5815],[-56.911,-27.5315],[-56.967,-27.495],[-57.032,-27.481]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/27/Flag_of_Paraguay.svg","name:en":"Paraguay","wikidata":"Q733","ISO3166-1:alpha2":"PY","ISO3166-1:alpha3":"PRY","ISO3166-1:numeric":"600"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-81.3445,-3.392],[-81.5445,-3.392],[-84.4395,-3.392],[-84.527,-3.7375],[-84.578,-4.017],[-84.615,-4.31],[-84.634,-4.5795],[-84.6355,-4.7015],[-84.628,-4.9155],[-84.607,-5.129],[-84.572,-5.3405],[-84.4945,-5.7045],[-84.467,-6.031],[-84.4615,-6.1715],[-84.4425,-6.386],[-84.4195,-6.546],[-84.377,-6.757],[-84.3205,-6.965],[-84.2585,-7.1515],[-84.1775,-7.354],[-84.0835,-7.551],[-83.9585,-7.7775],[-83.897,-7.983],[-83.823,-8.1855],[-83.7005,-8.453],[-83.5995,-8.639],[-83.486,-8.8185],[-83.362,-8.9905],[-83.2265,-9.154],[-83.081,-9.309],[-82.9255,-9.454],[-82.831,-9.534],[-82.6615,-9.6645],[-82.4835,-9.7835],[-82.298,-9.8915],[-82.1055,-9.987],[-82.0045,-10.0315],[-81.9635,-10.077],[-81.8655,-10.2575],[-81.7255,-10.4755],[-81.642,-10.6405],[-81.466,-10.944],[-81.4295,-11.027],[-81.3815,-11.1815],[-81.3085,-11.378],[-81.229,-11.5565],[-81.2125,-11.807],[-81.1835,-12.018],[-81.059,-12.636],[-81.0105,-12.8405],[-80.9485,-13.0415],[-80.874,-13.2385],[-80.787,-13.4305],[-80.661,-13.662],[-80.5765,-13.7965],[-80.4535,-13.969],[-80.3195,-14.1335],[-80.136,-14.329],[-79.978,-14.474],[-79.805,-14.6135],[-79.757,-14.796],[-79.6725,-15.0455],[-79.568,-15.288],[-79.3795,-15.6445],[-79.312,-15.777],[-79.2075,-15.9565],[-79.122,-16.087],[-78.998,-16.2545],[-78.856,-16.4275],[-78.7395,-16.5995],[-78.5725,-16.811],[-78.426,-16.9705],[-78.2285,-17.1565],[-77.9785,-17.361],[-77.8465,-17.524],[-77.739,-17.6425],[-77.5865,-17.7925],[-77.424,-17.9335],[-77.2415,-18.0755],[-77.0565,-18.202],[-76.795,-18.353],[-76.698,-18.414],[-76.5055,-18.522],[-76.3725,-18.587],[-76.194,-18.698],[-75.938,-18.833],[-75.6495,-19.0235],[-75.509,-19.11],[-75.315,-19.2145],[-75.063,-19.3285],[-74.819,-19.417],[-74.6085,-19.507],[-74.4665,-19.5585],[-74.264,-19.6565],[-74.203,-19.701],[-74.023,-19.9155],[-73.831,-20.109],[-73.7305,-20.1985],[-73.727,-20.164],[-73.7535,-19.9735],[-73.6035,-19.8785],[-73.153,-19.561],[-72.9275,-19.4025],[-72.8635,-19.351],[-72.34,-18.89],[-72.0335,-18.6295],[-71.7825,-18.35],[-71.4005,-18.35],[-70.91,-18.35],[-70.591,-18.35],[-70.3765,-18.35],[-70.302,-18.309],[-70.1495,-18.3195],[-70.0505,-18.269],[-69.966,-18.262],[-69.8605,-18.17],[-69.816,-18.1165],[-69.7545,-17.9895],[-69.75,-17.9475],[-69.799,-17.8655],[-69.801,-17.762],[-69.826,-17.706],[-69.7965,-17.646],[-69.6665,-17.6605],[-69.5525,-17.5755],[-69.4685,-17.4985],[-69.4685,-17.374],[-69.616,-17.2615],[-69.557,-17.1595],[-69.512,-17.146],[-69.449,-17.098],[-69.4065,-17.1005],[-69.3735,-17.073],[-69.3945,-17.024],[-69.3435,-16.987],[-69.3045,-16.916],[-69.219,-16.836],[-69.17,-16.7275],[-69.0455,-16.6885],[-69.017,-16.638],[-69.04,-16.5735],[-68.9885,-16.4255],[-68.8005,-16.349],[-68.7955,-16.3315],[-68.893,-16.2595],[-68.9615,-16.1945],[-69.013,-16.22],[-69.0555,-16.1975],[-69.1025,-16.225],[-69.1965,-16.168],[-69.402,-15.606],[-69.3295,-15.537],[-69.29,-15.437],[-69.2535,-15.469],[-69.2485,-15.371],[-69.2155,-15.317],[-69.1315,-15.246],[-69.133,-15.221],[-69.236,-15.1205],[-69.285,-15.095],[-69.299,-15.0495],[-69.3615,-14.949],[-69.344,-14.8825],[-69.354,-14.8015],[-69.301,-14.7655],[-69.2255,-14.7405],[-69.258,-14.683],[-69.2295,-14.6565],[-69.22,-14.5815],[-69.15,-14.5805],[-69.1405,-14.5135],[-69.084,-14.452],[-69.031,-14.426],[-68.9855,-14.3745],[-69.015,-14.3145],[-68.9625,-14.22],[-68.828,-14.22],[-68.856,-14.1625],[-68.8955,-14.0415],[-68.942,-14.0215],[-68.9785,-13.976],[-68.982,-13.894],[-68.951,-13.8785],[-68.9175,-13.8135],[-68.9805,-13.7835],[-68.977,-13.758],[-69.066,-13.6885],[-69.061,-13.657],[-68.993,-13.65],[-68.917,-13.4865],[-68.8455,-13.246],[-68.8745,-13.1155],[-68.85,-13.037],[-68.8705,-13.019],[-68.8715,-12.8855],[-68.836,-12.859],[-68.8345,-12.813],[-68.788,-12.757],[-68.743,-12.726],[-68.713,-12.6815],[-68.7415,-12.6355],[-68.713,-12.603],[-68.652,-12.498],[-68.9705,-11.913],[-69.255,-11.459],[-69.379,-11.264],[-69.572,-10.9455],[-69.7395,-10.964],[-69.7885,-10.9295],[-69.901,-10.92],[-70.067,-10.9945],[-70.092,-10.9855],[-70.171,-11.043],[-70.256,-11.0505],[-70.3085,-11.071],[-70.3765,-11.06],[-70.4375,-11.0265],[-70.5305,-10.9345],[-70.614,-10.9965],[-70.6135,-10.608],[-70.613,-10.162],[-70.6125,-9.8055],[-70.5365,-9.766],[-70.528,-9.726],[-70.551,-9.67],[-70.598,-9.6075],[-70.5915,-9.5485],[-70.5415,-9.538],[-70.4945,-9.4405],[-70.5055,-9.422],[-70.5845,-9.443],[-70.6735,-9.5295],[-70.75,-9.5695],[-70.799,-9.642],[-70.868,-9.665],[-70.9245,-9.7415],[-70.974,-9.7605],[-70.9985,-9.818],[-71.051,-9.816],[-71.1575,-9.8725],[-71.192,-9.9405],[-71.2975,-9.9925],[-71.3425,-9.967],[-71.378,-10.0005],[-71.8225,-10.0005],[-72.181,-10.0005],[-72.1615,-9.9805],[-72.174,-9.93],[-72.1505,-9.9055],[-72.162,-9.831],[-72.1515,-9.7975],[-72.215,-9.779],[-72.259,-9.712],[-72.246,-9.6575],[-72.2885,-9.603],[-72.2815,-9.5425],[-72.318,-9.544],[-72.341,-9.5065],[-72.407,-9.4775],[-72.5305,-9.4825],[-72.6425,-9.4435],[-72.6975,-9.436],[-72.717,-9.4125],[-73.2005,-9.4115],[-73.189,-9.363],[-73.156,-9.3565],[-73.064,-9.2275],[-73.034,-9.234],[-73.0195,-9.1855],[-72.9635,-9.144],[-72.94,-9.093],[-72.948,-9.029],[-73.0015,-8.917],[-73.037,-8.916],[-73.112,-8.8195],[-73.1145,-8.7855],[-73.167,-8.6985],[-73.251,-8.6905],[-73.3195,-8.6105],[-73.344,-8.6025],[-73.331,-8.476],[-73.373,-8.472],[-73.408,-8.4305],[-73.4415,-8.4255],[-73.543,-8.3475],[-73.526,-8.2745],[-73.563,-8.2455],[-73.5955,-8.166],[-73.585,-8.127],[-73.6405,-8.0065],[-73.669,-8.0125],[-73.706,-7.962],[-73.7455,-7.9495],[-73.767,-7.9105],[-73.7355,-7.879],[-73.678,-7.858],[-73.6765,-7.8],[-73.714,-7.743],[-73.8035,-7.714],[-73.8375,-7.6705],[-73.869,-7.674],[-73.9,-7.639],[-73.8895,-7.603],[-73.9585,-7.5645],[-73.9175,-7.5005],[-73.9235,-7.362],[-73.8615,-7.388],[-73.8185,-7.338],[-73.7225,-7.341],[-73.695,-7.299],[-73.7165,-7.2275],[-73.7985,-7.113],[-73.762,-7.0625],[-73.7615,-6.94],[-73.724,-6.8875],[-73.7105,-6.84],[-73.6035,-6.732],[-73.5615,-6.7215],[-73.521,-6.676],[-73.392,-6.6415],[-73.3525,-6.5935],[-73.2315,-6.5855],[-73.1995,-6.5695],[-73.171,-6.518],[-73.1115,-6.447],[-73.1565,-6.327],[-73.1605,-6.2765],[-73.1865,-6.2625],[-73.221,-6.177],[-73.25,-6.133],[-73.2375,-6.043],[-73.1935,-6.0055],[-73.1855,-5.95],[-73.152,-5.868],[-73.0575,-5.7965],[-72.959,-5.6565],[-72.9735,-5.6135],[-72.9495,-5.5475],[-72.958,-5.4645],[-72.9245,-5.408],[-72.8955,-5.325],[-72.867,-5.29],[-72.864,-5.2345],[-72.887,-5.1615],[-72.822,-5.1405],[-72.7085,-5.049],[-72.6595,-5.063],[-72.607,-5.028],[-72.5995,-4.9875],[-72.5205,-4.933],[-72.4855,-4.935],[-72.429,-4.9085],[-72.388,-4.8655],[-72.3825,-4.8315],[-72.3265,-4.8015],[-72.31,-4.7745],[-72.2295,-4.7755],[-72.208,-4.7465],[-72.102,-4.706],[-72.059,-4.6495],[-71.9865,-4.6135],[-71.887,-4.53],[-71.784,-4.4835],[-71.708,-4.511],[-71.6445,-4.5015],[-71.6195,-4.528],[-71.566,-4.5045],[-71.537,-4.4635],[-71.422,-4.4685],[-71.3795,-4.425],[-71.34,-4.443],[-71.264,-4.4305],[-71.2615,-4.3995],[-71.145,-4.3815],[-71.107,-4.401],[-71.085,-4.373],[-71.038,-4.402],[-71.017,-4.383],[-70.9335,-4.3795],[-70.878,-4.2935],[-70.8365,-4.2555],[-70.8025,-4.1845],[-70.7475,-4.161],[-70.7265,-4.185],[-70.672,-4.1955],[-70.6355,-4.172],[-70.5845,-4.1855],[-70.523,-4.1375],[-70.467,-4.1765],[-70.43,-4.131],[-70.3295,-4.1445],[-70.2875,-4.1655],[-70.284,-4.272],[-70.256,-4.31],[-70.211,-4.3175],[-70.202,-4.3555],[-70.126,-4.2865],[-70.04,-4.348],[-69.952,-4.311],[-69.948,-4.2315],[-69.9695,-4.1955],[-70.0405,-4.125],[-70.062,-4.0825],[-70.1265,-4.035],[-70.161,-3.9875],[-70.1805,-3.9305],[-70.2475,-3.8965],[-70.284,-3.824],[-70.3475,-3.8005],[-70.415,-3.828],[-70.4535,-3.863],[-70.5235,-3.88],[-70.5605,-3.827],[-70.6505,-3.8305],[-70.713,-3.7955],[-70.3355,-3.1975],[-70.0805,-2.795],[-70.061,-2.7595],[-70.0675,-2.6755],[-70.1145,-2.697],[-70.1605,-2.691],[-70.1565,-2.647],[-70.219,-2.6455],[-70.229,-2.5815],[-70.2675,-2.5455],[-70.3335,-2.578],[-70.354,-2.49],[-70.4115,-2.526],[-70.4445,-2.512],[-70.472,-2.454],[-70.601,-2.4845],[-70.6425,-2.4585],[-70.626,-2.4075],[-70.674,-2.4105],[-70.6615,-2.363],[-70.7615,-2.3205],[-70.768,-2.298],[-70.843,-2.2855],[-70.8635,-2.226],[-70.898,-2.224],[-70.9415,-2.2555],[-70.978,-2.2125],[-71.046,-2.265],[-71.1435,-2.297],[-71.1715,-2.3745],[-71.1935,-2.3825],[-71.231,-2.336],[-71.266,-2.377],[-71.3535,-2.3795],[-71.392,-2.355],[-71.4675,-2.272],[-71.599,-2.239],[-71.6375,-2.202],[-71.713,-2.23],[-71.718,-2.1665],[-71.829,-2.1875],[-71.872,-2.2965],[-71.927,-2.3085],[-71.9695,-2.3675],[-72.0055,-2.3705],[-72.06,-2.343],[-72.129,-2.4095],[-72.227,-2.447],[-72.2595,-2.432],[-72.2985,-2.4745],[-72.3685,-2.491],[-72.38,-2.453],[-72.5765,-2.3935],[-72.6065,-2.3625],[-72.6865,-2.4205],[-72.735,-2.424],[-72.777,-2.3855],[-72.8575,-2.4395],[-72.9315,-2.418],[-73.003,-2.3605],[-73.0435,-2.3575],[-73.096,-2.324],[-73.155,-2.2585],[-73.173,-2.195],[-73.1255,-2.173],[-73.1165,-2.1305],[-73.0705,-2.1055],[-73.1135,-2.072],[-73.092,-2.0415],[-73.1295,-1.9945],[-73.1115,-1.95],[-73.11,-1.877],[-73.1505,-1.863],[-73.1465,-1.8055],[-73.1945,-1.7885],[-73.212,-1.752],[-73.252,-1.7805],[-73.3,-1.7775],[-73.3345,-1.802],[-73.3775,-1.776],[-73.4325,-1.7875],[-73.466,-1.735],[-73.5385,-1.7005],[-73.5165,-1.64],[-73.463,-1.589],[-73.4905,-1.571],[-73.491,-1.4835],[-73.531,-1.4375],[-73.5815,-1.346],[-73.611,-1.3185],[-73.6195,-1.264],[-73.674,-1.2405],[-73.853,-1.2335],[-73.8955,-1.1695],[-73.904,-1.1245],[-73.958,-1.131],[-74.006,-1.099],[-74.027,-1.0615],[-74.126,-1.0325],[-74.1725,-1.0055],[-74.2255,-1.0085],[-74.2725,-0.979],[-74.262,-0.941],[-74.2795,-0.899],[-74.2655,-0.8415],[-74.318,-0.778],[-74.37,-0.737],[-74.338,-0.6775],[-74.374,-0.6535],[-74.3865,-0.555],[-74.4245,-0.55],[-74.4195,-0.504],[-74.487,-0.4885],[-74.537,-0.4585],[-74.544,-0.419],[-74.6115,-0.351],[-74.676,-0.3655],[-74.732,-0.334],[-74.729,-0.2985],[-74.7615,-0.2235],[-74.7935,-0.189],[-74.845,-0.231],[-74.8835,-0.2435],[-74.931,-0.2215],[-74.9725,-0.155],[-75.012,-0.1485],[-75.0775,-0.082],[-75.1315,-0.0705],[-75.162,-0.0445],[-75.2095,-0.046],[-75.258,-0.116],[-75.3045,-0.154],[-75.4035,-0.17],[-75.504,-0.127],[-75.567,-0.1305],[-75.6105,-0.1135],[-75.6105,-0.1925],[-75.531,-0.181],[-75.482,-0.237],[-75.4735,-0.297],[-75.3855,-0.437],[-75.3275,-0.4595],[-75.313,-0.4945],[-75.2415,-0.5245],[-75.2235,-0.629],[-75.2575,-0.6435],[-75.2515,-0.6915],[-75.2775,-0.73],[-75.243,-0.7975],[-75.225,-0.8625],[-75.2315,-0.939],[-75.195,-0.9705],[-75.26,-0.989],[-75.3135,-0.9845],[-75.386,-0.9375],[-75.5555,-1.547],[-75.5865,-1.5535],[-76.0445,-2.125],[-76.6325,-2.591],[-77.1745,-2.7715],[-77.7105,-2.951],[-77.844,-3.0005],[-77.944,-3.0655],[-77.971,-3.118],[-78.0325,-3.189],[-78.0785,-3.2265],[-78.107,-3.2765],[-78.17,-3.35],[-78.1975,-3.365],[-78.155,-3.4265],[-78.148,-3.479],[-78.229,-3.5055],[-78.248,-3.398],[-78.324,-3.391],[-78.359,-3.4695],[-78.365,-3.5295],[-78.3875,-3.555],[-78.401,-3.6515],[-78.4225,-3.691],[-78.4025,-3.7355],[-78.414,-3.7925],[-78.4765,-3.8585],[-78.4885,-3.9335],[-78.5245,-3.9375],[-78.566,-3.9935],[-78.5625,-4.05],[-78.541,-4.074],[-78.5815,-4.1355],[-78.5785,-4.198],[-78.5985,-4.208],[-78.618,-4.294],[-78.6525,-4.3325],[-78.634,-4.415],[-78.6375,-4.501],[-78.661,-4.53],[-78.661,-4.586],[-78.706,-4.624],[-78.805,-4.638],[-78.8425,-4.6545],[-78.889,-4.715],[-78.891,-4.8085],[-78.917,-4.892],[-78.9725,-4.894],[-79.0125,-5.012],[-79.076,-4.97],[-79.102,-4.9785],[-79.1805,-4.9605],[-79.2655,-4.967],[-79.3065,-4.901],[-79.352,-4.8805],[-79.361,-4.844],[-79.399,-4.829],[-79.4315,-4.737],[-79.458,-4.702],[-79.463,-4.6545],[-79.488,-4.6275],[-79.477,-4.5685],[-79.505,-4.5175],[-79.5445,-4.525],[-79.6175,-4.4455],[-79.6555,-4.434],[-79.745,-4.483],[-79.8135,-4.492],[-79.8715,-4.413],[-79.92,-4.386],[-79.9525,-4.398],[-80.0335,-4.3495],[-80.0615,-4.311],[-80.1065,-4.2915],[-80.1555,-4.304],[-80.2825,-4.4065],[-80.3295,-4.4655],[-80.3885,-4.486],[-80.4405,-4.4555],[-80.4535,-4.3825],[-80.412,-4.316],[-80.3675,-4.28],[-80.3315,-4.2255],[-80.372,-4.198],[-80.41,-4.218],[-80.45,-4.2095],[-80.449,-4.125],[-80.4845,-4.09],[-80.469,-4.038],[-80.398,-3.981],[-80.3385,-3.993],[-80.299,-4.0175],[-80.1865,-3.929],[-80.14,-3.913],[-80.1585,-3.8715],[-80.159,-3.809],[-80.182,-3.7855],[-80.1995,-3.689],[-80.1855,-3.648],[-80.212,-3.592],[-80.2065,-3.5345],[-80.243,-3.4665],[-80.2495,-3.4125],[-80.3065,-3.3925],[-80.671,-3.392],[-81.3445,-3.392]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/cf/Flag_of_Peru.svg","name:en":"Peru","wikidata":"Q419","ISO3166-1:alpha2":"PE","ISO3166-1:alpha3":"PER","ISO3166-1:numeric":"604"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-87.663,13.1275],[-87.72,13.109],[-87.811,13.055],[-87.9015,12.976],[-87.834,12.8865],[-87.7415,12.7815],[-87.588,12.676],[-87.4175,12.499],[-87.291,12.4025],[-87.237,12.335],[-86.8955,12.137],[-86.7995,11.924],[-86.742,11.852],[-86.73,11.8095],[-86.6405,11.7175],[-86.627,11.684],[-86.454,11.5415],[-86.436,11.512],[-86.2605,11.3955],[-85.898,11.076],[-85.706,11.069],[-85.693,11.079],[-85.61,11.2195],[-85.5605,11.21],[-85.5235,11.168],[-85.4235,11.129],[-85.357,11.126],[-85.23,11.064],[-84.91,10.9435],[-84.6805,11.0845],[-84.606,11.0385],[-84.485,11.0],[-84.4205,10.9555],[-84.3585,10.996],[-84.319,10.916],[-84.226,10.8745],[-84.222,10.8005],[-84.14,10.7875],[-84.0705,10.76],[-84.006,10.7665],[-83.9685,10.731],[-83.9135,10.711],[-83.8625,10.7175],[-83.7945,10.7675],[-83.671,10.797],[-83.667,10.8815],[-83.7035,10.9425],[-83.658,11.0295],[-83.695,11.068],[-83.77,11.283],[-83.77,11.3625],[-83.7185,11.3915],[-83.6975,11.461],[-83.624,11.5055],[-83.5675,11.564],[-83.5455,11.6145],[-83.6165,11.955],[-83.563,12.019],[-83.5565,12.2835],[-83.465,12.293],[-83.373,12.3455],[-83.3915,12.452],[-83.439,12.6245],[-83.411,12.912],[-83.4745,13.295],[-83.4515,13.4715],[-83.4305,13.5345],[-83.412,13.7475],[-83.3605,13.855],[-83.2955,13.97],[-83.1865,14.113],[-83.0975,14.3125],[-83.1085,14.3885],[-83.194,14.6645],[-83.2135,14.743],[-83.21,14.815],[-83.188,14.8745],[-83.0425,14.9535],[-83.046,15.033],[-83.1415,14.998],[-83.2355,14.9825],[-83.385,15.0165],[-83.459,14.981],[-83.5015,15.011],[-83.541,14.975],[-83.529,14.951],[-83.5775,14.912],[-83.7,14.858],[-83.8895,14.765],[-83.9285,14.7785],[-83.9715,14.7475],[-84.0395,14.7655],[-84.0955,14.7315],[-84.1885,14.716],[-84.2355,14.7605],[-84.2735,14.7235],[-84.2585,14.675],[-84.297,14.663],[-84.3755,14.6995],[-84.434,14.632],[-84.496,14.6195],[-84.5435,14.6545],[-84.6295,14.6745],[-84.7035,14.6665],[-84.745,14.7155],[-84.752,14.7735],[-84.7995,14.822],[-84.9005,14.8145],[-84.9425,14.754],[-84.987,14.74],[-85.0275,14.696],[-85.0215,14.6075],[-85.0425,14.575],[-85.096,14.5525],[-85.147,14.5765],[-85.1385,14.519],[-85.183,14.4695],[-85.181,14.4285],[-85.2055,14.3865],[-85.1985,14.3475],[-85.159,14.288],[-85.1985,14.2565],[-85.3415,14.247],[-85.404,14.123],[-85.4985,14.0855],[-85.536,14.046],[-85.592,14.0425],[-85.626,14.0085],[-85.6645,14.011],[-85.7535,13.968],[-85.7665,13.8495],[-85.8265,13.859],[-85.8455,13.9175],[-85.9125,13.926],[-86.042,13.996],[-86.0085,14.0555],[-86.076,14.07],[-86.1525,14.0355],[-86.1405,14.006],[-86.227,13.9065],[-86.2785,13.8755],[-86.3325,13.769],[-86.417,13.759],[-86.5225,13.781],[-86.723,13.787],[-86.7655,13.7665],[-86.7605,13.7255],[-86.786,13.662],[-86.749,13.6395],[-86.7695,13.582],[-86.7495,13.5655],[-86.741,13.4865],[-86.722,13.4325],[-86.744,13.4065],[-86.7105,13.359],[-86.706,13.303],[-86.7545,13.2675],[-86.829,13.3135],[-86.9085,13.269],[-86.931,13.19],[-86.9255,13.083],[-86.955,13.039],[-87.0275,13.003],[-87.3085,12.9875],[-87.3945,13.004],[-87.4795,13.078],[-87.543,13.1675],[-87.663,13.1275]]],[[[-83.1815,12.156],[-83.159,12.084],[-83.083,12.048],[-83.006,12.0495],[-82.932,12.1265],[-82.92,12.1815],[-82.887,12.2065],[-82.8615,12.2815],[-82.8935,12.3655],[-82.938,12.4035],[-83.046,12.3935],[-83.088,12.36],[-83.099,12.2845],[-83.13,12.269],[-83.1745,12.202],[-83.1815,12.156]]],[[[-82.928,14.3855],[-82.927,14.3355],[-82.896,14.277],[-82.8545,14.252],[-82.726,14.2455],[-82.67,14.2755],[-82.632,14.331],[-82.628,14.4315],[-82.6515,14.482],[-82.702,14.5195],[-82.796,14.525],[-82.883,14.473],[-82.928,14.3855]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/19/Flag_of_Nicaragua.svg","name:en":"Nicaragua","wikidata":"Q811","ISO3166-1:alpha2":"NI","ISO3166-1:alpha3":"NIC","ISO3166-1:numeric":"558"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-78.578,18.269],[-78.577,18.241],[-78.3985,16.768],[-78.3765,16.698],[-78.33,16.6405],[-78.265,16.603],[-78.1905,16.59],[-78.1165,16.6035],[-77.456,16.849],[-75.953,17.188],[-75.872,17.218],[-75.8305,17.247],[-75.7735,17.3225],[-75.754,17.409],[-75.7695,17.487],[-75.986,17.9935],[-76.1585,18.26],[-76.203,18.3095],[-76.27,18.3495],[-76.8085,18.5935],[-76.866,18.6105],[-77.282,18.671],[-77.7945,18.7235],[-77.9325,18.7155],[-78.2555,18.652],[-78.3565,18.605],[-78.486,18.5095],[-78.549,18.4055],[-78.578,18.269]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/0a/Flag_of_Jamaica.svg","name:en":"Jamaica","wikidata":"Q766","ISO3166-1:alpha2":"JM","ISO3166-1:alpha3":"JAM","ISO3166-1:numeric":"388"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-11.377,12.4125],[-11.4365,12.3825],[-11.4305,12.283],[-11.4955,12.2135],[-11.493,12.179],[-11.4545,12.134],[-11.367,12.1075],[-11.3585,12.0665],[-11.311,12.024],[-11.225,11.998],[-11.1755,12.024],[-11.0735,12.137],[-11.0475,12.2215],[-10.9855,12.2155],[-10.9225,12.2255],[-10.841,12.1305],[-10.792,12.1155],[-10.8015,12.0415],[-10.765,11.9715],[-10.7735,11.9475],[-10.7075,11.909],[-10.634,11.906],[-10.609,11.9745],[-10.5675,11.9895],[-10.5795,12.025],[-10.5265,12.0375],[-10.513,12.126],[-10.453,12.12],[-10.41,12.145],[-10.396,12.1825],[-10.3585,12.178],[-10.342,12.223],[-10.1925,12.2055],[-10.149,12.1765],[-10.091,12.1695],[-9.968,12.103],[-9.914,12.104],[-9.872,12.058],[-9.8295,12.057],[-9.7445,12.0305],[-9.6885,12.0345],[-9.683,12.1185],[-9.6465,12.1685],[-9.572,12.1975],[-9.5325,12.199],[-9.4845,12.257],[-9.4225,12.2645],[-9.3735,12.2505],[-9.3185,12.281],[-9.302,12.367],[-9.3795,12.4305],[-9.3555,12.5015],[-9.284,12.4855],[-9.25,12.506],[-9.135,12.48],[-9.0835,12.441],[-9.0145,12.4205],[-9.007,12.396],[-8.9335,12.325],[-8.9885,12.239],[-8.971,12.1925],[-8.918,12.1975],[-8.8835,12.1515],[-8.9115,12.0945],[-8.9065,12.0575],[-8.8625,12.0305],[-8.8285,12.0395],[-8.772,11.977],[-8.7985,11.8285],[-8.82,11.781],[-8.849,11.6545],[-8.751,11.6245],[-8.7015,11.6455],[-8.6415,11.548],[-8.655,11.523],[-8.601,11.4755],[-8.52,11.484],[-8.498,11.4165],[-8.372,11.3885],[-8.404,11.332],[-8.3535,11.3155],[-8.391,11.276],[-8.4655,11.29],[-8.4735,11.2315],[-8.5505,11.2115],[-8.559,11.138],[-8.6045,11.1305],[-8.601,11.0955],[-8.6345,11.039],[-8.675,11.0035],[-8.665,10.953],[-8.5795,10.9605],[-8.5115,10.999],[-8.481,11.0715],[-8.3425,11.0625],[-8.2785,10.994],[-8.26,10.8915],[-8.306,10.8565],[-8.3035,10.818],[-8.3295,10.7715],[-8.2895,10.758],[-8.2885,10.6985],[-8.32,10.674],[-8.325,10.5905],[-8.2875,10.5955],[-8.246,10.5135],[-8.2305,10.4195],[-8.1825,10.415],[-8.1005,10.4495],[-8.105,10.3935],[-8.071,10.341],[-7.985,10.3435],[-7.945,10.2525],[-7.9795,10.1705],[-7.932,10.1545],[-7.8925,10.2],[-7.8545,10.2005],[-7.8005,10.2445],[-7.754,10.3415],[-7.7215,10.3485],[-7.718,10.3955],[-7.626,10.462],[-7.55,10.4125],[-7.514,10.4605],[-7.4495,10.4445],[-7.4525,10.4015],[-7.421,10.342],[-7.3585,10.3345],[-7.363,10.2745],[-7.3415,10.236],[-7.2705,10.2595],[-7.1415,10.2495],[-7.0755,10.1965],[-7.049,10.148],[-6.995,10.1725],[-6.977,10.2165],[-7.013,10.2425],[-6.945,10.3505],[-6.8675,10.344],[-6.789,10.371],[-6.6785,10.3385],[-6.648,10.371],[-6.626,10.439],[-6.6625,10.457],[-6.682,10.528],[-6.661,10.5885],[-6.669,10.6255],[-6.6335,10.6735],[-6.59,10.623],[-6.505,10.5795],[-6.4215,10.574],[-6.3975,10.5985],[-6.415,10.698],[-6.395,10.709],[-6.3165,10.6855],[-6.246,10.74],[-6.2105,10.569],[-6.242,10.5445],[-6.193,10.4975],[-6.181,10.4115],[-6.1635,10.3665],[-6.2305,10.305],[-6.218,10.264],[-6.178,10.227],[-6.108,10.205],[-5.9845,10.1985],[-5.9495,10.274],[-5.9015,10.263],[-5.874,10.3145],[-5.874,10.3555],[-5.8075,10.419],[-5.7655,10.439],[-5.704,10.431],[-5.6495,10.468],[-5.621,10.452],[-5.5625,10.464],[-5.513,10.431],[-5.508,10.461],[-5.4575,10.556],[-5.463,10.728],[-5.431,10.778],[-5.4025,10.8545],[-5.4455,10.8945],[-5.452,10.976],[-5.4895,11.0815],[-5.392,11.1025],[-5.343,11.1295],[-5.3265,11.201],[-5.256,11.244],[-5.255,11.359],[-5.2425,11.3965],[-5.1985,11.429],[-5.21,11.4575],[-5.2,11.528],[-5.24,11.6045],[-5.294,11.617],[-5.268,11.6765],[-5.2595,11.736],[-5.285,11.7765],[-5.3555,11.825],[-5.319,11.8415],[-5.245,11.84],[-5.1725,11.927],[-5.127,11.957],[-4.9275,12.005],[-4.843,12.0145],[-4.7405,11.999],[-4.7005,12.066],[-4.63,12.0595],[-4.6285,12.1135],[-4.6075,12.139],[-4.544,12.1415],[-4.569,12.2085],[-4.4715,12.286],[-4.472,12.332],[-4.3965,12.3105],[-4.439,12.4045],[-4.419,12.487],[-4.3645,12.533],[-4.4025,12.5605],[-4.4115,12.61],[-4.4805,12.6585],[-4.453,12.7165],[-4.3995,12.7185],[-4.347,12.744],[-4.3245,12.7115],[-4.2675,12.7045],[-4.233,12.734],[-4.209,12.8165],[-4.226,12.866],[-4.2155,12.92],[-4.2255,12.974],[-4.2645,12.99],[-4.275,13.0395],[-4.346,13.114],[-4.34,13.143],[-4.259,13.2335],[-4.2075,13.2695],[-4.1405,13.2935],[-4.1075,13.3345],[-4.1005,13.387],[-4.053,13.407],[-3.9595,13.473],[-3.9135,13.3825],[-3.8195,13.3605],[-3.798,13.372],[-3.7225,13.2925],[-3.6725,13.275],[-3.5875,13.201],[-3.5235,13.166],[-3.4635,13.156],[-3.4225,13.1785],[-3.4375,13.2765],[-3.3385,13.288],[-3.234,13.289],[-3.255,13.3655],[-3.278,13.5575],[-3.2505,13.5855],[-3.247,13.682],[-3.108,13.689],[-3.0295,13.6145],[-3.0075,13.6545],[-2.9525,13.6245],[-2.874,13.654],[-2.9065,13.7335],[-2.9065,13.821],[-2.8805,13.867],[-2.836,13.993],[-2.839,14.0545],[-2.806,14.077],[-2.6765,14.135],[-2.589,14.216],[-2.4735,14.2995],[-2.2975,14.249],[-2.178,14.192],[-2.1035,14.147],[-1.998,14.1915],[-1.9805,14.4755],[-1.907,14.4895],[-1.7775,14.4825],[-1.678,14.5015],[-1.3185,14.73],[-1.093,14.785],[-1.069,14.785],[-0.724,15.084],[-0.4315,15.0835],[-0.39,15.004],[-0.2455,15.079],[0.0615,14.9745],[0.1295,14.978],[0.232,15.0],[0.3945,14.964],[0.5145,14.9975],[0.5725,14.989],[0.6875,14.9445],[0.7335,14.96],[0.9065,14.972],[1.0,15.0],[1.3115,15.278],[1.8495,15.3065],[2.166,15.3225],[2.6365,15.347],[3.02,15.338],[3.0315,15.4285],[3.493,15.358],[3.5365,15.4035],[3.537,15.485],[3.5915,15.517],[3.645,15.586],[3.7225,15.659],[3.798,15.6635],[3.856,15.6865],[3.901,15.747],[3.928,15.8645],[3.9775,15.935],[3.9935,16.0295],[3.986,16.081],[4.063,16.2225],[4.097,16.323],[4.1185,16.35],[4.2,16.395],[4.1975,16.815],[4.2065,16.837],[4.2145,17.0],[4.2665,17.0015],[4.266,17.388],[4.2675,17.92],[4.2665,18.2855],[4.267,18.7635],[4.2665,19.144],[3.8105,19.0585],[3.3575,18.968],[3.2945,19.003],[3.276,19.0335],[3.1635,19.1145],[3.1245,19.1255],[3.1225,19.185],[3.2005,19.277],[3.1785,19.359],[3.208,19.431],[3.263,19.439],[3.248,19.498],[3.265,19.5305],[3.2325,19.6055],[3.2505,19.6495],[3.23,19.711],[3.2595,19.8175],[3.2375,19.842],[3.145,19.8585],[3.0765,19.9055],[2.977,19.953],[2.857,19.973],[2.8235,19.9645],[2.723,20.006],[2.701,20.087],[2.666,20.052],[2.562,20.033],[2.525,20.089],[2.4745,20.098],[2.3975,20.061],[2.4015,20.1175],[2.3565,20.1905],[2.318,20.218],[2.3125,20.2695],[2.2735,20.299],[2.194,20.3155],[2.192,20.28],[2.1335,20.2445],[2.084,20.244],[2.037,20.2695],[2.0025,20.2425],[1.947,20.26],[1.8975,20.243],[1.884,20.2945],[1.798,20.298],[1.775,20.337],[1.668,20.4065],[1.675,20.4795],[1.6425,20.56],[1.591,20.6005],[1.5035,20.6225],[1.353,20.677],[1.3165,20.74],[1.1785,20.7265],[1.164,20.7905],[1.1915,20.924],[1.192,21.0035],[1.1665,21.1165],[0.9095,21.2825],[0.505,21.5445],[-0.061,21.911],[-0.52,22.208],[-0.923,22.469],[-1.304,22.7155],[-1.668,22.951],[-2.054,23.201],[-2.5945,23.5505],[-2.987,23.805],[-3.602,24.203],[-4.0945,24.5215],[-4.4805,24.7715],[-4.8335,25.0],[-5.553,25.001],[-6.0,25.0005],[-6.572,25.0],[-6.4995,24.356],[-6.5485,24.354],[-6.4845,24.222],[-6.4565,23.972],[-6.4055,23.568],[-6.3585,23.201],[-6.3345,22.9635],[-6.2805,22.5],[-6.2175,21.9615],[-6.178,21.618],[-6.1045,20.969],[-6.0595,20.569],[-6.0005,20.041],[-5.943,19.511],[-5.9075,19.187],[-5.8265,18.4875],[-5.7715,18.0],[-5.705,17.3975],[-5.6615,17.0005],[-5.609,16.5],[-5.3335,16.3335],[-5.3885,16.057],[-5.5,15.5],[-6.189,15.5],[-6.731,15.5],[-7.429,15.5],[-8.0,15.5],[-8.509,15.5005],[-9.0,15.5005],[-9.3315,15.5015],[-9.3315,15.5655],[-9.3095,15.6935],[-9.331,15.703],[-9.447,15.6055],[-9.401,15.439],[-9.685,15.427],[-9.722,15.4075],[-9.781,15.414],[-9.799,15.389],[-9.9225,15.4],[-10.065,15.3615],[-10.1375,15.3765],[-10.1675,15.402],[-10.3205,15.443],[-10.3325,15.4345],[-10.519,15.451],[-10.639,15.42],[-10.705,15.4435],[-10.7285,15.4275],[-10.7155,15.375],[-10.732,15.3435],[-10.8,15.307],[-10.829,15.2705],[-10.833,15.2155],[-10.867,15.1985],[-10.882,15.12],[-10.954,15.16],[-10.993,15.232],[-11.2045,15.399],[-11.2515,15.4195],[-11.3395,15.526],[-11.411,15.637],[-11.4925,15.6415],[-11.517,15.5935],[-11.5605,15.5835],[-11.581,15.55],[-11.64,15.521],[-11.71,15.533],[-11.7045,15.492],[-11.7435,15.461],[-11.767,15.336],[-11.806,15.278],[-11.7995,15.2325],[-11.836,15.1845],[-11.812,15.1315],[-11.8445,15.096],[-11.789,15.03],[-11.8395,14.858],[-11.9355,14.8125],[-11.9375,14.7805],[-12.025,14.7635],[-12.0555,14.721],[-12.119,14.7545],[-12.2405,14.7645],[-12.2145,14.7055],[-12.146,14.6525],[-12.189,14.5545],[-12.2165,14.55],[-12.22,14.49],[-12.19,14.443],[-12.2005,14.4065],[-12.1025,14.375],[-12.092,14.3045],[-12.024,14.2835],[-11.989,14.195],[-11.9825,14.09],[-12.0145,14.0285],[-12.009,13.986],[-11.933,13.9245],[-11.949,13.8115],[-12.026,13.743],[-12.072,13.721],[-12.04,13.6665],[-12.0315,13.6165],[-11.9535,13.522],[-11.868,13.4585],[-11.8875,13.394],[-11.8285,13.309],[-11.7665,13.342],[-11.742,13.39],[-11.6275,13.3945],[-11.596,13.365],[-11.591,13.314],[-11.525,13.2595],[-11.5415,13.2325],[-11.5185,13.157],[-11.4355,13.0625],[-11.396,13.0055],[-11.355,12.976],[-11.346,12.9435],[-11.3975,12.93],[-11.4105,12.8485],[-11.3805,12.8035],[-11.383,12.743],[-11.4235,12.729],[-11.4235,12.664],[-11.4035,12.5875],[-11.4105,12.537],[-11.3585,12.508],[-11.352,12.4675],[-11.377,12.4125]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/92/Flag_of_Mali.svg","name:en":"Mali","wikidata":"Q912","ISO3166-1:alpha2":"ML","ISO3166-1:alpha3":"MLI","ISO3166-1:numeric":"466"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-4.8685,54.434],[-4.8965,54.391],[-4.9885,54.336],[-5.0365,54.286],[-5.0815,54.189],[-5.147,54.127],[-5.1655,54.018],[-5.116,53.942],[-5.0115,53.878],[-4.829,53.845],[-4.6195,53.854],[-4.4865,53.871],[-4.4175,53.895],[-4.132,54.06],[-4.0675,54.125],[-3.9795,54.24],[-3.9655,54.302],[-4.0175,54.404],[-4.0345,54.479],[-4.1535,54.5505],[-4.4005,54.5465],[-4.6725,54.4995],[-4.8685,54.434]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/bc/Flag_of_the_Isle_of_Man.svg","name:en":"Isle of Man","wikidata":"Q9676","ISO3166-1:alpha2":"IM","ISO3166-1:alpha3":"IMN","ISO3166-1:numeric":"833"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[33.2165,-13.9995],[33.3,-14.0315],[33.322,-14.0845],[33.2975,-14.146],[33.3415,-14.2165],[33.3815,-14.2305],[33.445,-14.328],[33.4495,-14.365],[33.512,-14.4235],[33.545,-14.4345],[33.566,-14.4815],[33.6075,-14.4915],[33.634,-14.587],[33.6795,-14.616],[33.708,-14.578],[33.7165,-14.5015],[33.7405,-14.501],[33.799,-14.5515],[33.9155,-14.4775],[34.087,-14.4895],[34.085,-14.4585],[34.239,-14.425],[34.301,-14.3985],[34.391,-14.395],[34.4755,-14.5325],[34.5155,-14.5545],[34.551,-14.649],[34.5205,-14.687],[34.5835,-14.809],[34.58,-14.908],[34.618,-15.015],[34.573,-15.0675],[34.578,-15.2005],[34.6035,-15.238],[34.5985,-15.287],[34.519,-15.3535],[34.521,-15.3845],[34.4255,-15.476],[34.455,-15.611],[34.437,-15.6595],[34.388,-15.6925],[34.3645,-15.7425],[34.2995,-15.761],[34.2505,-15.8425],[34.259,-15.906],[34.355,-15.9695],[34.389,-16.027],[34.4295,-16.0445],[34.3975,-16.1855],[34.4195,-16.25],[34.489,-16.2955],[34.52,-16.2805],[34.5755,-16.3265],[34.594,-16.4175],[34.6525,-16.453],[34.6895,-16.505],[34.7215,-16.497],[34.775,-16.544],[34.7875,-16.5855],[34.8435,-16.6085],[34.8585,-16.676],[34.9095,-16.6935],[34.919,-16.746],[34.9845,-16.749],[34.9905,-16.7895],[35.0635,-16.8355],[35.113,-16.818],[35.1575,-16.84],[35.1675,-16.9215],[35.1355,-16.9715],[35.063,-16.994],[35.0565,-17.0495],[35.0885,-17.0785],[35.0975,-17.1295],[35.2955,-17.128],[35.2965,-16.9645],[35.2635,-16.939],[35.3145,-16.831],[35.2755,-16.706],[35.167,-16.6195],[35.146,-16.557],[35.213,-16.5],[35.262,-16.475],[35.263,-16.399],[35.303,-16.3455],[35.2785,-16.326],[35.3135,-16.2075],[35.4055,-16.125],[35.4815,-16.1235],[35.5025,-16.1555],[35.6125,-16.1245],[35.6615,-16.1],[35.704,-16.1025],[35.8175,-16.0275],[35.8065,-15.992],[35.853,-15.419],[35.79,-15.17],[35.9185,-14.8955],[35.872,-14.8955],[35.872,-14.6755],[35.5205,-14.259],[35.487,-14.1705],[35.2275,-13.881],[35.0895,-13.6995],[34.927,-13.5465],[34.867,-13.483],[34.607,-13.483],[34.5615,-13.348],[34.576,-13.232],[34.5305,-12.88],[34.535,-12.7365],[34.5205,-12.6825],[34.4555,-12.571],[34.43,-12.401],[34.4175,-12.2655],[34.378,-12.1715],[34.484,-11.9725],[34.591,-11.856],[34.6345,-11.739],[34.6435,-11.656],[34.637,-11.5735],[34.959,-11.5735],[34.956,-11.4915],[34.9305,-11.4625],[34.928,-11.3985],[34.7895,-11.3135],[34.8005,-11.281],[34.7565,-11.2085],[34.638,-11.118],[34.6375,-11.053],[34.612,-11.029],[34.6545,-10.9535],[34.6705,-10.8945],[34.681,-10.7515],[34.625,-10.597],[34.584,-10.5725],[34.568,-10.5285],[34.5935,-10.49],[34.56,-10.4325],[34.5845,-10.3035],[34.565,-10.258],[34.5695,-10.2065],[34.5195,-10.119],[34.54,-10.0665],[34.416,-9.885],[34.345,-9.8],[34.3265,-9.7575],[34.209,-9.6455],[34.038,-9.4935],[33.9785,-9.522],[33.9455,-9.5725],[33.9555,-9.654],[33.9165,-9.713],[33.871,-9.664],[33.8125,-9.63],[33.7735,-9.5865],[33.7315,-9.5845],[33.699,-9.6145],[33.554,-9.5875],[33.508,-9.626],[33.4435,-9.6145],[33.3905,-9.5335],[33.326,-9.486],[33.2135,-9.506],[33.1435,-9.4925],[33.022,-9.4],[32.9925,-9.3685],[32.9535,-9.4015],[32.9475,-9.487],[33.0145,-9.5025],[32.994,-9.5625],[32.9965,-9.626],[33.0605,-9.616],[33.1065,-9.659],[33.1135,-9.5865],[33.201,-9.601],[33.2275,-9.6295],[33.2425,-9.7055],[33.3065,-9.804],[33.35,-9.815],[33.391,-9.912],[33.355,-9.934],[33.3175,-10.0555],[33.433,-10.1165],[33.467,-10.158],[33.5555,-10.212],[33.561,-10.239],[33.5445,-10.342],[33.5725,-10.401],[33.6335,-10.458],[33.633,-10.4985],[33.6725,-10.5375],[33.6835,-10.6085],[33.598,-10.658],[33.5355,-10.7525],[33.4535,-10.808],[33.418,-10.794],[33.336,-10.821],[33.3165,-10.8615],[33.277,-10.8495],[33.259,-10.889],[33.305,-10.939],[33.328,-11.0705],[33.4045,-11.156],[33.3805,-11.234],[33.3545,-11.255],[33.3335,-11.311],[33.232,-11.444],[33.256,-11.475],[33.2605,-11.539],[33.2455,-11.5915],[33.301,-11.592],[33.331,-11.612],[33.327,-11.6975],[33.3405,-11.823],[33.3285,-11.8955],[33.3485,-11.93],[33.3325,-11.9875],[33.2735,-12.066],[33.257,-12.14],[33.3135,-12.202],[33.3115,-12.2485],[33.376,-12.348],[33.4755,-12.3255],[33.531,-12.34],[33.5485,-12.364],[33.399,-12.516],[33.3755,-12.554],[33.3225,-12.533],[33.272,-12.5485],[33.232,-12.595],[33.1815,-12.6135],[33.0575,-12.5945],[32.9525,-12.7615],[32.946,-12.8465],[32.976,-12.886],[33.0175,-12.8925],[33.017,-12.954],[32.99,-13.037],[32.984,-13.14],[33.008,-13.1935],[32.9305,-13.325],[32.932,-13.3735],[32.8985,-13.4495],[32.86,-13.469],[32.8475,-13.526],[32.7575,-13.5735],[32.6975,-13.559],[32.6705,-13.5745],[32.6785,-13.6255],[32.7185,-13.651],[32.7835,-13.6495],[32.843,-13.722],[32.7725,-13.7695],[32.8075,-13.8005],[32.844,-13.7935],[32.907,-13.8255],[32.9545,-13.8885],[32.9685,-13.9375],[32.998,-13.943],[32.994,-14.007],[33.041,-14.0495],[33.0865,-13.9705],[33.145,-13.939],[33.186,-13.959],[33.2165,-13.9995]]],[[[34.5725,-12.014],[34.5845,-12.069],[34.6195,-12.085],[34.6625,-12.041],[34.64,-11.982],[34.605,-11.9745],[34.5725,-12.014]]],[[[34.675,-12.0845],[34.7065,-12.1365],[34.7825,-12.0815],[34.794,-12.0425],[34.742,-11.9995],[34.706,-12.008],[34.675,-12.0845]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d1/Flag_of_Malawi.svg","name:en":"Malawi","wikidata":"Q1020","ISO3166-1:alpha2":"MW","ISO3166-1:alpha3":"MWI","ISO3166-1:numeric":"454"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[39.647,-4.681],[39.7125,-4.551],[39.9,-4.145],[39.963,-4.038],[40.336,-3.37],[40.3545,-3.324],[40.4185,-3.0855],[40.751,-2.7635],[41.3265,-2.271],[41.6505,-1.912],[41.718,-1.803],[41.5595,-1.662],[41.559,-1.5985],[41.2765,-1.2165],[40.9935,-0.834],[40.9935,-0.1995],[40.9945,0.5165],[40.9945,0.9305],[40.9935,1.231],[40.9915,2.1765],[40.989,2.8285],[41.1465,2.9855],[41.312,3.141],[41.575,3.5245],[41.7165,3.7155],[41.907,3.9825],[41.8445,3.949],[41.7235,3.987],[41.6745,3.96],[41.6315,3.9825],[41.567,3.9665],[41.5505,3.9815],[41.4795,3.952],[41.427,3.9455],[41.3915,3.963],[41.33,3.9395],[41.271,3.958],[41.1695,3.9425],[41.1055,3.9855],[41.092,4.0135],[40.965,4.132],[40.904,4.1545],[40.8825,4.213],[40.844,4.2465],[40.759,4.279],[40.7175,4.245],[40.652,4.225],[40.376,4.1065],[40.172,4.0275],[39.8695,3.8745],[39.787,3.702],[39.7585,3.659],[39.606,3.503],[39.588,3.492],[39.554,3.3975],[39.515,3.407],[39.498,3.4625],[39.4295,3.451],[39.191,3.4775],[39.0875,3.539],[39.0215,3.51],[38.9065,3.5125],[38.7145,3.5725],[38.6655,3.593],[38.58,3.604],[38.551,3.642],[38.4485,3.602],[38.1915,3.62],[38.13,3.606],[37.994,3.7295],[37.9025,3.7835],[37.7075,3.9105],[37.5015,4.0565],[37.137,4.293],[37.09,4.341],[37.0315,4.3795],[36.988,4.3895],[36.8445,4.4475],[36.681,4.439],[36.6455,4.4515],[36.5475,4.442],[36.234,4.449],[36.0475,4.4465],[35.9645,4.5275],[35.944,4.619],[35.6985,4.6185],[35.608,4.618],[35.5085,4.617],[34.8755,4.613],[34.3875,4.61],[34.0565,4.282],[33.988,4.234],[34.0475,4.179],[34.0495,4.121],[34.0915,4.0615],[34.059,4.0275],[34.1345,3.9615],[34.128,3.872],[34.2155,3.881],[34.2455,3.784],[34.308,3.7115],[34.331,3.733],[34.3825,3.7275],[34.405,3.6925],[34.463,3.6695],[34.4515,3.517],[34.396,3.488],[34.4195,3.4335],[34.4005,3.3705],[34.43,3.3425],[34.4475,3.283],[34.456,3.182],[34.546,3.1365],[34.5725,3.0915],[34.5765,3.0205],[34.599,2.9245],[34.6395,2.9065],[34.653,2.8675],[34.7365,2.855],[34.767,2.811],[34.7835,2.755],[34.7755,2.698],[34.852,2.5835],[34.8975,2.588],[34.913,2.5165],[34.9525,2.461],[34.9165,2.4205],[34.9455,2.2115],[35.0,1.9625],[35.0005,1.7615],[34.991,1.6645],[34.9435,1.5765],[34.864,1.5285],[34.8445,1.4575],[34.794,1.4135],[34.787,1.365],[34.829,1.3095],[34.831,1.269],[34.795,1.223],[34.6685,1.2075],[34.612,1.1595],[34.579,1.1495],[34.575,1.0985],[34.5265,1.107],[34.5025,1.0705],[34.48,0.94],[34.4465,0.864],[34.3615,0.774],[34.315,0.7615],[34.314,0.698],[34.2795,0.646],[34.2005,0.6255],[34.138,0.583],[34.116,0.523],[34.1195,0.4835],[34.0885,0.4555],[34.108,0.3695],[33.91,0.099],[33.9845,-0.1335],[33.929,-0.4575],[33.9255,-0.5605],[33.9335,-0.903],[33.9335,-1.0],[34.019,-1.0],[34.046,-1.046],[34.0805,-1.022],[34.3865,-1.194],[34.776,-1.4145],[35.141,-1.6225],[35.484,-1.8135],[36.001,-2.1055],[36.1455,-2.189],[36.6315,-2.463],[37.004,-2.6715],[37.381,-2.884],[37.512,-2.9545],[37.673,-3.061],[37.712,-3.3105],[37.5845,-3.4405],[37.6175,-3.466],[37.617,-3.52],[37.6655,-3.505],[37.7495,-3.5445],[37.784,-3.6715],[37.99,-3.8155],[38.367,-4.086],[38.8255,-4.405],[39.2195,-4.68],[39.347,-4.832],[39.3645,-4.8635],[39.424,-4.898],[39.478,-4.892],[39.605,-4.681],[39.647,-4.681]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/49/Flag_of_Kenya.svg","name:en":"Kenya","wikidata":"Q114","ISO3166-1:alpha2":"KE","ISO3166-1:alpha3":"KEN","ISO3166-1:numeric":"404"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.7735,33.3355],[35.7155,33.3255],[35.658,33.2745],[35.5835,33.2675],[35.5465,33.238],[35.527,33.1415],[35.5035,33.0895],[35.4315,33.0655],[35.354,33.0575],[35.2935,33.108],[35.193,33.0855],[35.0795,33.096],[34.8825,33.172],[34.842,33.0025],[34.794,32.973],[34.745,32.919],[34.7185,32.8445],[34.694,32.7335],[34.678,32.611],[34.595,32.293],[34.4945,32.0295],[34.413,31.904],[34.401,31.8525],[34.295,31.704],[34.488,31.597],[34.565,31.5425],[34.547,31.513],[34.478,31.476],[34.3805,31.3895],[34.3645,31.3615],[34.3665,31.2905],[34.2675,31.22],[34.332,31.041],[34.4035,30.859],[34.497,30.679],[34.519,30.592],[34.5185,30.5325],[34.5575,30.4945],[34.5435,30.413],[34.6115,30.371],[34.707,30.1175],[34.8485,29.759],[34.8565,29.688],[34.8785,29.6435],[34.8665,29.597],[34.8785,29.5435],[34.921,29.4535],[34.9665,29.519],[34.9785,29.577],[35.0145,29.639],[35.012,29.697],[35.03,29.772],[35.0845,29.8855],[35.0785,29.957],[35.1125,30.039],[35.146,30.063],[35.161,30.1345],[35.1445,30.1625],[35.146,30.282],[35.192,30.346],[35.162,30.4415],[35.193,30.499],[35.204,30.582],[35.2615,30.657],[35.296,30.762],[35.341,30.8155],[35.333,30.867],[35.371,30.927],[35.4165,30.951],[35.4275,31.05],[35.4545,31.1045],[35.4495,31.1575],[35.4,31.239],[35.4065,31.2815],[35.4565,31.3625],[35.475,31.4965],[35.3945,31.4915],[35.2305,31.3745],[35.1345,31.355],[35.006,31.3575],[34.92,31.3435],[34.882,31.393],[34.894,31.415],[34.944,31.51],[34.9525,31.5945],[35.0065,31.6525],[35.085,31.6905],[35.127,31.7295],[35.239,31.7095],[35.264,31.8265],[35.178,31.808],[35.1085,31.8235],[35.0835,31.8525],[35.0335,31.859],[35.039,31.907],[34.9905,31.961],[35.005,32.022],[34.985,32.0945],[34.9905,32.142],[34.9595,32.1755],[35.0305,32.266],[35.0165,32.3385],[35.0515,32.367],[35.0655,32.449],[35.2245,32.552],[35.2955,32.5095],[35.358,32.5185],[35.403,32.501],[35.419,32.417],[35.555,32.389],[35.554,32.513],[35.577,32.5525],[35.5735,32.641],[35.633,32.6855],[35.7565,32.745],[35.8375,32.828],[35.851,32.89],[35.895,32.945],[35.8715,32.9815],[35.8505,33.1025],[35.817,33.113],[35.8445,33.1675],[35.815,33.245],[35.7775,33.2765],[35.813,33.317],[35.7735,33.3355]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d4/Flag_of_Israel.svg","name:en":"Israel","wikidata":"Q801","ISO3166-1:alpha2":"IL","ISO3166-1:alpha3":"ISR","ISO3166-1:numeric":"376"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[9.607,47.061],[9.6345,47.101],[9.626,47.146],[9.573,47.1755],[9.585,47.2055],[9.5305,47.2705],[9.486,47.1805],[9.5195,47.0985],[9.56,47.0485],[9.607,47.061]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/47/Flag_of_Liechtenstein.svg","name:en":"Liechtenstein","wikidata":"Q347","ISO3166-1:alpha2":"LI","ISO3166-1:alpha3":"LIE","ISO3166-1:numeric":"438"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[22.1555,48.4035],[22.121,48.3785],[22.021,48.3925],[21.764,48.339],[21.6685,48.389],[21.621,48.492],[21.525,48.5225],[21.5185,48.547],[21.4395,48.5855],[21.4165,48.5595],[21.3275,48.5595],[21.252,48.532],[21.161,48.5215],[21.1285,48.4925],[21.0665,48.526],[20.956,48.5215],[20.9165,48.56],[20.8365,48.583],[20.654,48.5615],[20.5865,48.536],[20.5065,48.5345],[20.5045,48.4905],[20.416,48.418],[20.4075,48.363],[20.2845,48.262],[20.2495,48.279],[20.07,48.1915],[19.9745,48.166],[19.814,48.167],[19.762,48.213],[19.68,48.213],[19.631,48.25],[19.5085,48.1785],[19.4945,48.11],[19.3995,48.084],[19.301,48.0885],[19.239,48.054],[19.1275,48.0735],[19.058,48.0575],[18.8335,48.048],[18.811,47.9895],[18.767,47.9845],[18.763,47.872],[18.8215,47.8505],[18.739,47.813],[18.682,47.7675],[18.434,47.7565],[18.291,47.7315],[18.087,47.7565],[17.8945,47.739],[17.7785,47.74],[17.7185,47.754],[17.6045,47.8225],[17.454,47.8855],[17.4325,47.921],[17.336,47.9925],[17.202,48.02],[17.161,48.0065],[17.0945,47.971],[17.1125,47.9275],[17.078,47.8775],[17.01,47.8585],[17.072,47.808],[17.0705,47.728],[17.093,47.7085],[16.913,47.688],[16.749,47.6815],[16.721,47.7355],[16.6375,47.756],[16.548,47.7515],[16.552,47.7225],[16.477,47.6905],[16.5145,47.646],[16.59,47.6175],[16.653,47.622],[16.7055,47.523],[16.6525,47.5005],[16.662,47.4555],[16.588,47.423],[16.4455,47.4065],[16.4895,47.2805],[16.442,47.2485],[16.428,47.185],[16.517,47.1495],[16.4645,47.099],[16.5205,47.0615],[16.4525,47.003],[16.3025,46.9985],[16.2465,46.9495],[16.114,46.869],[16.291,46.873],[16.3475,46.8405],[16.313,46.7975],[16.3185,46.754],[16.4285,46.694],[16.392,46.6335],[16.5085,46.5655],[16.5235,46.5055],[16.597,46.476],[16.6645,46.459],[16.7075,46.403],[16.801,46.3855],[16.88,46.334],[16.887,46.28],[16.973,46.225],[17.006,46.223],[17.1595,46.1695],[17.313,45.9665],[17.3745,45.982],[17.3955,45.9305],[17.4455,45.9505],[17.5655,45.9375],[17.6145,45.9095],[17.6725,45.835],[17.746,45.828],[17.833,45.792],[18.0015,45.7945],[18.082,45.7645],[18.191,45.788],[18.244,45.761],[18.4105,45.739],[18.5555,45.7945],[18.628,45.8735],[18.705,45.918],[18.791,45.878],[18.807,45.903],[18.89,45.922],[18.962,45.926],[19.006,45.9585],[19.0795,45.9635],[19.0655,46.0],[19.1045,46.04],[19.1475,45.996],[19.2855,45.997],[19.364,46.052],[19.4155,46.0455],[19.5025,46.142],[19.5675,46.1785],[19.6315,46.1695],[19.6965,46.1875],[19.818,46.128],[19.935,46.176],[20.096,46.177],[20.1375,46.144],[20.182,46.16],[20.2645,46.1265],[20.3555,46.1695],[20.459,46.143],[20.503,46.1895],[20.632,46.128],[20.715,46.167],[20.749,46.251],[20.8735,46.288],[20.922,46.262],[21.0245,46.2665],[21.066,46.243],[21.1155,46.3015],[21.168,46.298],[21.1995,46.348],[21.2065,46.403],[21.2755,46.4065],[21.318,46.4505],[21.2605,46.502],[21.321,46.583],[21.33,46.6315],[21.4165,46.6425],[21.5295,46.721],[21.484,46.765],[21.519,46.836],[21.595,46.8605],[21.6795,46.998],[21.6825,47.0455],[21.726,47.0985],[21.773,47.109],[21.8175,47.173],[21.858,47.187],[21.852,47.239],[21.9385,47.373],[22.012,47.376],[22.0355,47.4275],[22.0075,47.4755],[22.045,47.54],[22.188,47.609],[22.2645,47.731],[22.426,47.75],[22.481,47.811],[22.549,47.772],[22.68,47.788],[22.7685,47.878],[22.8475,47.908],[22.8975,47.954],[22.8365,47.99],[22.8815,48.055],[22.827,48.1155],[22.7205,48.1155],[22.6755,48.092],[22.603,48.104],[22.566,48.179],[22.49,48.253],[22.369,48.245],[22.315,48.3285],[22.2675,48.361],[22.2645,48.404],[22.2155,48.4245],[22.1555,48.4035]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/c1/Flag_of_Hungary.svg","name:en":"Hungary","wikidata":"Q28","ISO3166-1:alpha2":"HU","ISO3166-1:alpha3":"HUN","ISO3166-1:numeric":"348"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[22.5655,49.088],[22.414,49.102],[22.3695,49.1455],[22.3205,49.1355],[22.236,49.1545],[22.0305,49.225],[22.034,49.2785],[21.961,49.349],[21.7635,49.3835],[21.631,49.4475],[21.434,49.4125],[21.2775,49.461],[21.192,49.401],[21.124,49.4365],[21.0565,49.4215],[21.104,49.3765],[21.044,49.3655],[20.994,49.3125],[20.9255,49.296],[20.8655,49.3475],[20.826,49.3345],[20.723,49.4195],[20.6115,49.4135],[20.574,49.3765],[20.464,49.416],[20.408,49.393],[20.3295,49.391],[20.3195,49.347],[20.242,49.3505],[20.1465,49.318],[20.1025,49.253],[20.0755,49.179],[20.0085,49.22],[19.9195,49.236],[19.8445,49.195],[19.763,49.2075],[19.805,49.323],[19.7905,49.4105],[19.7295,49.3915],[19.6345,49.4125],[19.582,49.458],[19.5305,49.536],[19.5295,49.573],[19.469,49.5965],[19.41,49.592],[19.36,49.5355],[19.2815,49.5355],[19.2335,49.511],[19.219,49.4485],[19.1775,49.414],[19.027,49.394],[18.961,49.4545],[18.9715,49.5045],[18.851,49.517],[18.7545,49.4885],[18.7105,49.5025],[18.5455,49.5005],[18.548,49.468],[18.492,49.435],[18.379,49.3305],[18.297,49.3045],[18.184,49.287],[18.1065,49.134],[18.116,49.092],[18.082,49.0465],[18.0245,49.021],[17.9245,49.02],[17.8855,48.9275],[17.7815,48.9255],[17.704,48.861],[17.633,48.855],[17.52,48.813],[17.477,48.8415],[17.3965,48.813],[17.242,48.8685],[17.1925,48.8745],[17.115,48.8335],[17.089,48.786],[17.043,48.7635],[16.94,48.6165],[16.954,48.5435],[16.85,48.4495],[16.843,48.3515],[16.895,48.313],[16.976,48.172],[17.0775,48.106],[17.0675,48.0315],[17.161,48.0065],[17.202,48.02],[17.336,47.9925],[17.4325,47.921],[17.454,47.8855],[17.6045,47.8225],[17.7185,47.754],[17.7785,47.74],[17.8945,47.739],[18.087,47.7565],[18.291,47.7315],[18.434,47.7565],[18.682,47.7675],[18.739,47.813],[18.8215,47.8505],[18.763,47.872],[18.767,47.9845],[18.811,47.9895],[18.8335,48.048],[19.058,48.0575],[19.1275,48.0735],[19.239,48.054],[19.301,48.0885],[19.3995,48.084],[19.4945,48.11],[19.5085,48.1785],[19.631,48.25],[19.68,48.213],[19.762,48.213],[19.814,48.167],[19.9745,48.166],[20.07,48.1915],[20.2495,48.279],[20.2845,48.262],[20.4075,48.363],[20.416,48.418],[20.5045,48.4905],[20.5065,48.5345],[20.5865,48.536],[20.654,48.5615],[20.8365,48.583],[20.9165,48.56],[20.956,48.5215],[21.0665,48.526],[21.1285,48.4925],[21.161,48.5215],[21.252,48.532],[21.3275,48.5595],[21.4165,48.5595],[21.4395,48.5855],[21.5185,48.547],[21.525,48.5225],[21.621,48.492],[21.6685,48.389],[21.764,48.339],[22.021,48.3925],[22.121,48.3785],[22.1555,48.4035],[22.1375,48.4325],[22.161,48.5675],[22.1895,48.61],[22.3395,48.684],[22.3505,48.7715],[22.376,48.834],[22.421,48.8845],[22.4285,48.9315],[22.4775,48.991],[22.5455,49.007],[22.5655,49.088]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/e6/Flag_of_Slovakia.svg","name:en":"Slovakia","wikidata":"Q214","ISO3166-1:alpha2":"SK","ISO3166-1:alpha3":"SVK","ISO3166-1:numeric":"703"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[27.3515,57.518],[27.2255,57.5545],[27.1175,57.562],[27.0315,57.6045],[26.9455,57.603],[26.782,57.5725],[26.676,57.5665],[26.6145,57.5295],[26.5,57.5245],[26.4695,57.5755],[26.4015,57.572],[26.275,57.5975],[26.2055,57.715],[26.136,57.751],[26.027,57.775],[26.0585,57.8365],[25.8205,57.865],[25.7725,57.9125],[25.7035,57.9035],[25.486,57.975],[25.467,58.005],[25.3035,58.0345],[25.2165,58.074],[25.106,58.078],[25.0205,58.018],[24.8325,57.972],[24.742,57.982],[24.6285,57.942],[24.585,57.962],[24.4625,57.925],[24.46,57.88],[24.357,57.8745],[24.261,57.917],[24.2095,57.899],[23.601,57.899],[23.6485,57.783],[23.5825,57.6695],[23.406,57.594],[23.181,57.5865],[22.999,57.702],[22.7075,57.941],[22.5835,57.9275],[22.1435,57.779],[21.916,57.7495],[21.843,57.763],[21.656,57.746],[21.465,57.6865],[21.364,57.6135],[21.2635,57.5145],[21.162,57.378],[21.098,57.2195],[21.0645,57.0935],[20.924,56.998],[20.7715,56.931],[20.712,56.8485],[20.6785,56.6775],[20.6715,56.2475],[20.7095,56.0455],[21.2005,56.0765],[21.3225,56.215],[21.4525,56.2485],[21.5025,56.2955],[21.618,56.3245],[21.722,56.3135],[21.8355,56.3715],[21.9195,56.3685],[21.9945,56.4135],[22.1255,56.4325],[22.241,56.4],[22.3455,56.394],[22.423,56.41],[22.6165,56.3825],[22.8405,56.3655],[23.0025,56.4065],[23.027,56.331],[23.0935,56.305],[23.184,56.3375],[23.177,56.3685],[23.388,56.379],[23.439,56.3515],[23.545,56.332],[23.63,56.36],[23.7805,56.3675],[23.795,56.339],[24.019,56.33],[24.1215,56.249],[24.342,56.295],[24.454,56.258],[24.5645,56.289],[24.587,56.3315],[24.699,56.3975],[24.9025,56.418],[24.969,56.366],[24.973,56.3105],[25.056,56.266],[25.092,56.186],[25.2795,56.1585],[25.404,56.158],[25.48,56.143],[25.6505,56.135],[25.6885,56.0865],[25.82,56.055],[25.8605,55.9995],[25.974,55.9815],[26.0455,55.945],[26.1205,55.8795],[26.2035,55.859],[26.2685,55.7675],[26.35,55.7425],[26.3755,55.7095],[26.443,55.709],[26.531,55.677],[26.6305,55.6805],[26.659,55.706],[26.8,55.698],[26.8705,55.714],[26.9205,55.788],[27.015,55.8285],[27.1605,55.85],[27.2875,55.785],[27.378,55.8075],[27.429,55.795],[27.6155,55.787],[27.653,55.9295],[27.8005,55.9845],[27.833,56.0235],[27.906,56.052],[27.972,56.1205],[28.026,56.1195],[28.1515,56.17],[28.24,56.2715],[28.1655,56.3745],[28.1945,56.4425],[28.0975,56.511],[28.125,56.5825],[28.062,56.586],[28.0115,56.6365],[27.981,56.707],[27.901,56.76],[27.9765,56.812],[27.8985,56.838],[27.8475,56.88],[27.6595,56.8345],[27.686,56.9115],[27.7375,56.92],[27.7695,57.005],[27.748,57.053],[27.757,57.131],[27.817,57.1425],[27.8675,57.297],[27.6715,57.395],[27.5175,57.427],[27.5525,57.469],[27.547,57.5195],[27.5005,57.54],[27.3515,57.518]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/84/Flag_of_Latvia.svg","name:en":"Latvia","wikidata":"Q211","ISO3166-1:alpha2":"LV","ISO3166-1:alpha3":"LVA","ISO3166-1:numeric":"428"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[124.9775,37.6875],[125.047,37.6375],[125.2445,37.5835],[125.4265,37.649],[125.5165,37.682],[125.695,37.6915],[125.75,37.7145],[126.0165,37.6585],[126.111,37.7125],[126.1605,37.7175],[126.187,37.749],[126.206,37.823],[126.4135,37.8445],[126.575,37.7625],[126.652,37.781],[126.6915,37.8395],[126.6695,37.9445],[126.7215,37.955],[126.8125,37.999],[126.8565,38.041],[126.87,38.0885],[126.9645,38.135],[126.976,38.199],[127.042,38.259],[127.1105,38.2955],[127.2235,38.328],[127.306,38.3175],[127.3855,38.337],[127.5055,38.301],[127.5735,38.3335],[127.691,38.3245],[127.7425,38.341],[127.8205,38.3065],[127.882,38.331],[128.0675,38.3085],[128.1575,38.343],[128.283,38.4365],[128.3135,38.514],[128.3065,38.57],[128.3745,38.6235],[128.656,38.6175],[128.6225,38.683],[128.597,38.7745],[128.539,38.8175],[128.511,38.8615],[128.4315,38.9285],[128.329,38.962],[128.2755,39.0135],[128.17,39.059],[128.029,39.153],[127.9755,39.233],[127.8215,39.375],[127.788,39.488],[127.8395,39.588],[127.824,39.6555],[127.993,39.7],[128.0965,39.7755],[128.1985,39.775],[128.298,39.7955],[128.473,39.8585],[128.651,39.965],[128.7745,40.006],[128.8365,40.04],[128.9125,40.138],[128.92,40.171],[129.014,40.1985],[129.104,40.2665],[129.154,40.277],[129.2855,40.3385],[129.3205,40.394],[129.3755,40.434],[129.4065,40.486],[129.509,40.538],[129.5285,40.5725],[129.6765,40.6355],[129.7815,40.6345],[129.9175,40.694],[130.007,40.8515],[130.0205,40.943],[129.9915,41.0445],[129.994,41.189],[130.0575,41.3245],[130.055,41.438],[129.996,41.5295],[129.9475,41.5685],[130.048,41.6225],[130.1125,41.723],[130.158,41.757],[130.2325,41.855],[130.3725,41.93],[130.388,41.956],[130.51,42.0285],[130.6435,42.0435],[130.88,42.134],[130.9245,42.1705],[130.702,42.2925],[130.6545,42.325],[130.6695,42.378],[130.637,42.4185],[130.5785,42.43],[130.5785,42.4845],[130.5245,42.547],[130.4145,42.5675],[130.37,42.6205],[130.253,42.7065],[130.2375,42.8205],[130.262,42.9035],[130.1725,42.914],[130.138,42.902],[130.0985,42.9865],[130.063,42.9675],[129.954,42.9785],[129.9005,43.005],[129.8515,42.9615],[129.8365,42.865],[129.808,42.789],[129.76,42.708],[129.785,42.6795],[129.732,42.4985],[129.7445,42.467],[129.704,42.427],[129.607,42.437],[129.5325,42.3745],[129.4945,42.4125],[129.421,42.433],[129.304,42.4155],[129.2235,42.3525],[129.225,42.278],[129.1765,42.2595],[129.203,42.2065],[129.1145,42.139],[129.0515,42.138],[128.915,42.01],[128.745,42.0515],[128.6765,42.012],[128.6305,42.037],[128.5765,42.0],[128.506,41.997],[128.4575,42.0185],[128.285,42.024],[128.0845,42.0205],[128.031,41.9955],[128.0995,41.945],[128.0965,41.838],[128.1085,41.789],[128.1645,41.7105],[128.242,41.679],[128.311,41.589],[128.2965,41.538],[128.2355,41.4985],[128.201,41.4085],[128.108,41.3605],[128.0825,41.393],[128.032,41.391],[127.967,41.4355],[127.8475,41.4195],[127.559,41.43],[127.493,41.4755],[127.4105,41.4555],[127.3375,41.4635],[127.1615,41.54],[127.104,41.64],[127.03,41.672],[127.054,41.7],[126.9445,41.805],[126.725,41.705],[126.5655,41.598],[126.588,41.5625],[126.505,41.435],[126.4865,41.371],[126.433,41.352],[126.3625,41.281],[126.3535,41.243],[126.3095,41.215],[126.274,41.155],[126.2345,41.145],[126.1305,41.058],[126.1035,41.005],[126.0515,40.9635],[126.0185,40.9015],[125.958,40.8805],[125.858,40.8835],[125.7955,40.865],[125.702,40.8635],[125.6295,40.799],[125.5745,40.7825],[125.5245,40.7165],[125.486,40.7215],[125.4515,40.6745],[125.329,40.637],[125.267,40.6475],[125.2575,40.613],[125.186,40.6075],[125.0955,40.5565],[125.0085,40.525],[125.0285,40.492],[124.922,40.4545],[124.8895,40.4755],[124.817,40.4045],[124.7495,40.379],[124.706,40.313],[124.6425,40.301],[124.5095,40.2185],[124.481,40.1725],[124.412,40.139],[124.3505,40.0805],[124.3375,40.0465],[124.372,40.017],[124.354,39.9705],[124.2925,39.968],[124.284,39.9275],[124.2375,39.9265],[124.21,39.8575],[124.173,39.829],[124.1595,39.743],[124.0915,39.612],[124.1805,39.5895],[124.2225,39.5235],[124.327,39.481],[124.33,39.373],[124.3695,39.306],[124.4505,39.249],[124.4935,39.138],[124.582,39.0815],[124.7155,39.0615],[124.811,39.075],[124.8925,39.103],[124.9735,39.209],[125.0295,39.196],[125.022,39.16],[124.9595,39.081],[124.874,38.947],[124.797,38.909],[124.7415,38.859],[124.696,38.7635],[124.6135,38.7185],[124.539,38.645],[124.517,38.5945],[124.5055,38.4925],[124.541,38.3925],[124.5935,38.3305],[124.5525,38.2965],[124.445,38.249],[124.398,38.1885],[124.394,38.131],[124.4165,38.05],[124.6335,38.05],[124.85,38.0],[124.8665,37.7665],[124.9775,37.6875]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/5/51/Flag_of_North_Korea.svg","name:en":"North Korea","wikidata":"Q423","ISO3166-1:alpha2":"KP","ISO3166-1:alpha3":"PRK","ISO3166-1:numeric":"408"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[122.714,24.45],[122.73,24.375],[122.7555,24.332],[122.8145,24.2805],[122.862,24.2575],[122.9505,24.2385],[123.0225,24.2375],[123.1275,24.2675],[123.202,24.322],[123.24,24.37],[123.2625,24.4335],[123.2515,24.531],[123.2135,24.5905],[123.157,24.6345],[123.068,24.667],[123.0045,24.675],[122.887,24.66],[122.7725,24.5855],[122.721,24.499],[122.714,24.45]]],[[[123.2365,25.736],[123.2535,25.6595],[123.302,25.594],[123.374,25.5505],[123.558,25.52],[123.643,25.535],[123.7155,25.579],[123.7635,25.644],[123.777,25.738],[123.847,25.7805],[123.8955,25.8455],[123.9125,25.922],[123.8955,25.9985],[123.8405,26.071],[123.7685,26.1145],[123.683,26.1295],[123.598,26.1145],[123.5255,26.071],[123.4775,26.006],[123.4655,25.952],[123.3875,25.938],[123.315,25.895],[123.2535,25.813],[123.2365,25.736]]],[[[123.3355,24.1925],[123.354,24.112],[123.3955,24.055],[123.484,24.003],[123.545,23.9925],[123.576,23.9355],[123.6455,23.8775],[123.7685,23.8465],[123.864,23.8565],[123.9245,23.8825],[123.9865,23.938],[124.025,24.021],[124.1295,24.052],[124.219,24.1335],[124.329,24.164],[124.4005,24.2105],[124.446,24.2695],[124.4775,24.392],[124.5355,24.489],[124.6075,24.452],[124.7385,24.4405],[124.8315,24.47],[124.8795,24.504],[124.929,24.5725],[124.9475,24.639],[124.945,24.693],[125.042,24.631],[125.1225,24.548],[125.2165,24.5155],[125.3105,24.512],[125.408,24.5265],[125.502,24.521],[125.605,24.5565],[125.666,24.61],[125.693,24.659],[125.703,24.7535],[125.6505,24.8585],[125.5695,24.9175],[125.5765,25.018],[125.552,25.0775],[125.48,25.1475],[125.3595,25.1805],[125.2865,25.169],[125.2235,25.138],[125.117,25.106],[125.062,25.0635],[124.9435,24.9335],[124.915,24.8475],[124.8115,24.93],[124.702,24.961],[124.6045,24.9455],[124.544,24.913],[124.4865,24.849],[124.463,24.7725],[124.4025,24.8025],[124.319,24.818],[124.2125,24.7935],[124.139,24.735],[124.108,24.6845],[124.01,24.6605],[123.9635,24.6295],[123.847,24.675],[123.741,24.665],[123.6695,24.626],[123.547,24.5075],[123.485,24.428],[123.453,24.3695],[123.393,24.327],[123.348,24.259],[123.3355,24.1925]]],[[[124.332,25.9225],[124.341,25.8655],[124.388,25.7875],[124.474,25.7335],[124.554,25.7195],[124.6725,25.7485],[124.748,25.8135],[124.784,25.9195],[124.77,25.991],[124.731,26.0525],[124.692,26.086],[124.622,26.1175],[124.561,26.125],[124.482,26.1125],[124.429,26.088],[124.3635,26.024],[124.332,25.9225]]],[[[126.485,26.3605],[126.518,26.252],[126.555,26.209],[126.696,26.1025],[126.7685,26.074],[126.8215,26.0685],[126.903,26.0825],[127.0005,26.1505],[127.035,26.0685],[127.1175,25.99],[127.2315,25.9465],[127.341,25.9355],[127.3975,25.942],[127.498,25.901],[127.678,25.8745],[127.737,25.882],[127.973,25.9665],[128.093,26.036],[128.142,26.092],[128.417,26.51],[128.538,26.661],[128.558,26.726],[128.5435,26.839],[128.6435,26.912],[128.6785,26.9825],[128.6835,27.083],[128.6565,27.144],[128.7175,27.17],[128.793,27.2225],[128.8965,27.317],[128.9325,27.3865],[128.937,27.4615],[129.0575,27.48],[129.157,27.5385],[129.229,27.6395],[129.254,27.699],[129.2615,27.7975],[129.3225,27.8075],[129.4165,27.857],[129.661,28.0735],[129.742,28.1665],[129.806,28.115],[129.915,28.0765],[130.011,28.0825],[130.106,28.122],[130.152,28.16],[130.2185,28.241],[130.249,28.295],[130.2615,28.3755],[130.2415,28.449],[130.163,28.5345],[130.0765,28.572],[129.948,28.569],[129.9355,28.6135],[129.991,28.687],[130.0055,28.741],[129.9945,28.822],[129.942,28.8975],[129.8295,28.9535],[129.7175,28.951],[129.6385,28.9165],[129.5835,28.8625],[129.5495,28.76],[129.559,28.7005],[129.07,28.4805],[129.0225,28.45],[128.9385,28.3565],[128.9105,28.291],[128.905,28.1085],[128.814,28.0775],[128.7385,28.0335],[128.6765,27.949],[128.6535,27.8175],[128.657,27.6965],[128.6755,27.64],[128.559,27.604],[128.4575,27.5935],[128.3995,27.5665],[128.3455,27.5165],[128.317,27.47],[128.295,27.3755],[128.3085,27.3035],[128.336,27.2505],[128.24,27.1935],[128.1965,27.236],[128.116,27.286],[128.03,27.3015],[127.889,27.2675],[127.764,27.1655],[127.7095,27.086],[127.542,26.8095],[127.462,26.645],[127.3985,26.7405],[127.288,26.796],[127.189,26.798],[127.123,26.7775],[127.0255,26.6965],[126.989,26.7385],[126.8855,26.7895],[126.8095,26.7945],[126.735,26.7755],[126.665,26.7295],[126.623,26.6725],[126.604,26.5915],[126.6095,26.5495],[126.5435,26.502],[126.4955,26.4235],[126.485,26.3605]]],[[[127.8685,32.2465],[127.8975,32.1475],[127.9445,32.097],[127.995,32.0665],[128.1125,32.044],[128.116,31.9395],[128.168,31.853],[128.2385,31.8065],[128.3215,31.786],[128.41,31.7925],[128.4775,31.818],[128.5325,31.8565],[128.631,31.958],[128.6565,32.024],[128.6415,32.127],[128.613,32.17],[128.5475,32.2215],[128.4035,32.259],[128.344,32.2545],[128.3275,32.3225],[128.2525,32.4065],[128.1705,32.441],[128.044,32.441],[127.9635,32.407],[127.895,32.337],[127.8685,32.2465]]],[[[127.991,27.8785],[127.9975,27.8325],[128.0355,27.755],[128.0825,27.7095],[128.17,27.6685],[128.2795,27.6645],[128.3525,27.6895],[128.4235,27.75],[128.458,27.824],[128.457,27.9095],[128.41,28.0005],[128.3405,28.0565],[128.267,28.0815],[128.156,28.077],[128.1055,28.0575],[128.037,28.002],[128.0125,27.9635],[127.991,27.8785]]],[[[128.3515,32.7215],[128.3615,32.5925],[128.386,32.523],[128.446,32.459],[128.5395,32.411],[128.6145,32.3885],[128.805,32.371],[129.21,32.077],[129.5195,31.85],[129.4425,31.7395],[129.1915,31.2305],[129.1825,31.163],[129.19,30.822],[129.2175,30.7355],[129.27,30.679],[129.3865,30.6315],[129.851,30.577],[130.096,30.699],[130.3405,30.6775],[130.4795,30.761],[130.931,31.0425],[131.3795,31.321],[131.659,31.494],[131.701,31.614],[131.7035,31.696],[131.723,31.794],[131.7075,31.8585],[131.7175,31.948],[131.8045,32.1935],[131.8515,32.2625],[132.593,32.5045],[133.0415,32.522],[133.1375,32.548],[133.219,32.6115],[133.2585,32.6895],[133.3015,32.86],[133.728,32.9465],[134.235,33.047],[134.3465,33.099],[134.407,33.1665],[134.6295,33.4365],[135.2045,33.4645],[135.327,33.375],[135.4,33.3415],[135.5125,33.3065],[135.58,33.297],[135.6825,33.244],[135.769,33.2335],[135.9315,33.2675],[135.9975,33.3],[136.057,33.3525],[136.0935,33.4105],[136.1415,33.445],[136.1935,33.523],[136.6765,33.8595],[137.0555,34.122],[137.663,34.468],[137.807,34.445],[137.946,34.466],[138.105,34.414],[138.2125,34.392],[138.952,34.3725],[138.849,34.269],[138.828,34.163],[138.7355,34.145],[138.677,34.1135],[138.6035,34.0425],[138.5805,33.992],[138.5805,33.903],[138.608,33.844],[138.68,33.7785],[138.751,33.7505],[138.8185,33.7425],[138.914,33.7595],[138.993,33.8055],[139.0415,33.8565],[139.0745,33.9255],[139.078,33.98],[139.149,33.9835],[139.202,33.909],[139.3025,33.856],[139.197,33.833],[139.1275,33.7925],[139.0695,33.7125],[139.064,33.6085],[139.115,33.5225],[139.173,33.4805],[139.247,33.456],[139.339,33.454],[139.424,33.48],[139.4965,33.537],[139.5325,33.6035],[139.539,33.658],[139.6225,33.6505],[139.75,33.681],[139.8065,33.7225],[139.8655,33.8225],[139.8725,33.8615],[139.855,33.948],[139.799,34.0255],[139.802,34.1435],[139.763,34.2195],[139.719,34.2615],[139.6265,34.311],[139.5225,34.325],[139.543,34.498],[139.9665,34.706],[140.094,34.757],[140.164,34.8245],[140.199,34.882],[140.262,34.916],[140.391,34.945],[140.469,34.9775],[140.5675,35.055],[140.623,35.1325],[140.643,35.1905],[140.66,35.316],[140.641,35.3895],[140.6865,35.4505],[140.733,35.4855],[140.8055,35.4955],[140.904,35.4915],[140.9825,35.5125],[141.0335,35.541],[141.0865,35.596],[141.1235,35.696],[141.1205,35.7555],[141.07,35.855],[140.996,35.914],[140.9585,35.969],[140.9395,36.0335],[140.865,36.113],[140.8155,36.2205],[140.854,36.293],[140.8825,36.385],[140.8875,36.4955],[141.031,36.7445],[141.155,36.827],[141.225,36.9385],[141.288,37.298],[141.295,37.488],[141.2795,37.6005],[141.2935,37.6615],[141.265,37.7565],[141.7555,38.125],[141.828,38.2145],[141.98,38.588],[142.168,39.046],[142.31,39.411],[142.3305,39.528],[142.3255,39.593],[142.2375,39.9335],[142.2525,40.0015],[142.2375,40.069],[142.158,40.156],[142.0925,40.299],[142.0015,40.4455],[141.904,40.5605],[141.7485,40.6955],[141.696,40.722],[141.6615,40.8675],[141.6705,40.9475],[141.6505,41.013],[141.664,41.104],[141.6585,41.163],[141.6915,41.281],[141.7285,41.359],[141.7365,41.421],[141.716,41.5045],[141.6175,41.601],[140.9345,41.603],[140.8765,41.5995],[140.2335,41.2665],[139.6035,40.931],[139.2995,40.666],[139.254,40.61],[139.2345,40.528],[139.2505,40.465],[139.429,40.002],[139.343,39.593],[139.268,39.231],[139.0465,38.659],[138.513,38.5415],[138.3835,38.5225],[138.2785,38.4645],[138.197,38.382],[138.126,38.3375],[138.0675,38.243],[138.024,38.195],[137.5315,38.135],[136.8505,38.0495],[136.7955,38.0305],[136.69,37.9485],[136.6585,37.8775],[136.4805,37.379],[136.47,37.326],[136.4275,37.2245],[136.4185,37.1385],[136.432,37.08],[136.5015,36.958],[136.5045,36.8865],[136.4385,36.781],[136.3575,36.688],[136.2175,36.5415],[136.1465,36.5105],[136.0815,36.4515],[136.0285,36.4395],[135.475,36.15],[135.11,35.9575],[134.9775,35.9195],[134.859,35.864],[134.7655,35.8745],[134.608,35.862],[134.4875,35.8675],[134.218,35.785],[134.1335,35.744],[134.0645,35.732],[133.943,35.7305],[133.8585,35.7075],[133.768,35.7005],[133.645,35.7265],[133.5285,35.728],[133.44,35.776],[133.3495,35.7895],[133.2225,35.7825],[133.1835,35.8125],[133.2435,35.845],[133.3625,35.9695],[133.4555,35.991],[133.551,36.061],[133.594,36.1205],[133.6345,36.235],[133.6335,36.31],[133.58,36.409],[133.539,36.4475],[133.419,36.522],[133.349,36.548],[133.265,36.5555],[133.1455,36.526],[133.04,36.465],[132.969,36.3935],[132.9365,36.327],[132.871,36.3],[132.796,36.2505],[132.7355,36.1865],[132.7005,36.1045],[132.7015,36.037],[132.7455,35.9485],[132.8205,35.872],[132.967,35.78],[132.898,35.7365],[132.74,35.6905],[132.5305,35.62],[132.435,35.562],[132.398,35.523],[132.369,35.459],[132.3655,35.4045],[132.2745,35.3535],[132.1415,35.2235],[131.643,35.1175],[131.0685,34.9925],[130.996,34.966],[130.398,34.6475],[130.198,34.537],[130.0125,34.4395],[129.889,34.391],[129.8035,34.058],[129.639,33.911],[129.121,33.5055],[128.859,33.5055],[128.616,33.314],[128.5875,33.276],[128.37,32.8015],[128.3515,32.7215]]],[[[128.7445,28.8],[128.7625,28.7245],[128.795,28.674],[128.861,28.6215],[128.9105,28.602],[128.9955,28.5915],[129.0665,28.6015],[129.147,28.6385],[129.192,28.683],[129.2205,28.7355],[129.2335,28.841],[129.2065,28.9265],[129.2875,28.9355],[129.373,28.978],[129.424,29.0345],[129.5145,29.0765],[129.562,29.127],[129.589,29.188],[129.593,29.245],[129.667,29.251],[129.7575,29.2885],[129.7985,29.322],[129.8405,29.3895],[129.8505,29.446],[129.921,29.5115],[129.968,29.619],[130.075,29.67],[130.1345,29.747],[130.1485,29.858],[130.2515,29.933],[130.2905,30.017],[130.295,30.079],[130.381,30.043],[130.4665,30.0285],[130.5665,30.029],[130.652,30.048],[130.7385,30.0885],[130.8115,30.152],[130.909,30.145],[131.057,30.172],[131.147,30.222],[131.194,30.2825],[131.2125,30.35],[131.214,30.424],[131.2645,30.507],[131.3155,30.6815],[131.316,30.784],[131.301,30.8585],[131.26,30.943],[131.1085,30.8685],[131.045,30.89],[130.987,30.8575],[130.948,30.8025],[130.9115,30.789],[130.8755,30.8155],[130.819,30.806],[130.786,30.7665],[130.785,30.701],[130.3205,30.463],[130.27,30.51],[130.1775,30.5495],[130.1055,30.528],[130.0915,30.4635],[129.9415,30.3885],[129.97,30.3495],[130.0755,30.2595],[129.9925,30.25],[129.907,30.208],[129.8235,30.191],[129.761,30.1565],[129.694,30.075],[129.5915,30.1105],[129.537,30.116],[129.4445,30.1005],[129.3465,30.035],[129.31,29.9765],[129.2985,29.899],[129.3315,29.797],[129.2955,29.687],[129.3125,29.6105],[129.371,29.5365],[129.3575,29.43],[129.277,29.424],[129.1935,29.3925],[129.1435,29.353],[129.0765,29.3305],[129.0095,29.281],[128.9625,29.195],[128.9605,29.119],[129.0,29.0365],[128.913,29.0215],[128.8135,28.954],[128.754,28.859],[128.7445,28.8]]],[[[128.998,33.9475],[129.071,33.9085],[129.239,34.038],[129.283,34.059],[129.6855,34.4155],[129.7415,34.6465],[129.7205,34.76],[129.6535,34.8485],[129.5995,34.8915],[129.2685,34.6795],[129.223,34.567],[128.998,33.9475]]],[[[130.961,24.4715],[130.9775,24.395],[131.0155,24.3355],[131.057,24.3005],[131.1245,24.27],[131.1885,24.2615],[131.2725,24.2765],[131.3685,24.344],[131.4055,24.411],[131.4085,24.518],[131.3715,24.5885],[131.317,24.636],[131.2435,24.6655],[131.181,24.672],[131.107,24.6605],[131.0255,24.6135],[130.982,24.5575],[130.961,24.4715]]],[[[130.9915,25.823],[130.999,25.7715],[131.032,25.7085],[131.1125,25.643],[131.1795,25.6185],[131.2715,25.6155],[131.353,25.6375],[131.3985,25.6635],[131.4655,25.739],[131.486,25.796],[131.5385,25.8775],[131.554,25.9515],[131.5365,26.03],[131.496,26.087],[131.437,26.1285],[131.361,26.153],[131.2745,26.1605],[131.1875,26.1405],[131.1055,26.0785],[131.071,26.014],[131.024,25.959],[131.0045,25.9135],[130.9915,25.823]]],[[[135.8535,20.4235],[135.87,20.347],[135.916,20.2825],[135.985,20.239],[136.1025,20.2145],[136.184,20.23],[136.2535,20.273],[136.306,20.346],[136.3225,20.4225],[136.3065,20.4965],[136.26,20.5635],[136.183,20.614],[136.1015,20.629],[135.985,20.608],[135.916,20.5645],[135.87,20.5],[135.8535,20.4235]]],[[[145.6375,44.3015],[145.6135,44.4005],[145.5795,44.4515],[145.5,44.508],[145.3755,44.5425],[145.289,44.543],[145.1755,44.512],[145.09,44.455],[144.9725,44.333],[144.826,44.2465],[144.658,44.119],[144.531,44.127],[144.502,44.1945],[144.435,44.259],[144.3795,44.288],[144.279,44.3115],[144.07,44.3275],[143.8285,44.384],[143.6,44.462],[143.5375,44.518],[143.3825,44.6055],[143.295,44.644],[143.1905,44.7095],[143.133,44.765],[142.9645,44.884],[142.864,45.002],[142.784,45.1235],[142.707,45.1995],[142.648,45.232],[142.4945,45.3615],[142.4325,45.43],[142.039,45.505],[142.018,45.545],[141.9165,45.5765],[141.1845,45.711],[140.9415,45.705],[140.84,45.686],[140.7215,45.613],[140.6815,45.5435],[140.6835,45.401],[140.899,44.7425],[141.0125,44.3885],[141.045,43.842],[140.1735,43.5005],[140.1215,43.4635],[139.8585,43.0975],[139.5945,42.724],[139.213,42.339],[139.159,42.2655],[139.1335,42.199],[139.095,41.804],[139.068,41.498],[139.093,41.419],[139.1215,41.381],[139.201,41.325],[139.6985,41.167],[139.756,41.1545],[139.862,41.157],[140.238,41.356],[140.873,41.692],[140.9445,41.6615],[141.0375,41.6655],[141.0865,41.683],[141.4015,41.6825],[141.4545,41.787],[141.4475,41.86],[141.2975,42.2635],[141.35,42.2845],[141.5875,42.4145],[141.7315,42.383],[141.7835,42.381],[141.916,42.302],[142.0315,42.273],[142.078,42.2355],[142.255,42.14],[142.386,42.082],[142.4675,42.0575],[142.535,42.019],[142.695,41.9625],[142.8265,41.933],[142.9245,41.8905],[143.0125,41.8085],[143.0995,41.754],[143.215,41.718],[143.3655,41.7325],[143.471,41.7945],[143.51,41.8475],[143.5255,41.9285],[143.5675,41.989],[143.605,42.159],[143.604,42.2305],[143.623,42.3145],[143.675,42.3845],[143.7905,42.5005],[144.0725,42.7085],[144.171,42.7615],[144.2505,42.7885],[144.424,42.7425],[144.5925,42.7485],[144.7425,42.733],[144.8365,42.738],[144.97,42.7615],[145.1045,42.805],[145.628,42.981],[145.7995,43.066],[146.0085,43.2255],[145.8425,43.381],[145.7595,43.502],[145.4475,43.6235],[145.279,43.7355],[145.2765,43.8395],[145.32,43.9405],[145.514,44.132],[145.6375,44.3015]]],[[[139.4365,33.132],[139.443,33.0835],[139.492,32.9945],[139.543,32.953],[139.623,32.9215],[139.7405,32.8615],[139.8085,32.8465],[139.887,32.851],[139.9745,32.885],[140.017,32.918],[140.0715,32.9895],[140.0965,33.0625],[140.0925,33.133],[140.0605,33.239],[140.0375,33.277],[139.9675,33.336],[139.871,33.3665],[139.7205,33.3585],[139.5985,33.325],[139.504,33.2745],[139.4585,33.2175],[139.4365,33.132]]],[[[139.5165,32.456],[139.537,32.3725],[139.61,32.2915],[139.7075,32.25],[139.781,32.2435],[139.8525,32.2555],[139.9265,32.291],[139.9755,32.336],[140.011,32.403],[140.0185,32.4575],[139.995,32.544],[139.938,32.61],[139.878,32.6475],[139.801,32.6705],[139.674,32.6605],[139.619,32.6355],[139.5585,32.583],[139.5315,32.537],[139.5165,32.456]]],[[[139.6815,31.888],[139.688,31.8405],[139.725,31.7715],[139.781,31.724],[139.8325,31.7005],[139.944,31.688],[140.0075,31.702],[140.099,31.7585],[140.1505,31.85],[140.152,31.9185],[140.1185,31.995],[140.073,32.0395],[139.9945,32.078],[139.8935,32.0875],[139.801,32.062],[139.7415,32.021],[139.698,31.9615],[139.6815,31.888]]],[[[139.816,31.4395],[139.8545,31.329],[139.913,31.2765],[140.0365,31.2385],[140.155,31.258],[140.24,31.3165],[140.286,31.4095],[140.281,31.4885],[140.229,31.5725],[140.135,31.629],[140.0615,31.642],[140.0,31.6375],[139.9195,31.608],[139.8385,31.527],[139.816,31.4395]]],[[[140.0545,30.4825],[140.07,30.41],[140.1135,30.349],[140.197,30.2925],[140.2745,30.2735],[140.3825,30.285],[140.473,30.3335],[140.516,30.383],[140.5445,30.4555],[140.546,30.509],[140.524,30.574],[140.4665,30.6405],[140.3965,30.6795],[140.339,30.6935],[140.2415,30.689],[140.1435,30.646],[140.083,30.5815],[140.0545,30.4825]]],[[[140.1115,29.794],[140.135,29.706],[140.1665,29.6645],[140.2255,29.621],[140.342,29.5935],[140.411,29.603],[140.4815,29.6345],[140.5285,29.676],[140.566,29.745],[140.5725,29.8035],[140.534,29.905],[140.4505,29.97],[140.3875,29.9895],[140.3185,29.9925],[140.2215,29.964],[140.1695,29.926],[140.1235,29.8575],[140.1115,29.794]]],[[[140.6475,27.247],[140.6605,27.1805],[140.7025,27.1155],[140.7775,27.063],[140.8905,27.0435],[140.9535,27.055],[141.0415,27.1075],[141.0935,27.185],[141.105,27.2515],[141.091,27.3215],[141.021,27.408],[140.902,27.451],[140.7905,27.4375],[140.717,27.394],[140.663,27.3195],[140.6475,27.247]]],[[[141.052,25.4295],[141.0685,25.3535],[141.125,25.2775],[141.1965,25.2345],[141.2805,25.2195],[141.3645,25.2345],[141.45,25.286],[141.4975,25.35],[141.5145,25.426],[141.4935,25.5215],[141.446,25.5855],[141.3655,25.633],[141.2815,25.648],[141.1975,25.633],[141.1175,25.5815],[141.0695,25.517],[141.052,25.4295]]],[[[141.065,24.7985],[141.0825,24.6685],[141.13,24.604],[141.2015,24.5605],[141.2855,24.5455],[141.3695,24.5605],[141.5035,24.639],[141.551,24.7035],[141.568,24.78],[141.546,24.874],[141.4985,24.9385],[141.4075,24.9975],[141.3235,25.0125],[141.2035,24.9915],[141.1325,24.948],[141.0845,24.8835],[141.065,24.7985]]],[[[141.2355,24.2265],[141.252,24.1495],[141.2995,24.0845],[141.371,24.041],[141.455,24.026],[141.554,24.0425],[141.625,24.086],[141.6725,24.151],[141.6925,24.242],[141.676,24.319],[141.6285,24.384],[141.557,24.427],[141.4585,24.445],[141.374,24.43],[141.303,24.3865],[141.2555,24.3215],[141.2355,24.2265]]],[[[141.8675,27.7225],[141.897,27.6175],[141.979,27.5175],[141.988,27.434],[142.043,27.347],[141.99,27.289],[141.942,27.1625],[141.936,27.0935],[141.954,26.9775],[142.0055,26.8955],[141.9355,26.8385],[141.9,26.769],[141.8925,26.7025],[141.9105,26.536],[141.9755,26.417],[142.0395,26.3675],[142.1105,26.3435],[142.168,26.3405],[142.296,26.367],[142.377,26.408],[142.439,26.473],[142.4655,26.557],[142.4565,26.624],[142.367,26.7885],[142.315,26.8505],[142.3775,26.885],[142.4355,26.945],[142.4755,27.0665],[142.463,27.1595],[142.421,27.227],[142.3955,27.294],[142.36,27.3365],[142.431,27.424],[142.444,27.511],[142.4115,27.5995],[142.4025,27.683],[142.35,27.7965],[142.2915,27.867],[142.24,27.9085],[142.184,27.932],[142.095,27.9405],[142.0015,27.914],[141.9415,27.873],[141.892,27.816],[141.8675,27.7225]]],[[[153.752,24.2845],[153.765,24.205],[153.7855,24.1705],[153.8505,24.113],[153.931,24.0815],[154.0235,24.0815],[154.1055,24.114],[154.1745,24.166],[154.199,24.2005],[154.2015,24.3285],[154.169,24.4],[154.0755,24.472],[153.9795,24.49],[153.883,24.4765],[153.8175,24.434],[153.7675,24.371],[153.752,24.2845]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9e/Flag_of_Japan.svg","name:en":"Japan","wikidata":"Q17","ISO3166-1:alpha2":"JP","ISO3166-1:alpha3":"JPN","ISO3166-1:numeric":"392"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[55.999,41.32],[55.999,43.7535],[55.999,44.437],[55.9985,45.0005],[57.0005,45.2375],[57.9045,45.442],[58.5885,45.5905],[58.9905,45.3695],[59.5055,45.161],[59.9745,44.968],[60.7445,44.644],[61.068,44.3705],[61.1065,44.3605],[61.138,44.226],[61.4255,43.9995],[62.004,43.5055],[62.2425,43.5185],[63.349,43.6505],[63.8575,43.5995],[64.535,43.5705],[64.9325,43.7355],[65.002,43.7155],[65.185,43.495],[65.4955,43.333],[65.5595,43.2895],[65.742,43.0],[65.8655,42.8675],[66.08,42.933],[66.095,42.566],[66.088,42.3465],[66.0,42.36],[65.9995,41.9395],[66.5355,41.8785],[66.6085,41.494],[66.6455,41.3475],[66.7095,41.1425],[67.3125,41.1355],[67.7235,41.197],[67.8665,41.1365],[67.9655,41.149],[67.9885,41.0855],[67.9835,41.025],[68.0205,41.0205],[68.071,41.0805],[68.111,41.036],[68.0555,41.009],[68.088,40.97],[68.0885,40.9255],[68.0125,40.8905],[67.9775,40.831],[68.077,40.789],[68.218,40.6875],[68.371,40.6175],[68.502,40.5795],[68.6105,40.577],[68.652,40.614],[68.644,40.694],[68.584,40.709],[68.586,40.78],[68.5625,40.806],[68.5905,40.865],[68.579,40.925],[68.4905,40.9715],[68.5265,41.0205],[68.5895,41.029],[68.6285,40.9975],[68.6435,40.943],[68.7485,40.9815],[68.714,41.032],[68.804,41.116],[68.9915,41.214],[69.0395,41.255],[69.0415,41.366],[69.1665,41.395],[69.1565,41.4345],[69.25,41.4665],[69.301,41.438],[69.4005,41.5035],[69.4455,41.5635],[69.501,41.547],[69.6175,41.662],[69.696,41.673],[69.7455,41.701],[69.839,41.7205],[69.8825,41.7065],[69.9575,41.734],[69.981,41.781],[70.077,41.8085],[70.1575,41.8495],[70.2615,41.9545],[70.3355,41.9745],[70.32,42.037],[70.476,42.1095],[70.55,42.0675],[70.5595,42.0235],[70.6345,42.0155],[70.687,42.121],[70.7985,42.2075],[70.8835,42.2105],[70.9435,42.2635],[70.891,42.263],[70.8655,42.32],[70.898,42.3665],[70.9555,42.402],[70.9815,42.498],[71.0755,42.4725],[71.0015,42.5855],[71.1795,42.6105],[71.182,42.6635],[71.274,42.743],[71.268,42.778],[71.396,42.7975],[71.464,42.7855],[71.534,42.8015],[71.5705,42.775],[71.637,42.767],[71.6755,42.79],[71.8635,42.833],[72.057,42.769],[72.1205,42.7365],[72.166,42.7545],[72.282,42.7595],[72.499,42.685],[72.588,42.684],[72.7445,42.639],[72.7975,42.5805],[72.911,42.535],[73.12,42.552],[73.3095,42.518],[73.3145,42.458],[73.357,42.4365],[73.487,42.4175],[73.5155,42.435],[73.4485,42.5085],[73.433,42.549],[73.4655,42.7315],[73.501,42.7655],[73.5215,42.8625],[73.516,42.935],[73.5575,42.9825],[73.5595,43.029],[73.612,43.041],[73.766,43.1065],[73.838,43.128],[73.8995,43.119],[73.9575,43.1805],[73.969,43.2225],[74.042,43.172],[74.0765,43.187],[74.222,43.205],[74.1925,43.2555],[74.294,43.242],[74.3085,43.2165],[74.3845,43.1975],[74.432,43.1655],[74.5775,43.133],[74.6155,43.085],[74.7515,42.99],[74.8535,43.0],[75.037,42.9205],[75.2315,42.8535],[75.3365,42.8555],[75.419,42.8395],[75.5705,42.8305],[75.717,42.7975],[75.7615,42.8335],[75.7865,42.899],[75.8235,42.9385],[75.937,42.9555],[76.0285,42.911],[76.1955,42.931],[76.309,42.901],[76.344,42.861],[76.486,42.8885],[76.5225,42.922],[76.709,42.904],[76.8515,42.9785],[76.9165,42.966],[77.0045,42.983],[77.034,42.9475],[77.0845,42.981],[77.155,42.9725],[77.2335,42.912],[77.302,42.9165],[77.3535,42.899],[77.4225,42.9285],[77.55,42.9395],[77.5805,42.9155],[77.697,42.904],[77.7795,42.915],[77.9675,42.851],[78.035,42.865],[78.1715,42.8605],[78.198,42.882],[78.3505,42.855],[78.492,42.9005],[78.642,42.8425],[78.8655,42.8005],[78.9305,42.7745],[79.0565,42.7755],[79.1995,42.804],[79.17,42.7435],[79.182,42.6975],[79.271,42.6215],[79.3465,42.6025],[79.505,42.46],[79.641,42.4545],[79.6735,42.48],[79.787,42.4465],[79.968,42.432],[79.982,42.3775],[80.0305,42.343],[80.0815,42.3395],[80.1745,42.211],[80.2835,42.2355],[80.232,42.349],[80.2335,42.4045],[80.2075,42.4675],[80.267,42.503],[80.2205,42.533],[80.1625,42.625],[80.1765,42.6715],[80.223,42.713],[80.2775,42.838],[80.329,42.8275],[80.5985,42.8945],[80.5395,42.936],[80.4855,42.945],[80.388,43.003],[80.384,43.046],[80.485,43.0705],[80.556,43.102],[80.5865,43.1365],[80.6575,43.15],[80.7815,43.138],[80.8075,43.1775],[80.765,43.267],[80.7785,43.3115],[80.698,43.316],[80.6855,43.3375],[80.735,43.393],[80.751,43.469],[80.6855,43.5795],[80.521,43.8245],[80.506,43.906],[80.4545,43.982],[80.4455,44.071],[80.396,44.11],[80.4065,44.2495],[80.384,44.3775],[80.347,44.4825],[80.4105,44.6105],[80.3135,44.7035],[80.2195,44.7305],[80.173,44.7935],[80.128,44.823],[80.034,44.804],[79.9445,44.85],[79.9535,44.882],[79.882,44.9105],[79.9555,44.9615],[80.013,44.98],[80.062,45.036],[80.1525,45.0525],[80.2135,45.0235],[80.3255,45.0695],[80.382,45.04],[80.4375,45.097],[80.576,45.11],[80.698,45.1335],[80.74,45.1615],[80.8985,45.125],[80.957,45.1675],[81.0245,45.1635],[81.1195,45.215],[81.2035,45.237],[81.282,45.2395],[81.382,45.264],[81.452,45.262],[81.6325,45.3565],[81.7135,45.359],[81.774,45.386],[81.922,45.229],[82.088,45.243],[82.151,45.222],[82.2675,45.2415],[82.3405,45.209],[82.475,45.177],[82.582,45.2215],[82.5875,45.3445],[82.543,45.421],[82.4375,45.4595],[82.28,45.535],[82.259,45.6165],[82.288,45.709],[82.345,45.7955],[82.3355,45.939],[82.3755,45.964],[82.456,45.977],[82.512,46.1545],[82.608,46.2985],[82.725,46.4915],[82.782,46.669],[82.8295,46.7775],[82.869,46.83],[82.9325,46.972],[82.938,47.016],[82.993,47.0645],[83.0515,47.2255],[83.151,47.2345],[83.2395,47.179],[83.354,47.1745],[83.4135,47.116],[83.4575,47.132],[83.578,47.0605],[83.701,47.0225],[83.7835,47.0245],[83.9175,46.973],[83.9845,46.9955],[84.0565,46.9685],[84.1265,46.9675],[84.1655,46.994],[84.2585,47.003],[84.379,46.996],[84.436,47.009],[84.5065,46.9755],[84.5795,46.9995],[84.7095,47.0095],[84.8475,46.954],[84.858,46.9285],[84.944,46.863],[84.983,46.915],[85.0785,46.931],[85.0955,46.964],[85.194,47.0125],[85.217,47.0525],[85.2995,47.065],[85.3825,47.046],[85.4125,47.06],[85.5295,47.0515],[85.5765,47.1375],[85.6825,47.223],[85.6985,47.288],[85.6745,47.312],[85.6805,47.434],[85.608,47.5095],[85.6145,47.54],[85.543,48.0095],[85.5295,48.025],[85.5595,48.149],[85.5935,48.196],[85.6595,48.242],[85.7255,48.373],[85.7875,48.4175],[85.9115,48.436],[86.2325,48.432],[86.3215,48.488],[86.407,48.4785],[86.584,48.54],[86.6355,48.627],[86.773,48.7205],[86.761,48.804],[86.8165,48.8565],[86.7565,48.894],[86.727,48.9505],[86.749,49.0145],[86.8475,49.0655],[86.887,49.1325],[86.996,49.1425],[87.0565,49.1295],[87.104,49.151],[87.199,49.143],[87.286,49.116],[87.3025,49.1695],[87.2695,49.2325],[87.1985,49.2525],[87.0255,49.2575],[87.006,49.299],[86.9265,49.349],[86.93,49.42],[86.8685,49.435],[86.831,49.47],[86.8555,49.499],[86.827,49.5455],[86.7395,49.571],[86.618,49.5705],[86.6025,49.6055],[86.7095,49.6875],[86.779,49.696],[86.7715,49.764],[86.6975,49.8085],[86.5905,49.801],[86.6105,49.772],[86.5825,49.724],[86.5265,49.702],[86.4885,49.658],[86.3715,49.5935],[86.2715,49.5825],[86.267,49.5465],[86.202,49.466],[86.113,49.5245],[86.037,49.522],[85.969,49.487],[85.8835,49.567],[85.841,49.5475],[85.757,49.577],[85.6785,49.551],[85.5835,49.6135],[85.49,49.598],[85.3885,49.635],[85.3215,49.591],[85.2575,49.5885],[85.2105,49.6275],[85.1985,49.738],[85.1505,49.7675],[85.0595,49.885],[84.995,49.9055],[84.9835,49.984],[85.038,50.009],[85.024,50.06],[84.946,50.0965],[84.8565,50.092],[84.7575,50.137],[84.692,50.1885],[84.628,50.2105],[84.5305,50.206],[84.447,50.2465],[84.41,50.209],[84.326,50.2285],[84.2515,50.29],[84.2685,50.3565],[84.204,50.469],[84.2485,50.5155],[84.176,50.5515],[84.1455,50.603],[84.0825,50.6335],[84.023,50.698],[83.951,50.7465],[83.9645,50.801],[83.9105,50.8165],[83.8335,50.88],[83.7115,50.8935],[83.6485,50.955],[83.5935,50.9495],[83.4475,50.977],[83.407,51.013],[83.202,50.9935],[83.121,51.011],[83.1125,50.966],[83.006,50.925],[82.9795,50.884],[82.911,50.9075],[82.733,50.9275],[82.739,50.8505],[82.7015,50.812],[82.5675,50.747],[82.351,50.7755],[82.2705,50.77],[82.2225,50.737],[82.0785,50.751],[81.997,50.788],[81.8855,50.8045],[81.797,50.772],[81.5825,50.7425],[81.4495,50.756],[81.486,50.824],[81.4355,50.863],[81.4125,50.9755],[81.289,50.974],[81.191,50.951],[81.06,50.948],[81.103,51.0605],[81.142,51.079],[81.177,51.1615],[80.92,51.219],[80.9405,51.2595],[80.6775,51.3165],[80.635,51.263],[80.6345,51.207],[80.447,51.2085],[80.4365,51.119],[80.482,51.1095],[80.482,50.9685],[80.421,50.962],[80.376,50.927],[80.2455,50.9185],[80.193,50.9005],[80.185,50.8275],[80.083,50.828],[80.076,50.738],[79.7205,51.217],[79.4115,51.624],[79.1875,51.9235],[78.975,52.1505],[78.7175,52.429],[78.403,52.7815],[78.267,52.924],[78.0255,53.161],[77.9065,53.2915],[77.339,53.595],[76.829,53.8495],[76.538,54.0],[76.4985,54.0715],[76.435,54.1225],[76.4405,54.1675],[76.5825,54.149],[76.645,54.1245],[76.7565,54.162],[76.828,54.225],[76.8285,54.274],[76.8785,54.302],[76.86,54.361],[76.9355,54.4155],[76.929,54.458],[76.7955,54.419],[76.73,54.418],[76.675,54.345],[76.476,54.318],[76.352,54.334],[76.2575,54.3595],[76.1865,54.3015],[76.203,54.26],[75.598,54.099],[75.529,54.115],[75.375,54.07],[75.4345,54.0095],[75.441,53.9725],[75.377,53.9595],[75.0465,53.7925],[74.982,53.8225],[74.7885,53.828],[74.757,53.7445],[74.6675,53.756],[74.635,53.668],[74.457,53.691],[74.421,53.594],[74.4865,53.5725],[74.3935,53.4595],[74.2915,53.4915],[74.2875,53.5605],[74.1615,53.6015],[74.1465,53.555],[74.0515,53.566],[74.072,53.6295],[73.901,53.651],[73.821,53.5785],[73.7475,53.607],[73.6665,53.613],[73.645,53.5525],[73.4385,53.4355],[73.3625,53.4675],[73.3975,53.522],[73.2385,53.5675],[73.277,53.6395],[73.323,53.6755],[73.3555,53.766],[73.449,53.8075],[73.4525,53.876],[73.691,53.856],[73.7675,54.054],[73.657,54.0465],[73.609,54.0085],[73.519,54.004],[73.5045,53.939],[73.276,53.9425],[73.1935,53.9705],[73.067,53.9855],[73.0595,54.0405],[72.9785,54.0495],[72.994,54.0985],[72.8315,54.098],[72.8255,54.1215],[72.6085,54.137],[72.56,54.1165],[72.571,54.0515],[72.68,54.045],[72.7195,53.9995],[72.7145,53.951],[72.543,53.976],[72.4865,53.901],[72.3695,53.9445],[72.4175,53.9985],[72.3965,54.0515],[72.44,54.12],[72.4175,54.151],[72.2995,54.187],[72.2785,54.317],[72.189,54.355],[72.0325,54.3665],[72.0515,54.338],[72.136,54.305],[72.0855,54.219],[72.2185,54.182],[72.1845,54.1205],[72.101,54.1475],[72.0735,54.1995],[71.949,54.2435],[71.886,54.2315],[71.751,54.254],[71.7265,54.1875],[71.764,54.1475],[71.7205,54.1065],[71.61,54.117],[71.488,54.101],[71.491,54.1585],[71.296,54.185],[71.2745,54.147],[71.179,54.095],[71.1225,54.1295],[71.138,54.162],[71.0715,54.2055],[71.073,54.275],[70.9965,54.2755],[70.9985,54.336],[71.1125,54.335],[71.1985,54.3115],[71.2295,54.354],[71.1705,54.461],[71.1675,54.5605],[71.2145,54.61],[71.2915,54.606],[71.293,54.668],[71.2105,54.7],[71.0965,54.713],[71.0305,54.7785],[71.0195,54.852],[70.9565,54.8885],[71.0085,54.9705],[70.993,55.0735],[70.9215,55.1305],[70.789,55.2895],[70.695,55.2895],[70.6205,55.2535],[70.478,55.2725],[70.41,55.2535],[70.402,55.2155],[70.292,55.1925],[70.2275,55.148],[70.1135,55.172],[70.057,55.209],[69.9505,55.21],[69.863,55.3],[69.707,55.3515],[69.442,55.342],[69.4165,55.3815],[69.1995,55.33],[69.1605,55.3905],[69.048,55.427],[68.9325,55.442],[68.916,55.3655],[68.929,55.3185],[68.7485,55.379],[68.7085,55.348],[68.697,55.289],[68.635,55.2655],[68.6355,55.204],[68.4445,55.1965],[68.282,55.205],[68.1895,55.1515],[68.2715,55.085],[68.2375,55.054],[68.2345,54.969],[68.0325,54.9505],[67.9105,54.9835],[67.8195,54.9715],[67.7735,54.886],[67.563,54.8445],[67.5165,54.862],[67.3985,54.8555],[67.3325,54.8735],[67.282,54.8255],[66.994,54.7895],[66.782,54.7685],[66.738,54.7435],[66.654,54.744],[66.3815,54.7035],[65.9985,54.625],[65.964,54.638],[65.982,54.717],[65.8455,54.697],[65.831,54.653],[65.7475,54.6075],[65.633,54.641],[65.4565,54.6245],[65.495,54.5965],[65.306,54.5685],[65.2125,54.532],[65.2015,54.466],[65.239,54.376],[65.121,54.3305],[64.975,54.424],[64.7385,54.354],[64.6,54.375],[64.4815,54.3665],[64.4195,54.3265],[64.359,54.3365],[64.1845,54.309],[64.0985,54.311],[63.984,54.2715],[64.0215,54.2305],[63.9615,54.2005],[63.8975,54.2165],[63.8035,54.271],[63.728,54.261],[63.5185,54.207],[63.357,54.1885],[63.337,54.201],[63.151,54.1825],[63.1515,54.123],[63.0795,54.104],[62.936,54.1165],[62.806,54.112],[62.5965,54.072],[62.569,54.0465],[62.5975,53.995],[62.5545,53.9335],[62.4465,53.927],[62.3965,54.004],[62.339,54.0325],[62.1755,54.0285],[62.0835,54.049],[62.006,54.0375],[62.044,53.9485],[61.857,53.9625],[61.893,54.009],[61.8185,54.019],[61.782,53.9845],[61.7005,54.019],[61.569,54.008],[61.4805,54.03],[61.48,54.083],[61.294,54.074],[61.25,54.0235],[61.28,53.9285],[61.205,53.919],[61.1805,53.972],[61.1,53.975],[61.0295,53.955],[60.9935,53.907],[61.137,53.9045],[61.179,53.8525],[61.237,53.8415],[61.227,53.7955],[61.1145,53.721],[61.0585,53.7215],[60.9955,53.673],[60.896,53.668],[60.9005,53.6285],[61.122,53.615],[61.1265,53.59],[61.2155,53.5635],[61.358,53.5615],[61.391,53.602],[61.553,53.593],[61.565,53.5385],[61.488,53.476],[61.4285,53.4555],[61.2995,53.5125],[61.229,53.484],[61.2165,53.432],[61.1555,53.4055],[61.177,53.312],[61.226,53.2835],[61.4,53.274],[61.527,53.2155],[61.6305,53.223],[61.674,53.2655],[61.7325,53.239],[61.784,53.1845],[61.916,53.142],[62.0275,53.142],[62.1195,53.112],[62.1475,53.068],[62.1305,52.99],[62.0295,52.951],[61.961,52.95],[61.834,52.993],[61.702,52.998],[61.632,52.954],[61.557,53.012],[61.469,53.0365],[61.3905,52.9925],[61.307,52.996],[61.251,53.036],[61.1215,52.9875],[61.0495,52.981],[61.076,52.928],[60.987,52.8865],[60.8345,52.774],[60.712,52.7645],[60.7145,52.663],[60.7615,52.6265],[60.843,52.641],[60.842,52.5225],[60.9825,52.508],[60.992,52.423],[61.067,52.347],[61.0105,52.326],[60.9455,52.2675],[60.772,52.208],[60.7165,52.156],[60.511,52.1565],[60.3395,52.055],[60.1975,51.991],[60.002,51.9935],[60.0325,51.9135],[60.144,51.8705],[60.162,51.892],[60.428,51.7985],[60.5175,51.7975],[60.4315,51.7195],[60.3545,51.6905],[60.4255,51.647],[60.538,51.618],[60.9355,51.6155],[60.911,51.5505],[60.9995,51.4675],[61.203,51.461],[61.2565,51.441],[61.3535,51.439],[61.391,51.42],[61.505,51.407],[61.5495,51.3235],[61.69,51.256],[61.568,51.238],[61.486,50.9585],[61.449,50.806],[60.809,50.6565],[60.741,50.6655],[60.5575,50.661],[60.325,50.6705],[60.183,50.7785],[60.188,50.8385],[60.015,50.822],[59.9725,50.7685],[59.9975,50.675],[59.91,50.6365],[59.813,50.5355],[59.776,50.541],[59.5165,50.5285],[59.4575,50.5435],[59.463,50.5845],[59.569,50.5735],[59.4795,50.6415],[59.418,50.6365],[59.114,50.683],[58.8745,50.7],[58.7745,50.8145],[58.6565,50.8085],[58.6965,50.848],[58.688,50.8855],[58.5935,50.8695],[58.5785,50.94],[58.6425,50.9875],[58.5765,51.056],[58.5205,51.085],[58.384,51.075],[58.3405,51.1455],[58.206,51.1295],[58.177,51.066],[58.1025,51.07],[58.0735,51.123],[57.9515,51.0865],[57.867,51.0995],[57.772,51.1385],[57.754,51.056],[57.7515,50.9395],[57.736,50.9275],[57.559,50.936],[57.507,50.876],[57.415,50.8965],[57.3245,50.943],[57.2825,51.0325],[57.246,51.026],[57.185,51.115],[57.1465,51.0875],[56.9845,51.0625],[56.943,51.0875],[56.8875,51.0605],[56.78,51.094],[56.6995,51.075],[56.751,50.9815],[56.634,50.985],[56.51,51.0805],[56.448,51.057],[56.4605,50.965],[56.3925,50.9455],[56.3615,50.9015],[56.298,50.902],[56.1815,50.938],[56.133,50.8305],[56.137,50.7535],[56.0545,50.695],[55.916,50.6415],[55.83,50.623],[55.7465,50.5795],[55.6615,50.56],[55.6115,50.5985],[55.534,50.6135],[55.5025,50.67],[55.403,50.6815],[55.3835,50.6535],[55.1465,50.797],[55.056,50.8195],[55.1295,50.8795],[55.071,50.924],[55.01,50.9055],[54.894,50.92],[54.898,50.958],[54.7915,50.9855],[54.7185,51.0285],[54.5685,51.0185],[54.566,50.9155],[54.658,50.915],[54.6975,50.8925],[54.6785,50.718],[54.718,50.69],[54.73,50.6175],[54.614,50.5385],[54.545,50.5275],[54.466,50.5665],[54.4135,50.6205],[54.4735,50.7785],[54.5185,50.812],[54.518,50.8545],[54.4415,50.8795],[54.288,50.9025],[54.2885,50.949],[54.1995,50.964],[54.137,51.108],[54.042,51.136],[53.9325,51.1935],[53.7475,51.204],[53.6615,51.231],[53.66,51.26],[53.5955,51.306],[53.575,51.4245],[53.4815,51.444],[53.3925,51.491],[53.255,51.512],[53.144,51.485],[53.0565,51.4895],[52.9045,51.4775],[52.7905,51.5135],[52.747,51.4865],[52.6475,51.4885],[52.5655,51.4635],[52.4505,51.6235],[52.416,51.64],[52.3355,51.7435],[52.2385,51.724],[52.09,51.6585],[51.9995,51.6835],[51.894,51.6835],[51.7705,51.583],[51.8135,51.497],[51.656,51.4485],[51.64,51.5385],[51.566,51.506],[51.4315,51.475],[51.2865,51.488],[51.2855,51.532],[51.3805,51.5635],[51.38,51.641],[51.3035,51.641],[51.272,51.685],[51.204,51.6675],[51.0845,51.6665],[50.8935,51.6815],[50.8715,51.7535],[50.768,51.773],[50.768,51.7145],[50.8125,51.649],[50.812,51.585],[50.7675,51.575],[50.7315,51.6285],[50.636,51.629],[50.5865,51.5905],[50.5375,51.589],[50.547,51.465],[50.497,51.4275],[50.338,51.382],[50.373,51.3325],[50.243,51.28],[50.04,51.2515],[49.911,51.201],[49.776,51.107],[49.5545,51.11],[49.393,51.09],[49.3965,51.049],[49.3385,50.987],[49.408,50.961],[49.4435,50.8775],[49.42,50.849],[49.2625,50.823],[49.126,50.7845],[49.0095,50.682],[48.8145,50.5955],[48.689,50.6125],[48.606,50.662],[48.5705,50.6325],[48.652,50.601],[48.659,50.528],[48.7325,50.257],[48.809,50.1705],[48.7635,50.098],[48.848,50.0915],[48.9035,50.0185],[48.7455,49.9215],[48.4505,49.835],[48.269,49.8595],[48.1315,49.998],[48.095,50.1],[48.009,50.142],[47.939,50.2425],[47.8855,50.2685],[47.8285,50.3275],[47.7625,50.341],[47.7315,50.386],[47.6635,50.4085],[47.6125,50.464],[47.5415,50.4605],[47.4725,50.42],[47.4065,50.3365],[47.3005,50.303],[47.3355,50.238],[47.2575,50.1915],[47.3175,50.152],[47.3535,50.0945],[47.1825,49.935],[46.9575,49.8715],[46.9025,49.864],[46.87,49.743],[46.858,49.6435],[46.8295,49.5655],[46.782,49.34],[46.9145,49.283],[46.996,49.2325],[47.0515,49.1655],[47.0285,49.089],[46.972,49.0275],[46.8925,48.982],[46.779,48.9515],[46.493,48.4325],[46.7475,48.3535],[46.8895,48.3275],[47.123,48.2685],[47.1035,48.212],[47.125,48.158],[47.091,48.103],[47.211,48.082],[47.2005,48.055],[47.05,47.998],[47.101,47.928],[47.181,47.854],[47.122,47.8315],[47.1945,47.761],[47.385,47.6815],[47.412,47.7635],[47.4155,47.8385],[47.6525,47.758],[47.9625,47.7515],[48.028,47.763],[48.1115,47.745],[48.2095,47.6915],[48.319,47.5515],[48.4515,47.4115],[48.5195,47.412],[48.71,47.096],[48.9155,46.8805],[48.996,46.743],[48.92,46.6935],[48.7505,46.6855],[48.7135,46.721],[48.5485,46.711],[48.478,46.6705],[48.5605,46.642],[48.5735,46.6065],[48.545,46.5635],[48.7025,46.563],[48.788,46.541],[48.8195,46.4855],[48.8785,46.4815],[48.912,46.4505],[48.986,46.433],[49.045,46.3965],[49.162,46.384],[49.2625,46.294],[49.5065,46.1935],[49.6,46.1765],[49.7015,46.1865],[49.7785,46.1185],[49.886,46.046],[49.9675,46.081],[50.021,46.129],[50.056,46.2285],[50.086,46.4325],[50.147,46.4775],[50.33,46.5505],[50.6985,46.6735],[50.8125,46.7305],[51.147,46.8095],[51.2385,46.85],[51.3855,46.8395],[51.449,46.6985],[51.5105,46.6335],[51.5665,46.61],[51.775,46.5855],[52.132,46.6405],[52.2585,46.6135],[52.364,46.6195],[52.652,46.7065],[52.7985,46.6055],[52.741,46.4225],[52.7405,46.366],[52.784,46.216],[52.876,46.021],[52.6755,45.8115],[52.568,45.7495],[52.472,45.608],[52.2905,45.5525],[52.1555,45.5345],[51.7785,45.5935],[51.6985,45.5875],[51.3425,45.507],[51.269,45.4625],[51.164,45.3645],[51.0785,45.264],[50.8715,45.1155],[50.7075,45.081],[50.4105,45.1855],[50.3145,45.1985],[50.074,45.1935],[49.962,45.1725],[49.883,45.1345],[49.847,45.0945],[49.8085,44.9755],[49.8225,44.901],[49.8715,44.816],[49.925,44.759],[50.0335,44.5495],[50.05,44.3865],[50.0775,44.317],[50.1225,44.2645],[50.2255,44.207],[50.3105,44.174],[50.41,44.155],[50.549,44.144],[50.7005,43.9605],[50.8595,43.7285],[50.989,43.5595],[51.097,43.451],[51.082,43.228],[51.071,43.1605],[51.1035,43.079],[51.16,43.034],[51.2625,43.004],[51.558,42.9675],[51.6355,42.8935],[51.675,42.8335],[51.767,42.73],[51.8875,42.6855],[52.1955,42.651],[52.393,42.602],[52.4405,42.4695],[52.289,42.2625],[52.2355,42.149],[52.2265,42.089],[52.2405,42.0135],[52.247,41.772],[52.259,41.7245],[52.335,41.7195],[52.45,41.762],[52.497,41.7975],[52.9345,42.093],[52.985,42.121],[53.5705,42.289],[54.218,42.379],[54.7795,42.0525],[54.952,41.9065],[54.95,41.833],[55.066,41.6995],[55.1155,41.611],[55.2115,41.5145],[55.3155,41.391],[55.4995,41.2515],[55.5675,41.2885],[55.613,41.2705],[55.7435,41.302],[55.774,41.284],[55.8765,41.332],[55.999,41.32]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d3/Flag_of_Kazakhstan.svg","name:en":"Kazakhstan","wikidata":"Q232","ISO3166-1:alpha2":"KZ","ISO3166-1:alpha3":"KAZ","ISO3166-1:numeric":"398"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[105.2065,14.3435],[105.206,14.3125],[105.247,14.26],[105.282,14.171],[105.3245,14.159],[105.3615,14.1065],[105.4435,14.1085],[105.5565,14.1605],[105.699,14.094],[105.7885,14.0775],[105.7925,14.0235],[105.911,13.9305],[105.996,13.9305],[106.1055,13.9095],[106.1,13.979],[106.168,14.0195],[106.1875,14.063],[106.1215,14.1095],[106.1075,14.188],[106.0515,14.2025],[106.027,14.246],[106.023,14.312],[106.0955,14.373],[106.1875,14.3515],[106.2375,14.3985],[106.2415,14.4645],[106.264,14.4785],[106.3295,14.439],[106.4045,14.45],[106.4355,14.4815],[106.438,14.5235],[106.5235,14.5915],[106.546,14.59],[106.5705,14.521],[106.612,14.4975],[106.6395,14.4485],[106.7175,14.422],[106.838,14.294],[106.895,14.3335],[106.967,14.314],[107.0155,14.397],[107.0775,14.4265],[107.0835,14.395],[107.165,14.416],[107.2085,14.4885],[107.2535,14.5225],[107.2875,14.5795],[107.329,14.5915],[107.4375,14.524],[107.469,14.6215],[107.5565,14.686],[107.542,14.7505],[107.5075,14.7925],[107.5895,14.864],[107.4785,14.9575],[107.468,14.9875],[107.54,15.0495],[107.583,15.0285],[107.598,15.088],[107.623,15.1075],[107.6025,15.1835],[107.6215,15.2145],[107.6245,15.311],[107.606,15.3885],[107.5305,15.4055],[107.513,15.473],[107.471,15.4965],[107.386,15.4925],[107.365,15.57],[107.3365,15.6215],[107.291,15.624],[107.2755,15.704],[107.215,15.824],[107.347,15.8935],[107.393,15.887],[107.4155,15.912],[107.464,16.016],[107.4455,16.054],[107.3775,16.074],[107.3385,16.0555],[107.2835,16.1185],[107.242,16.1475],[107.1975,16.144],[107.16,16.1675],[107.1465,16.22],[107.157,16.2565],[107.0865,16.3125],[106.9685,16.307],[106.964,16.355],[106.8975,16.391],[106.8785,16.4685],[106.8865,16.522],[106.831,16.549],[106.8065,16.4785],[106.772,16.4325],[106.726,16.425],[106.6605,16.4745],[106.6475,16.5405],[106.672,16.5625],[106.565,16.643],[106.5515,16.6945],[106.5515,16.865],[106.5145,16.896],[106.548,16.9285],[106.5155,16.9715],[106.4275,17.008],[106.402,17.053],[106.416,17.0785],[106.373,17.112],[106.328,17.18],[106.305,17.258],[106.2355,17.25],[106.0815,17.3715],[105.862,17.6275],[105.78,17.675],[105.701,17.7615],[105.6605,17.8565],[105.608,17.877],[105.6425,17.9625],[105.5805,18.062],[105.5225,18.1255],[105.5015,18.1885],[105.46,18.206],[105.409,18.154],[105.3635,18.164],[105.3195,18.2015],[105.3315,18.2525],[105.256,18.255],[105.2425,18.2965],[105.192,18.319],[105.1715,18.392],[105.1035,18.4475],[105.115,18.539],[105.136,18.593],[105.1805,18.6025],[105.198,18.6405],[105.1065,18.7065],[105.052,18.7085],[105.018,18.7455],[104.941,18.7395],[104.913,18.787],[104.7945,18.783],[104.739,18.801],[104.63,18.886],[104.581,18.901],[104.545,18.97],[104.496,19.004],[104.4455,18.984],[104.3975,19.055],[104.3045,19.0935],[104.272,19.1215],[104.2125,19.1295],[104.179,19.1975],[104.128,19.205],[104.068,19.248],[104.003,19.233],[103.924,19.2855],[103.9085,19.3295],[103.958,19.384],[104.0145,19.402],[104.073,19.4505],[104.1055,19.568],[104.0605,19.613],[104.072,19.682],[104.105,19.662],[104.236,19.7075],[104.3515,19.6925],[104.4125,19.705],[104.4275,19.673],[104.49,19.6555],[104.506,19.626],[104.567,19.6155],[104.6465,19.6545],[104.7125,19.754],[104.7535,19.75],[104.796,19.7905],[104.835,19.802],[104.8465,19.9295],[104.903,19.968],[104.907,19.9985],[104.9765,20.08],[104.9135,20.1555],[104.862,20.1415],[104.7745,20.1985],[104.719,20.201],[104.63,20.2295],[104.6105,20.275],[104.6175,20.3615],[104.7095,20.4105],[104.6615,20.4745],[104.6255,20.462],[104.611,20.423],[104.565,20.4165],[104.4745,20.369],[104.421,20.3765],[104.4135,20.423],[104.376,20.467],[104.5225,20.549],[104.5415,20.5985],[104.601,20.6055],[104.6415,20.6625],[104.5855,20.6805],[104.552,20.719],[104.4935,20.718],[104.483,20.767],[104.428,20.79],[104.3375,20.856],[104.3335,20.881],[104.2815,20.925],[104.1215,20.9705],[104.054,20.95],[104.0485,20.9115],[103.9615,20.8975],[103.9015,20.9035],[103.7805,20.8015],[103.791,20.7465],[103.7335,20.7315],[103.735,20.667],[103.6835,20.6615],[103.6675,20.6985],[103.6015,20.7425],[103.5095,20.753],[103.463,20.826],[103.4415,20.794],[103.39,20.784],[103.2945,20.835],[103.261,20.8235],[103.227,20.8515],[103.2145,20.8995],[103.168,20.9135],[103.1095,20.9005],[103.0345,21.0585],[102.962,21.0775],[102.9495,21.139],[102.909,21.1805],[102.912,21.2295],[102.8585,21.2565],[102.8515,21.2985],[102.897,21.3045],[102.896,21.385],[102.88,21.4245],[102.8865,21.4765],[102.992,21.586],[102.9815,21.647],[102.9885,21.721],[102.973,21.7365],[102.8855,21.712],[102.8565,21.722],[102.8515,21.8425],[102.817,21.84],[102.8265,21.736],[102.7395,21.6595],[102.6665,21.678],[102.648,21.7355],[102.6615,21.7865],[102.642,21.849],[102.5945,21.925],[102.514,21.9665],[102.4875,21.997],[102.5095,22.0365],[102.4455,22.0745],[102.434,22.1125],[102.3125,22.208],[102.2465,22.2365],[102.2345,22.2825],[102.182,22.3235],[102.1445,22.4],[102.126,22.4325],[102.05,22.4565],[101.999,22.4325],[101.9155,22.44],[101.899,22.381],[101.8315,22.4215],[101.823,22.4615],[101.7605,22.5085],[101.6725,22.476],[101.67,22.372],[101.625,22.2755],[101.5695,22.275],[101.5505,22.234],[101.6005,22.135],[101.5735,22.126],[101.596,22.064],[101.632,22.013],[101.6165,21.977],[101.739,21.873],[101.7725,21.817],[101.746,21.7285],[101.7695,21.6595],[101.8095,21.63],[101.824,21.5905],[101.75,21.583],[101.7615,21.5015],[101.7385,21.406],[101.7335,21.323],[101.799,21.2915],[101.8445,21.2525],[101.8305,21.2055],[101.7935,21.206],[101.757,21.144],[101.7185,21.143],[101.671,21.2035],[101.5865,21.189],[101.6075,21.238],[101.483,21.246],[101.446,21.2265],[101.3875,21.228],[101.292,21.1755],[101.2165,21.2315],[101.2505,21.293],[101.185,21.3285],[101.141,21.411],[101.1995,21.436],[101.217,21.4925],[101.217,21.556],[101.1475,21.562],[101.1635,21.5275],[101.0845,21.459],[101.059,21.455],[100.996,21.3855],[100.949,21.3795],[100.8825,21.3465],[100.8475,21.3045],[100.8105,21.2935],[100.732,21.316],[100.7005,21.222],[100.696,21.1735],[100.642,21.0955],[100.6405,21.0665],[100.596,21.029],[100.546,21.0245],[100.5095,20.888],[100.544,20.8705],[100.637,20.898],[100.608,20.841],[100.494,20.815],[100.4235,20.8325],[100.3615,20.825],[100.2965,20.78],[100.2235,20.714],[100.1755,20.6355],[100.1185,20.4335],[100.1235,20.4055],[100.0845,20.3525],[100.113,20.25],[100.1715,20.246],[100.181,20.3065],[100.2175,20.3145],[100.227,20.3555],[100.2725,20.4005],[100.3305,20.398],[100.374,20.352],[100.4185,20.25],[100.456,20.225],[100.5075,20.151],[100.5765,20.17],[100.57,20.099],[100.501,19.8705],[100.4425,19.8355],[100.438,19.791],[100.404,19.7525],[100.446,19.7065],[100.428,19.6725],[100.478,19.6125],[100.4855,19.5435],[100.5805,19.499],[100.608,19.547],[100.6505,19.5565],[100.7405,19.52],[100.779,19.4925],[100.883,19.606],[100.9125,19.627],[101.036,19.6285],[101.123,19.572],[101.198,19.577],[101.2065,19.605],[101.2805,19.5825],[101.264,19.4705],[101.2105,19.4735],[101.215,19.4315],[101.187,19.3995],[101.2595,19.231],[101.245,19.181],[101.257,19.124],[101.3555,19.051],[101.3355,19.004],[101.2925,18.981],[101.297,18.943],[101.246,18.8895],[101.2585,18.8095],[101.2335,18.72],[101.2725,18.6885],[101.225,18.6235],[101.182,18.612],[101.1835,18.5605],[101.1035,18.5215],[101.0915,18.478],[101.0525,18.4265],[101.083,18.3845],[101.1825,18.34],[101.161,18.3045],[101.166,18.217],[101.1925,18.212],[101.161,18.1245],[101.184,18.065],[101.1215,18.0],[101.1165,17.951],[101.02,17.8925],[101.0285,17.829],[100.978,17.7535],[101.007,17.732],[100.9915,17.619],[100.9675,17.5715],[101.0215,17.557],[101.08,17.5025],[101.161,17.467],[101.175,17.5235],[101.2295,17.5365],[101.2645,17.6035],[101.3065,17.6505],[101.3455,17.659],[101.456,17.747],[101.548,17.7875],[101.5645,17.841],[101.6025,17.857],[101.618,17.894],[101.7105,17.902],[101.766,18.054],[101.786,18.071],[101.8745,18.0285],[101.907,18.031],[101.9455,18.0875],[102.0345,18.1615],[102.045,18.1985],[102.0885,18.2205],[102.1645,18.2025],[102.1855,18.148],[102.3,18.053],[102.341,18.0465],[102.46,17.971],[102.594,17.96],[102.613,17.9075],[102.589,17.8495],[102.667,17.8055],[102.679,17.8565],[102.766,17.8995],[102.7805,17.929],[102.862,17.974],[102.969,18.004],[103.009,17.9765],[103.042,17.9825],[103.073,18.0225],[103.0835,18.129],[103.136,18.1605],[103.1685,18.2575],[103.281,18.2935],[103.2465,18.3705],[103.313,18.4335],[103.4035,18.4485],[103.457,18.425],[103.524,18.4255],[103.6035,18.405],[103.6955,18.344],[103.794,18.343],[103.834,18.328],[103.856,18.2845],[103.953,18.3395],[103.973,18.337],[104.057,18.2235],[104.1055,18.1155],[104.213,17.9995],[104.271,17.87],[104.355,17.8225],[104.457,17.667],[104.4965,17.635],[104.708,17.521],[104.744,17.4605],[104.796,17.402],[104.804,17.3715],[104.8015,17.1645],[104.7295,17.0085],[104.733,16.9365],[104.759,16.8415],[104.738,16.794],[104.7625,16.6895],[104.744,16.6375],[104.735,16.5455],[104.768,16.5],[104.83,16.4645],[104.895,16.343],[104.9205,16.3375],[105.015,16.244],[105.011,16.202],[105.0405,16.11],[105.195,16.055],[105.408,16.016],[105.342,15.922],[105.3905,15.8085],[105.4335,15.7555],[105.497,15.767],[105.596,15.7245],[105.6345,15.6625],[105.625,15.5635],[105.5935,15.5125],[105.6025,15.4805],[105.5785,15.408],[105.491,15.3845],[105.4675,15.3475],[105.5085,15.3185],[105.576,15.3225],[105.591,15.271],[105.54,15.252],[105.4745,15.1775],[105.481,15.0985],[105.573,15.001],[105.6195,14.98],[105.557,14.9245],[105.5765,14.897],[105.512,14.801],[105.531,14.6225],[105.5235,14.5495],[105.4305,14.4245],[105.36,14.3875],[105.322,14.398],[105.2815,14.3535],[105.2065,14.3435]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/5/56/Flag_of_Laos.svg","name:en":"Laos","wikidata":"Q819","ISO3166-1:alpha2":"LA","ISO3166-1:alpha3":"LAO","ISO3166-1:numeric":"418"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-170.1595,-19.082],[-170.1515,-19.1305],[-170.1045,-19.235],[-170.054,-19.291],[-169.9465,-19.344],[-169.8355,-19.351],[-169.7185,-19.307],[-169.6225,-19.2085],[-169.5705,-19.108],[-169.5675,-19.0275],[-169.612,-18.89],[-169.69,-18.798],[-169.7675,-18.7645],[-169.8615,-18.7535],[-169.9475,-18.771],[-170.0435,-18.832],[-170.1115,-18.924],[-170.148,-19.01],[-170.1595,-19.082]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/01/Flag_of_Niue.svg","name:en":"Niue","wikidata":"Q34020","ISO3166-1:alpha2":"NU","ISO3166-1:alpha3":"NIU","ISO3166-1:numeric":"570"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[87.8145,49.17],[87.866,49.1115],[87.827,49.051],[87.8765,49.0145],[87.868,48.948],[87.754,48.9285],[87.734,48.881],[87.7815,48.8695],[87.83,48.7985],[87.935,48.7555],[87.9995,48.7605],[88.0675,48.6805],[87.969,48.5755],[88.1425,48.5275],[88.175,48.499],[88.2425,48.501],[88.357,48.4585],[88.3495,48.4355],[88.437,48.387],[88.5135,48.3795],[88.603,48.339],[88.566,48.276],[88.6375,48.1785],[88.715,48.174],[88.824,48.1],[88.911,48.109],[89.0095,48.05],[89.0625,47.9885],[89.156,47.995],[89.234,47.976],[89.291,48.012],[89.4015,48.0305],[89.582,48.03],[89.5925,47.968],[89.6545,47.9055],[89.727,47.895],[89.7565,47.829],[89.954,47.8345],[89.9595,47.887],[90.0675,47.874],[90.0655,47.7925],[90.1395,47.7225],[90.298,47.6925],[90.4,47.539],[90.4715,47.491],[90.458,47.407],[90.5205,47.377],[90.484,47.313],[90.5945,47.168],[90.622,47.155],[90.778,46.992],[90.8295,46.9895],[90.902,46.9575],[90.9555,46.8775],[90.937,46.826],[91.016,46.764],[91.0485,46.72],[91.0145,46.5805],[91.066,46.5755],[91.0525,46.511],[91.0095,46.461],[90.9695,46.3605],[90.8915,46.3125],[90.949,46.229],[90.975,46.1625],[91.0105,46.1245],[91.022,46.0225],[90.848,45.8875],[90.715,45.7275],[90.6785,45.6055],[90.6705,45.487],[90.77,45.4285],[90.8115,45.325],[90.895,45.2505],[90.8645,45.204],[90.931,45.192],[90.995,45.214],[91.122,45.214],[91.2415,45.132],[91.329,45.129],[91.373,45.109],[91.43,45.1565],[91.492,45.1045],[91.609,45.07],[91.691,45.062],[91.801,45.0815],[92.0575,45.083],[92.0995,45.0755],[92.235,45.0135],[92.3135,45.027],[92.4905,45.0],[92.5495,45.0175],[92.645,45.0205],[92.776,45.05],[92.8815,45.0465],[92.9295,45.0155],[93.168,45.0135],[93.2855,44.9825],[93.366,44.988],[93.428,44.9565],[93.5225,44.9575],[93.671,44.91],[93.7305,44.8625],[94.062,44.7325],[94.15,44.6835],[94.2115,44.6675],[94.276,44.605],[94.3265,44.5825],[94.3605,44.515],[94.4625,44.5105],[94.5165,44.476],[94.6105,44.4425],[94.713,44.345],[94.945,44.288],[94.9985,44.251],[95.41,44.294],[95.4065,44.2415],[95.3725,44.2255],[95.3475,44.089],[95.3225,44.0255],[95.442,44.005],[95.523,44.005],[95.623,43.8515],[95.6475,43.7765],[95.736,43.5945],[95.8575,43.4135],[95.877,43.277],[95.909,43.2355],[96.363,42.899],[96.3875,42.7245],[96.7475,42.755],[96.9775,42.7545],[97.1715,42.7945],[97.5555,42.742],[98.1925,42.652],[98.5425,42.6375],[98.9005,42.61],[99.5045,42.5655],[99.964,42.646],[100.268,42.6355],[100.3185,42.687],[100.848,42.672],[101.567,42.5255],[101.8005,42.5035],[102.0485,42.2525],[102.0245,42.224],[102.0965,42.2],[102.1715,42.204],[102.432,42.147],[102.5505,42.159],[102.767,42.1255],[102.976,42.0375],[103.411,41.8815],[103.891,41.7955],[104.078,41.8005],[104.5235,41.8735],[104.4975,41.7725],[104.499,41.6795],[104.517,41.6595],[104.682,41.644],[104.9165,41.6525],[105.013,41.581],[105.108,41.6545],[105.217,41.7255],[105.289,41.747],[105.386,41.795],[105.697,41.931],[106.507,42.194],[106.5595,42.219],[106.786,42.2915],[107.041,42.3145],[107.257,42.363],[107.293,42.408],[107.453,42.4565],[107.496,42.455],[107.566,42.4115],[107.7275,42.412],[107.919,42.4025],[108.0165,42.4315],[108.241,42.4605],[108.306,42.436],[108.5275,42.441],[108.716,42.4095],[108.7955,42.414],[108.8385,42.3955],[108.96,42.4415],[109.0245,42.454],[109.284,42.4345],[109.469,42.4535],[109.5285,42.4675],[109.669,42.5515],[109.9035,42.6305],[110.101,42.639],[110.1365,42.672],[110.334,42.736],[110.436,42.782],[110.464,42.8325],[110.6295,42.9395],[110.6825,43.035],[110.759,43.095],[111.006,43.322],[111.213,43.3995],[111.327,43.425],[111.4015,43.4765],[111.4535,43.4895],[111.573,43.491],[111.799,43.6695],[111.883,43.672],[111.946,43.69],[111.968,43.744],[111.9545,43.8275],[111.874,43.932],[111.788,43.996],[111.693,44.0355],[111.609,44.106],[111.539,44.192],[111.519,44.271],[111.425,44.3175],[111.4095,44.3445],[111.4635,44.4715],[111.561,44.5715],[111.5535,44.6455],[111.605,44.756],[111.7695,44.9805],[111.874,45.038],[112.009,45.092],[112.1115,45.0715],[112.3745,45.0615],[112.4255,45.074],[112.519,45.0125],[112.585,44.936],[112.702,44.881],[112.8635,44.837],[112.931,44.8395],[113.1275,44.7955],[113.506,44.772],[113.6255,44.7425],[113.7765,44.839],[113.877,44.88],[113.907,44.9145],[114.056,44.9255],[114.1335,44.9665],[114.177,45.034],[114.3845,45.1435],[114.456,45.206],[114.528,45.2925],[114.541,45.3845],[114.737,45.4375],[114.9155,45.3815],[114.974,45.376],[115.165,45.396],[115.356,45.3895],[115.5575,45.436],[115.691,45.458],[115.932,45.6295],[116.015,45.656],[116.0305,45.6825],[116.17,45.6885],[116.2145,45.7415],[116.28,45.7705],[116.268,45.852],[116.2375,45.8725],[116.264,45.9615],[116.398,46.126],[116.4345,46.1375],[116.566,46.2535],[116.5775,46.2885],[116.658,46.321],[116.761,46.329],[116.8315,46.3835],[117.0885,46.3545],[117.243,46.365],[117.3685,46.3605],[117.392,46.478],[117.4405,46.5255],[117.415,46.5805],[117.494,46.5985],[117.6325,46.579],[117.702,46.5135],[117.834,46.5315],[117.889,46.5915],[118.0565,46.6345],[118.1155,46.6785],[118.184,46.6815],[118.301,46.7385],[118.4025,46.727],[118.437,46.7065],[118.5755,46.6935],[118.635,46.72],[118.6735,46.696],[118.792,46.687],[118.7835,46.717],[118.8425,46.7705],[118.897,46.776],[118.916,46.724],[119.0,46.751],[119.101,46.6475],[119.245,46.643],[119.3005,46.6085],[119.3835,46.6085],[119.4305,46.6385],[119.6375,46.625],[119.6805,46.5905],[119.7745,46.6255],[119.807,46.6675],[119.89,46.6635],[119.929,46.7205],[119.9015,46.7555],[119.932,46.7955],[119.8985,46.8655],[119.9215,46.9025],[119.855,46.9205],[119.7835,47.0245],[119.799,47.059],[119.708,47.1935],[119.6235,47.244],[119.571,47.2435],[119.5525,47.301],[119.4775,47.3275],[119.4635,47.358],[119.3215,47.425],[119.3575,47.4795],[119.143,47.5425],[119.1215,47.664],[118.755,47.772],[118.555,47.9935],[118.455,47.996],[118.4145,48.014],[118.2885,48.0035],[118.231,48.041],[118.1,48.0315],[118.0345,48.0125],[117.9135,48.0215],[117.805,48.014],[117.482,47.759],[117.375,47.64],[117.087,47.8205],[116.871,47.891],[116.6765,47.889],[116.4395,47.838],[116.2535,47.8785],[116.1115,47.819],[115.9555,47.6875],[115.9355,47.681],[115.742,47.8035],[115.5705,47.919],[115.5455,48.067],[115.5215,48.111],[115.5475,48.1615],[115.8135,48.259],[115.7985,48.4635],[115.811,48.547],[116.0205,48.757],[116.0725,48.8165],[116.0365,48.8675],[116.3255,49.289],[116.609,49.707],[116.7135,49.8455],[116.6215,49.931],[116.527,49.947],[116.3435,49.996],[116.233,50.036],[116.0725,50.0135],[115.983,49.9705],[115.7445,49.8875],[115.6825,49.886],[115.5065,49.907],[115.361,49.94],[115.224,49.9885],[115.0675,50.094],[115.0075,50.164],[114.831,50.2235],[114.617,50.2455],[114.4675,50.2335],[114.339,50.2825],[114.1785,50.243],[113.8785,50.105],[113.7425,50.075],[113.652,49.9995],[113.489,49.9735],[113.474,49.9355],[113.2185,49.837],[113.044,49.608],[112.907,49.5685],[112.8065,49.515],[112.689,49.504],[112.4865,49.524],[112.2785,49.4845],[112.1945,49.4465],[112.0075,49.3945],[111.909,49.3765],[111.788,49.3845],[111.6685,49.38],[111.515,49.323],[111.375,49.3575],[111.277,49.3105],[111.0785,49.262],[110.8195,49.182],[110.6835,49.171],[110.476,49.2015],[110.3795,49.247],[110.328,49.199],[110.226,49.158],[109.9025,49.2055],[109.8535,49.2015],[109.684,49.226],[109.559,49.221],[109.487,49.243],[109.4125,49.3],[109.326,49.329],[109.171,49.353],[109.058,49.323],[108.9555,49.345],[108.718,49.335],[108.6525,49.3475],[108.608,49.3275],[108.5175,49.3225],[108.431,49.372],[108.426,49.4135],[108.3565,49.428],[108.304,49.532],[108.161,49.562],[108.0515,49.6175],[108.0125,49.667],[107.9685,49.655],[107.9345,49.7115],[107.9675,49.735],[107.919,49.8755],[107.969,49.9415],[107.794,49.933],[107.729,49.9825],[107.5985,49.97],[107.383,49.9775],[107.349,49.9995],[107.211,50.0],[107.072,50.0685],[107.0,50.161],[106.9795,50.209],[106.8155,50.298],[106.5915,50.3385],[106.51,50.3415],[106.4475,50.3145],[106.3955,50.32],[106.261,50.298],[106.183,50.3395],[106.084,50.3335],[106.0745,50.3895],[105.989,50.416],[105.899,50.4125],[105.844,50.4405],[105.705,50.4265],[105.609,50.429],[105.525,50.458],[105.4265,50.444],[105.3475,50.476],[105.2985,50.472],[105.198,50.4115],[105.12,50.39],[105.008,50.387],[104.89,50.408],[104.8765,50.375],[104.8115,50.341],[104.699,50.367],[104.65,50.324],[104.557,50.307],[104.3965,50.3125],[104.3655,50.265],[104.3035,50.2445],[104.2785,50.201],[104.108,50.1405],[103.9985,50.1505],[103.9455,50.179],[103.8465,50.1975],[103.8005,50.1515],[103.6085,50.144],[103.514,50.199],[103.425,50.2115],[103.294,50.186],[103.2565,50.193],[103.2765,50.285],[103.238,50.322],[103.1765,50.3325],[102.942,50.3055],[102.7585,50.379],[102.727,50.408],[102.6475,50.402],[102.6105,50.5065],[102.547,50.527],[102.5255,50.5945],[102.479,50.5975],[102.446,50.6455],[102.3955,50.6445],[102.3025,50.682],[102.3405,50.7325],[102.295,50.7795],[102.2095,50.817],[102.211,50.873],[102.241,50.9175],[102.2035,51.0395],[102.151,51.0575],[102.1235,51.139],[102.138,51.1765],[102.1195,51.2795],[102.1685,51.3185],[102.1245,51.3615],[101.928,51.4165],[101.8935,51.401],[101.749,51.4555],[101.6205,51.4435],[101.6225,51.469],[101.5,51.5035],[101.439,51.4555],[101.37,51.456],[101.243,51.5475],[101.1435,51.52],[100.99,51.613],[100.93,51.607],[100.739,51.678],[100.688,51.7115],[100.529,51.75],[100.4105,51.73],[100.3535,51.7415],[100.201,51.743],[100.1035,51.7545],[100.033,51.7335],[99.978,51.7535],[99.9095,51.7455],[99.8195,51.848],[99.801,51.9155],[99.6255,51.8925],[99.52,51.9685],[99.4985,51.9515],[99.3055,51.9575],[99.276,52.0185],[99.1735,52.0355],[99.1325,52.024],[99.0715,52.0695],[98.9975,52.0865],[98.969,52.1425],[98.8615,52.136],[98.8415,52.059],[98.8065,52.0255],[98.809,51.958],[98.78,51.942],[98.7645,51.87],[98.6125,51.8065],[98.5215,51.7785],[98.4525,51.7365],[98.3785,51.74],[98.327,51.692],[98.234,51.5475],[98.2565,51.5115],[98.224,51.4565],[98.126,51.4715],[98.05,51.45],[98.017,51.3895],[97.9535,51.334],[97.962,51.276],[97.9395,51.202],[97.899,51.1495],[97.866,51.046],[97.828,51.019],[97.858,50.9595],[97.998,50.876],[98.0085,50.8365],[97.9785,50.784],[98.008,50.669],[98.1465,50.575],[98.274,50.538],[98.3275,50.474],[98.2725,50.41],[98.308,50.3235],[98.253,50.2945],[98.2285,50.236],[98.1695,50.202],[98.1825,50.158],[98.11,50.1315],[98.1205,50.1],[98.0635,50.07],[98.07,50.036],[97.982,50.019],[97.898,49.984],[97.8295,49.923],[97.7805,49.9935],[97.555,49.9225],[97.5805,49.8585],[97.5105,49.8145],[97.4215,49.7805],[97.3575,49.7815],[97.318,49.752],[97.231,49.743],[97.123,49.795],[97.117,49.82],[97.01,49.8335],[96.999,49.8885],[96.741,49.9075],[96.71,49.9285],[96.6005,49.8775],[96.549,49.9435],[96.5145,49.943],[96.417,49.877],[96.3485,49.9055],[96.2605,49.9765],[96.0175,50.0035],[95.9675,49.971],[95.9155,50.0215],[95.844,50.0435],[95.7655,50.017],[95.7955,49.964],[95.666,49.9645],[95.5725,49.9445],[95.531,49.8975],[95.426,49.9575],[95.0815,49.963],[95.0165,49.9805],[94.999,50.0445],[94.7605,50.055],[94.615,50.0355],[94.5435,50.1175],[94.522,50.1725],[94.4215,50.1915],[94.389,50.215],[94.344,50.4915],[94.2835,50.572],[94.2405,50.588],[94.031,50.5965],[93.941,50.59],[93.856,50.601],[93.7035,50.586],[93.636,50.607],[93.5445,50.602],[93.3975,50.6345],[93.249,50.594],[93.05,50.601],[92.975,50.6695],[93.023,50.716],[93.032,50.768],[92.854,50.81],[92.7875,50.8055],[92.754,50.7275],[92.628,50.7035],[92.6185,50.768],[92.571,50.798],[92.477,50.7865],[92.432,50.803],[92.373,50.88],[92.313,50.8765],[92.316,50.754],[92.172,50.7065],[92.0725,50.6965],[91.855,50.734],[91.7235,50.6975],[91.661,50.64],[91.65,50.586],[91.5345,50.5815],[91.455,50.5485],[91.459,50.5085],[91.4005,50.4765],[91.2935,50.4695],[91.16,50.42],[91.093,50.436],[90.9695,50.4295],[90.903,50.4085],[90.911,50.353],[90.8195,50.3545],[90.7065,50.313],[90.731,50.2435],[90.6735,50.2145],[90.5805,50.244],[90.504,50.235],[90.453,50.197],[90.389,50.1925],[90.285,50.111],[90.165,50.108],[90.0025,50.054],[90.0175,49.9905],[89.883,49.953],[89.7335,49.948],[89.6295,49.919],[89.6115,49.8635],[89.629,49.8085],[89.719,49.772],[89.719,49.7285],[89.661,49.7],[89.459,49.665],[89.36,49.5845],[89.241,49.645],[89.189,49.6325],[89.1915,49.586],[89.2325,49.5535],[89.201,49.523],[88.9845,49.4635],[88.9325,49.517],[88.88,49.478],[88.733,49.4515],[88.591,49.501],[88.495,49.472],[88.411,49.4935],[88.331,49.4935],[88.2965,49.471],[88.222,49.4795],[88.1655,49.4455],[88.185,49.4185],[88.1195,49.3915],[88.1605,49.363],[88.169,49.2925],[88.018,49.2265],[87.9885,49.183],[87.8145,49.17]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/4c/Flag_of_Mongolia.svg","name:en":"Mongolia","wikidata":"Q711","ISO3166-1:alpha2":"MN","ISO3166-1:alpha3":"MNG","ISO3166-1:numeric":"496"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-118.599,29.163],[-118.582,29.02],[-118.542,28.9555],[-118.5285,28.8485],[-118.4795,28.739],[-118.4185,28.685],[-118.3015,28.649],[-118.189,28.666],[-118.111,28.716],[-118.055,28.7865],[-118.003,28.886],[-117.9915,28.955],[-118.0035,29.115],[-118.03,29.1725],[-118.032,29.2265],[-118.0585,29.295],[-118.091,29.334],[-118.169,29.3815],[-118.294,29.395],[-118.4105,29.3675],[-118.486,29.3365],[-118.54,29.2975],[-118.5835,29.2355],[-118.599,29.163]]],[[[-117.465,32.5905],[-117.5175,32.527],[-117.5375,32.4415],[-117.5115,32.349],[-117.47,32.2975],[-117.358,32.214],[-117.2455,32.1875],[-117.205,32.1395],[-117.122,32.081],[-117.113,31.9795],[-117.0705,31.8975],[-117.0365,31.866],[-117.04,31.764],[-117.0035,31.6935],[-116.924,31.609],[-116.926,31.529],[-116.9005,31.459],[-116.769,31.3175],[-116.698,31.2765],[-116.648,31.2115],[-116.5755,31.1505],[-116.5515,31.107],[-116.568,30.941],[-116.541,30.87],[-116.457,30.791],[-116.3785,30.76],[-116.275,30.68],[-116.2665,30.652],[-116.3265,30.594],[-116.3545,30.5345],[-116.354,30.4375],[-116.321,30.3705],[-116.224,30.2975],[-116.155,30.2115],[-116.031,30.161],[-116.021,30.071],[-116.044,29.934],[-116.018,29.8555],[-116.034,29.792],[-116.0045,29.6715],[-115.9315,29.5825],[-115.838,29.533],[-115.7425,29.524],[-115.7025,29.5065],[-115.6405,29.4495],[-115.537,29.4125],[-115.4565,29.369],[-115.4045,29.3575],[-115.34,29.2765],[-115.272,29.2405],[-115.1465,29.2215],[-115.0925,29.1975],[-115.0535,29.1475],[-114.9395,29.046],[-114.8675,28.958],[-114.818,28.927],[-114.7815,28.857],[-114.667,28.757],[-114.6035,28.728],[-114.5135,28.594],[-114.4155,28.505],[-114.363,28.4775],[-114.2985,28.4095],[-114.3465,28.308],[-114.353,28.2435],[-114.3385,28.1855],[-114.4715,28.031],[-114.5355,27.9875],[-114.613,27.9805],[-114.672,28.002],[-114.8435,28.0295],[-114.9495,28.037],[-114.925,28.184],[-114.9605,28.343],[-115.027,28.477],[-115.0925,28.5375],[-115.196,28.577],[-115.288,28.5755],[-115.3965,28.534],[-115.44,28.4975],[-115.495,28.5135],[-115.6355,28.517],[-115.7515,28.467],[-115.804,28.405],[-115.824,28.3495],[-115.8255,28.272],[-115.806,28.216],[-115.764,28.1625],[-115.679,28.112],[-115.6095,28.097],[-115.5815,28.0165],[-115.5315,27.9595],[-115.451,27.9155],[-115.4405,27.836],[-115.401,27.7715],[-115.324,27.7015],[-115.2325,27.6585],[-115.1705,27.579],[-115.0265,27.4935],[-114.9445,27.435],[-114.89,27.375],[-114.843,27.346],[-114.7145,27.2925],[-114.713,27.224],[-114.694,27.148],[-114.6535,27.09],[-114.586,27.027],[-114.4855,26.965],[-114.343,26.9035],[-114.2345,26.9045],[-114.1505,26.818],[-114.0355,26.772],[-113.964,26.7735],[-113.93,26.7005],[-113.8785,26.6455],[-113.766,26.5625],[-113.7005,26.53],[-113.5905,26.5045],[-113.48,26.5265],[-113.4205,26.556],[-113.371,26.5235],[-113.2945,26.502],[-113.214,26.4505],[-113.108,26.3675],[-113.024,26.3235],[-112.8745,26.2125],[-112.824,26.16],[-112.7085,26.1165],[-112.6225,26.0635],[-112.528,26.0305],[-112.5125,25.988],[-112.45,25.912],[-112.3875,25.8735],[-112.3245,25.7155],[-112.3245,25.4645],[-112.34,25.365],[-112.3725,25.2245],[-112.42,25.06],[-112.4685,24.9425],[-112.5075,24.8855],[-112.5245,24.7855],[-112.495,24.693],[-112.381,24.5815],[-112.3055,24.4895],[-112.2715,24.4315],[-112.1905,24.373],[-112.092,24.3265],[-111.9015,24.2025],[-111.8645,24.1605],[-111.7845,24.1135],[-111.6975,24.1015],[-111.6415,24.1105],[-111.5635,24.1515],[-111.351,24.051],[-111.181,23.9475],[-111.0645,23.8555],[-110.8395,23.621],[-110.745,23.5455],[-110.643,23.4855],[-110.498,23.4265],[-110.451,23.37],[-110.417,23.2975],[-110.3765,23.245],[-110.3655,23.1805],[-110.3005,22.942],[-110.244,22.8345],[-110.1785,22.7635],[-110.042,22.6815],[-109.959,22.6705],[-109.8455,22.679],[-109.7155,22.742],[-109.6355,22.8165],[-109.5425,22.8835],[-109.5015,22.8935],[-109.4135,22.9425],[-109.369,22.9815],[-109.2745,23.0925],[-109.2285,23.186],[-109.1985,23.3625],[-109.2,23.444],[-109.2175,23.5125],[-109.2515,23.5745],[-109.276,23.6495],[-109.3215,23.7035],[-109.4275,23.7675],[-109.4825,23.7885],[-109.486,23.8365],[-109.519,23.9105],[-109.589,23.9825],[-109.5925,24.071],[-109.5755,24.1845],[-109.6145,24.295],[-109.7305,24.4765],[-109.7955,24.5425],[-109.9075,24.5805],[-110.02,24.5625],[-110.086,24.5195],[-110.1125,24.598],[-110.1885,24.676],[-110.25,24.7535],[-110.341,24.7965],[-110.3145,24.9045],[-110.3535,25.0135],[-110.3365,25.08],[-110.352,25.171],[-110.4145,25.2505],[-110.483,25.287],[-110.487,25.3605],[-110.515,25.4215],[-110.605,25.4955],[-110.582,25.5425],[-110.5745,25.6285],[-110.608,25.7765],[-110.6555,25.8375],[-110.7415,25.8855],[-110.822,25.8965],[-110.8785,25.888],[-110.8595,25.963],[-110.8635,26.0805],[-110.8785,26.1425],[-110.95,26.2275],[-110.9965,26.252],[-111.112,26.267],[-111.159,26.2975],[-111.1735,26.384],[-111.225,26.4735],[-111.1955,26.5565],[-111.1845,26.624],[-111.192,26.679],[-111.2255,26.746],[-111.266,26.786],[-111.3435,26.825],[-111.3975,26.8345],[-111.462,26.8775],[-111.5725,26.9805],[-111.697,27.068],[-111.7175,27.1355],[-111.815,27.2455],[-111.724,27.289],[-111.686,27.3265],[-111.6475,27.406],[-111.6585,27.518],[-111.7195,27.597],[-111.776,27.6315],[-111.8775,27.6615],[-112.008,27.6415],[-112.081,27.592],[-112.1355,27.619],[-112.2155,27.708],[-112.3065,27.766],[-112.457,27.827],[-112.5015,27.8675],[-112.5315,27.9325],[-112.5315,28.012],[-112.5725,28.1295],[-112.562,28.231],[-112.587,28.2945],[-112.6265,28.3415],[-112.617,28.441],[-112.5645,28.4215],[-112.568,28.337],[-112.524,28.245],[-112.4385,28.182],[-112.317,28.163],[-112.2405,28.1825],[-112.166,28.233],[-112.122,28.2975],[-112.104,28.3855],[-112.14,28.4915],[-112.178,28.533],[-112.101,28.596],[-112.053,28.5485],[-111.967,28.486],[-111.915,28.367],[-111.839,28.2905],[-111.664,28.233],[-111.5675,28.112],[-111.606,28.05],[-111.617,27.96],[-111.5685,27.847],[-111.5185,27.8005],[-111.421,27.758],[-111.3535,27.753],[-111.25,27.7815],[-110.963,27.6495],[-110.858,27.6395],[-110.8565,27.5905],[-110.8185,27.49],[-110.833,27.3465],[-110.8085,27.236],[-110.7625,27.174],[-110.699,27.1305],[-110.6,27.0935],[-110.5495,27.0855],[-110.4695,27.011],[-110.344,26.952],[-110.21,26.9235],[-110.138,26.887],[-110.097,26.8345],[-110.0455,26.6885],[-110.0055,26.6325],[-109.8955,26.545],[-109.7765,26.493],[-109.699,26.474],[-109.5885,26.4865],[-109.535,26.4735],[-109.482,26.429],[-109.479,26.3825],[-109.5145,26.2875],[-109.6035,26.158],[-109.6615,26.0215],[-109.672,25.93],[-109.651,25.854],[-109.6555,25.7815],[-109.6245,25.6985],[-109.623,25.616],[-109.5935,25.5335],[-109.5405,25.478],[-109.4745,25.445],[-109.3435,25.437],[-109.265,25.377],[-109.221,25.322],[-109.125,25.256],[-108.988,25.2315],[-108.853,25.1705],[-108.7545,25.1605],[-108.602,25.1045],[-108.564,25.026],[-108.4045,24.901],[-108.345,24.8015],[-108.334,24.736],[-108.301,24.679],[-108.23,24.622],[-108.1845,24.5415],[-108.1305,24.486],[-107.9915,24.396],[-107.927,24.328],[-107.829,24.2755],[-107.6935,24.1895],[-107.5225,24.061],[-107.415,23.9995],[-107.269,23.894],[-107.1575,23.8035],[-107.0955,23.7415],[-107.028,23.626],[-106.997,23.546],[-106.9575,23.502],[-106.8685,23.4355],[-106.7975,23.351],[-106.713,23.275],[-106.675,23.1585],[-106.6275,23.0795],[-106.581,23.03],[-106.477,22.98],[-106.2905,22.809],[-106.1815,22.684],[-106.082,22.619],[-105.9925,22.527],[-105.98,22.469],[-105.8925,22.315],[-105.8715,22.2305],[-105.8695,22.09],[-105.984,22.077],[-106.0565,22.0335],[-106.097,21.9825],[-106.123,21.9005],[-106.115,21.8225],[-106.097,21.779],[-106.032,21.706],[-105.9225,21.6645],[-105.838,21.6715],[-105.7375,21.73],[-105.7085,21.684],[-105.73,21.634],[-105.7365,21.565],[-105.6905,21.4575],[-105.641,21.4145],[-105.5725,21.386],[-105.46,21.388],[-105.459,21.3065],[-105.4395,21.241],[-105.449,21.1925],[-105.518,21.1015],[-105.611,21.0125],[-105.7275,20.8665],[-105.8245,20.811],[-105.857,20.769],[-105.8835,20.67],[-105.8755,20.616],[-105.8435,20.5545],[-105.884,20.5025],[-105.908,20.403],[-105.8815,20.285],[-105.8275,20.1975],[-105.7615,20.128],[-105.761,20.0635],[-105.7405,20.0035],[-105.6655,19.8715],[-105.558,19.738],[-105.5085,19.6385],[-105.3435,19.4685],[-105.279,19.419],[-105.2335,19.3155],[-105.1995,19.262],[-105.1165,19.1675],[-105.0515,19.123],[-104.999,19.107],[-104.925,19.0505],[-104.838,19.024],[-104.747,18.9795],[-104.629,18.951],[-104.581,18.9205],[-104.4955,18.89],[-104.431,18.84],[-104.23,18.7715],[-104.0665,18.681],[-103.9325,18.584],[-103.924,18.5335],[-103.866,18.4435],[-103.7705,18.381],[-103.733,18.2605],[-103.684,18.1915],[-103.5835,18.1245],[-103.5285,18.111],[-103.4545,18.0715],[-103.3745,18.055],[-103.153,17.98],[-103.0815,17.978],[-102.853,17.8935],[-102.725,17.859],[-102.67,17.8575],[-102.581,17.821],[-102.3265,17.754],[-102.1145,17.719],[-101.9985,17.7535],[-101.959,17.73],[-101.9265,17.6795],[-101.868,17.623],[-101.8295,17.547],[-101.749,17.478],[-101.6865,17.4465],[-101.609,17.3635],[-101.5085,17.3315],[-101.3385,17.2605],[-101.273,17.21],[-101.2155,17.1205],[-101.15,17.0805],[-100.3915,16.835],[-100.2015,16.7595],[-100.1075,16.734],[-100.0475,16.663],[-99.881,16.567],[-99.7335,16.5065],[-99.621,16.4875],[-99.5075,16.4895],[-99.3495,16.4435],[-99.1035,16.403],[-99.0365,16.3735],[-98.8735,16.335],[-98.751,16.228],[-98.715,16.183],[-98.6495,16.141],[-98.4655,16.072],[-98.298,16.0425],[-98.1615,15.987],[-97.9065,15.787],[-97.8445,15.7625],[-97.6955,15.741],[-97.537,15.7385],[-97.3745,15.7065],[-97.295,15.713],[-97.231,15.7005],[-97.173,15.648],[-97.1035,15.6075],[-97.0355,15.5885],[-96.934,15.536],[-96.746,15.502],[-96.61,15.4495],[-96.5005,15.439],[-96.39,15.4675],[-96.2445,15.462],[-96.1675,15.473],[-95.9615,15.571],[-95.865,15.641],[-95.703,15.6835],[-95.5165,15.7575],[-95.348,15.796],[-95.2585,15.8485],[-95.189,15.9205],[-95.1255,15.9645],[-94.897,16.0125],[-94.8135,16.015],[-94.573,15.9945],[-94.454,15.972],[-94.3285,15.9245],[-94.1615,15.8315],[-93.9965,15.781],[-93.951,15.7765],[-93.8055,15.69],[-93.5465,15.5],[-93.256,15.246],[-93.044,15.044],[-93.0165,15.0095],[-92.769,14.76],[-92.3745,14.3885],[-92.3105,14.451],[-92.186,14.5725],[-92.147,14.6585],[-92.159,14.769],[-92.185,14.8285],[-92.136,14.893],[-92.1485,14.9485],[-92.133,15.0115],[-92.102,15.011],[-92.0595,15.071],[-92.2105,15.2605],[-92.017,15.5895],[-91.7315,16.074],[-90.961,16.074],[-90.442,16.074],[-90.4265,16.1625],[-90.455,16.189],[-90.4395,16.3045],[-90.4105,16.3095],[-90.3985,16.3745],[-90.42,16.4245],[-90.4885,16.4285],[-90.5035,16.4685],[-90.586,16.47],[-90.637,16.5135],[-90.659,16.6425],[-90.683,16.6895],[-90.7645,16.7635],[-90.847,16.796],[-90.8825,16.825],[-90.92,16.821],[-90.9575,16.897],[-91.009,16.887],[-91.0645,16.925],[-91.2035,17.045],[-91.2325,17.1],[-91.2685,17.11],[-91.278,17.179],[-91.3255,17.1825],[-91.429,17.2185],[-91.439,17.251],[-90.9875,17.2515],[-90.9875,17.8155],[-90.2455,17.816],[-89.5155,17.8155],[-89.152,17.8155],[-89.152,17.9395],[-89.1325,17.9685],[-89.0375,18.005],[-88.993,17.9595],[-88.9455,17.9525],[-88.9205,17.916],[-88.8575,17.9245],[-88.8165,17.9495],[-88.767,18.016],[-88.7165,18.061],[-88.7185,18.108],[-88.6735,18.194],[-88.6155,18.2235],[-88.606,18.2915],[-88.548,18.355],[-88.517,18.464],[-88.478,18.4935],[-88.4435,18.48],[-88.3275,18.4905],[-88.25,18.473],[-88.2495,18.4155],[-88.0305,18.415],[-88.031,18.1675],[-87.9325,18.1675],[-87.91,18.1505],[-87.868,18.198],[-87.744,18.1165],[-87.435,17.924],[-87.375,17.917],[-87.385,18.198],[-87.538,18.223],[-87.6205,18.2465],[-87.563,18.348],[-87.548,18.4575],[-87.4435,18.3945],[-87.3645,18.375],[-87.3045,18.378],[-87.2115,18.4165],[-87.134,18.5025],[-87.113,18.5895],[-87.1245,18.667],[-87.102,18.7675],[-87.129,18.858],[-87.162,18.901],[-87.223,18.943],[-87.338,18.9625],[-87.385,18.952],[-87.349,19.031],[-87.3355,19.1425],[-87.271,19.221],[-87.2485,19.282],[-87.257,19.383],[-87.21,19.543],[-87.21,19.6425],[-87.2565,19.7465],[-87.221,19.8315],[-87.212,19.8935],[-87.231,19.999],[-87.263,20.0665],[-87.2435,20.112],[-87.192,20.177],[-87.138,20.12],[-87.0885,20.0875],[-87.02,20.0685],[-86.9555,20.0705],[-86.872,20.101],[-86.776,20.173],[-86.649,20.316],[-86.577,20.413],[-86.54,20.485],[-86.514,20.573],[-86.528,20.6705],[-86.587,20.75],[-86.6655,20.7915],[-86.6275,20.889],[-86.5955,20.93],[-86.4965,21.1655],[-86.494,21.2175],[-86.511,21.2805],[-86.5765,21.392],[-86.5755,21.505],[-86.6205,21.6405],[-86.693,21.7075],[-86.76,21.732],[-86.864,21.729],[-86.9485,21.773],[-87.044,21.8035],[-87.1485,21.8075],[-87.262,21.754],[-87.3655,21.7635],[-87.422,21.751],[-87.516,21.691],[-87.7305,21.737],[-87.929,21.806],[-88.109,21.8285],[-88.206,21.82],[-88.3135,21.767],[-88.4425,21.7665],[-88.5695,21.733],[-88.6655,21.721],[-88.747,21.687],[-88.822,21.6225],[-88.9235,21.5945],[-89.1675,21.556],[-89.4145,21.534],[-89.618,21.4985],[-89.7155,21.4895],[-89.9075,21.4435],[-90.03,21.396],[-90.0785,21.3665],[-90.214,21.3275],[-90.436,21.196],[-90.4965,21.1425],[-90.554,21.063],[-90.6605,20.7795],[-90.6695,20.669],[-90.6995,20.577],[-90.7085,20.506],[-90.699,20.4175],[-90.7085,20.3755],[-90.71,20.1575],[-90.7015,20.103],[-90.722,20.063],[-90.732,19.968],[-90.812,19.9085],[-90.847,19.8645],[-90.894,19.7735],[-90.911,19.7185],[-90.909,19.63],[-90.9205,19.563],[-90.9165,19.48],[-90.933,19.428],[-91.025,19.3515],[-91.329,19.155],[-91.486,19.092],[-91.6005,19.0135],[-91.642,18.969],[-91.7375,18.93],[-91.8385,18.874],[-91.901,18.9065],[-92.0,18.9185],[-92.369,18.8785],[-92.511,18.852],[-92.635,18.8145],[-92.7325,18.8145],[-92.793,18.791],[-92.8715,18.721],[-92.8995,18.6735],[-92.9915,18.637],[-93.337,18.653],[-93.46,18.648],[-93.5225,18.6355],[-93.9135,18.4945],[-94.2005,18.4065],[-94.366,18.365],[-94.4245,18.3595],[-94.4585,18.414],[-94.539,18.5],[-94.598,18.6155],[-94.6675,18.6945],[-94.709,18.72],[-94.804,18.75],[-94.8945,18.7535],[-94.9445,18.806],[-95.097,18.901],[-95.1965,18.9215],[-95.305,18.9255],[-95.458,18.915],[-95.581,18.9275],[-95.712,18.9935],[-95.7485,19.021],[-95.736,19.0755],[-95.762,19.189],[-95.844,19.2665],[-95.871,19.3315],[-95.9315,19.393],[-95.9965,19.4245],[-96.103,19.436],[-96.1055,19.5045],[-96.129,19.5765],[-96.1655,19.6365],[-96.191,19.7915],[-96.26,19.954],[-96.3585,20.0565],[-96.394,20.1225],[-96.452,20.1785],[-96.5385,20.29],[-96.6395,20.3985],[-96.769,20.5255],[-96.8085,20.582],[-96.961,20.733],[-97.041,20.933],[-97.081,20.9885],[-97.2015,21.2295],[-97.1865,21.309],[-97.113,21.3295],[-97.0715,21.3555],[-97.0205,21.4145],[-96.9945,21.5195],[-97.0105,21.587],[-97.045,21.6405],[-97.1225,21.714],[-97.215,21.7505],[-97.3965,21.9045],[-97.5325,22.0955],[-97.5725,22.2095],[-97.5665,22.2675],[-97.585,22.3425],[-97.621,22.42],[-97.635,22.4875],[-97.6305,22.6005],[-97.57,22.761],[-97.5455,22.866],[-97.5395,23.0615],[-97.548,23.259],[-97.544,23.4215],[-97.5175,23.756],[-97.5105,24.0665],[-97.487,24.3145],[-97.4645,24.457],[-97.411,24.6625],[-97.3195,24.948],[-97.2305,25.1475],[-96.989,25.578],[-96.969,25.63],[-96.933,25.7925],[-96.924,25.9755],[-97.178,25.962],[-97.2755,25.952],[-97.367,25.9035],[-97.3715,25.842],[-97.4435,25.848],[-97.456,25.884],[-97.5105,25.8875],[-97.556,25.937],[-97.6675,26.0245],[-97.779,26.03],[-97.793,26.0535],[-97.875,26.0635],[-97.9645,26.052],[-98.0005,26.0655],[-98.0385,26.0415],[-98.105,26.0675],[-98.1585,26.0535],[-98.249,26.072],[-98.3225,26.1195],[-98.3385,26.153],[-98.3845,26.157],[-98.4805,26.22],[-98.678,26.2435],[-98.7445,26.3155],[-98.789,26.3305],[-98.825,26.3705],[-98.889,26.357],[-98.9785,26.4],[-99.04,26.413],[-99.0835,26.3975],[-99.111,26.427],[-99.0915,26.476],[-99.1275,26.525],[-99.1675,26.5365],[-99.178,26.6205],[-99.2,26.656],[-99.209,26.725],[-99.24,26.746],[-99.2685,26.8435],[-99.3285,26.8785],[-99.3305,26.9205],[-99.388,26.9425],[-99.3835,26.9775],[-99.445,27.02],[-99.453,27.063],[-99.4285,27.092],[-99.426,27.1755],[-99.442,27.251],[-99.4965,27.2735],[-99.504,27.339],[-99.4785,27.4795],[-99.528,27.499],[-99.511,27.565],[-99.539,27.603],[-99.6005,27.6415],[-99.7215,27.666],[-99.8135,27.7745],[-99.875,27.7965],[-99.8935,27.899],[-99.9375,27.9415],[-99.9335,27.9815],[-99.9905,27.9935],[-100.018,28.065],[-100.0525,28.0845],[-100.0885,28.148],[-100.212,28.1945],[-100.294,28.2845],[-100.288,28.316],[-100.3495,28.404],[-100.336,28.43],[-100.411,28.5495],[-100.3985,28.5855],[-100.447,28.609],[-100.5005,28.662],[-100.5145,28.752],[-100.5465,28.825],[-100.6515,28.946],[-100.6455,28.988],[-100.664,29.075],[-100.685,29.1115],[-100.76,29.1575],[-100.795,29.2435],[-100.8785,29.281],[-100.8865,29.3075],[-101.005,29.3655],[-101.06,29.4585],[-101.1485,29.4755],[-101.193,29.5205],[-101.2505,29.5195],[-101.2415,29.5645],[-101.312,29.5865],[-101.3005,29.638],[-101.3645,29.6655],[-101.3945,29.711],[-101.3995,29.76],[-101.6615,29.771],[-101.71,29.7615],[-101.774,29.789],[-101.8035,29.78],[-101.9545,29.796],[-101.9815,29.815],[-102.0735,29.7865],[-102.144,29.8035],[-102.3005,29.8775],[-102.34,29.8695],[-102.404,29.7655],[-102.4875,29.7865],[-102.548,29.7445],[-102.677,29.7415],[-102.6935,29.6765],[-102.742,29.633],[-102.7785,29.5435],[-102.8085,29.523],[-102.8395,29.359],[-102.877,29.355],[-102.9065,29.2615],[-102.868,29.223],[-102.955,29.1775],[-102.99,29.1835],[-103.032,29.104],[-103.08,29.0875],[-103.098,29.0265],[-103.1525,28.972],[-103.2505,28.9805],[-103.3115,29.0135],[-103.416,29.038],[-103.474,29.0705],[-103.5005,29.116],[-103.557,29.1555],[-103.5925,29.15],[-103.719,29.181],[-103.783,29.264],[-103.975,29.296],[-104.1605,29.39],[-104.209,29.481],[-104.262,29.5135],[-104.3375,29.5195],[-104.3985,29.5715],[-104.5125,29.6465],[-104.5445,29.681],[-104.5655,29.7695],[-104.6335,29.8705],[-104.68,29.9205],[-104.6905,30.016],[-104.687,30.179],[-104.761,30.3],[-104.812,30.333],[-104.811,30.363],[-104.8595,30.3905],[-104.872,30.5105],[-104.8995,30.571],[-104.9715,30.61],[-105.007,30.6865],[-105.0525,30.6815],[-105.13,30.7505],[-105.196,30.792],[-105.2665,30.8085],[-105.387,30.8535],[-105.4,30.8895],[-105.556,30.989],[-105.606,31.085],[-105.649,31.116],[-105.7695,31.165],[-105.8705,31.29],[-105.9315,31.313],[-105.955,31.3655],[-106.0055,31.3925],[-106.0775,31.398],[-106.219,31.4795],[-106.245,31.539],[-106.2795,31.561],[-106.308,31.6295],[-106.382,31.7325],[-106.455,31.7645],[-106.49,31.7485],[-106.529,31.784],[-107.2905,31.7835],[-107.8655,31.7835],[-108.2085,31.7835],[-108.2085,31.3335],[-108.851,31.3325],[-109.2785,31.334],[-109.7465,31.334],[-110.456,31.333],[-111.075,31.332],[-111.864,31.5845],[-112.3995,31.752],[-113.1485,31.9805],[-113.924,32.2235],[-114.4385,32.381],[-114.8135,32.494],[-114.8105,32.61],[-114.765,32.643],[-114.72,32.7185],[-115.1805,32.6875],[-115.718,32.6485],[-116.3375,32.6],[-117.1245,32.5345],[-117.239,32.5275],[-117.264,32.5535],[-117.465,32.5905]]],[[[-115.9705,24.952],[-115.9505,24.868],[-115.9005,24.805],[-115.8455,24.7705],[-115.7525,24.7505],[-115.663,24.767],[-115.599,24.8055],[-115.5415,24.8845],[-115.53,24.975],[-115.569,25.068],[-115.607,25.1065],[-115.6705,25.141],[-115.726,25.153],[-115.801,25.149],[-115.884,25.113],[-115.9375,25.059],[-115.9705,24.952]]],[[[-114.9845,18.363],[-114.9775,18.302],[-114.929,18.2135],[-114.8635,18.164],[-114.8225,18.149],[-114.7115,18.146],[-114.627,18.165],[-114.5485,18.214],[-114.486,18.3085],[-114.4735,18.381],[-114.482,18.4385],[-114.54,18.528],[-114.646,18.5815],[-114.777,18.5825],[-114.8355,18.5715],[-114.9095,18.5325],[-114.942,18.499],[-114.977,18.429],[-114.9845,18.363]]],[[[-112.2835,18.9985],[-112.27,18.93],[-112.233,18.87],[-112.143,18.811],[-112.071,18.799],[-111.999,18.8115],[-111.9355,18.847],[-111.889,18.9005],[-111.864,18.966],[-111.8645,19.036],[-111.89,19.1015],[-111.9375,19.1545],[-112.001,19.189],[-112.0735,19.201],[-112.1455,19.1885],[-112.209,19.153],[-112.271,19.0675],[-112.2835,18.9985]]],[[[-111.279,18.8385],[-111.2655,18.7405],[-111.2245,18.6565],[-111.144,18.585],[-111.0455,18.5245],[-110.9835,18.5085],[-110.9015,18.514],[-110.793,18.564],[-110.7375,18.6305],[-110.69,18.7545],[-110.7045,18.848],[-110.77,18.9855],[-110.8115,19.0365],[-110.905,19.0865],[-110.969,19.092],[-111.0315,19.0795],[-111.188,19.0045],[-111.2565,18.9295],[-111.279,18.8385]]],[[[-111.044,19.3035],[-111.025,19.2165],[-110.9475,19.1295],[-110.8655,19.097],[-110.788,19.0945],[-110.7345,19.107],[-110.6665,19.1475],[-110.6055,19.2375],[-110.595,19.3555],[-110.624,19.4315],[-110.681,19.493],[-110.756,19.5275],[-110.8495,19.5315],[-110.9165,19.51],[-110.9675,19.4735],[-111.021,19.397],[-111.044,19.3035]]],[[[-106.925,21.7435],[-106.912,21.674],[-106.8645,21.6035],[-106.7975,21.4725],[-106.703,21.404],[-106.6535,21.3245],[-106.5805,21.269],[-106.4845,21.2305],[-106.453,21.1805],[-106.3765,21.117],[-106.2885,21.083],[-106.2355,21.0745],[-106.1465,21.088],[-106.079,21.128],[-106.0445,21.166],[-106.0145,21.2315],[-106.005,21.314],[-106.0155,21.383],[-106.055,21.454],[-106.155,21.5215],[-106.197,21.596],[-106.258,21.65],[-106.3025,21.67],[-106.367,21.786],[-106.4185,21.831],[-106.479,21.863],[-106.512,21.9085],[-106.612,21.97],[-106.696,21.9785],[-106.7935,21.948],[-106.881,21.866],[-106.909,21.8205],[-106.925,21.7435]]],[[[-92.5245,20.977],[-92.5025,20.887],[-92.404,20.7665],[-92.311,20.707],[-92.2055,20.6985],[-92.134,20.7225],[-92.067,20.7715],[-92.017,20.8415],[-92.004,20.9525],[-92.0535,21.051],[-92.18,21.1445],[-92.262,21.177],[-92.383,21.1675],[-92.462,21.119],[-92.505,21.061],[-92.5245,20.977]]],[[[-92.1865,20.212],[-92.1695,20.133],[-92.097,20.044],[-92.03,20.0115],[-91.91,20.009],[-91.819,20.057],[-91.7765,20.1085],[-91.751,20.2],[-91.76,20.2655],[-91.8035,20.344],[-91.8765,20.4],[-91.9385,20.4185],[-92.023,20.415],[-92.116,20.368],[-92.1625,20.3095],[-92.1865,20.212]]],[[[-91.6295,22.13],[-91.6115,22.049],[-91.565,21.985],[-91.5185,21.952],[-91.4455,21.928],[-91.351,21.928],[-91.243,21.976],[-91.188,22.045],[-91.169,22.1175],[-91.186,22.21],[-91.2375,22.278],[-91.294,22.3145],[-91.3865,22.334],[-91.478,22.3235],[-91.5665,22.273],[-91.609,22.217],[-91.6295,22.13]]],[[[-90.013,22.574],[-89.9955,22.4935],[-89.9185,22.3165],[-89.8055,22.213],[-89.6995,22.1725],[-89.579,22.1845],[-89.5205,22.2175],[-89.456,22.302],[-89.4405,22.37],[-89.4505,22.4395],[-89.488,22.5075],[-89.5515,22.561],[-89.5585,22.631],[-89.6,22.706],[-89.6575,22.752],[-89.7645,22.783],[-89.882,22.7615],[-89.95,22.7175],[-90.0055,22.627],[-90.013,22.574]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fc/Flag_of_Mexico.svg","name:en":"Mexico","wikidata":"Q96","ISO3166-1:alpha2":"MX","ISO3166-1:alpha3":"MEX","ISO3166-1:numeric":"484"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-62.4475,16.7575],[-62.4435,16.671],[-62.4095,16.604],[-62.3055,16.509],[-62.2095,16.476],[-62.1405,16.477],[-62.057,16.503],[-62.0065,16.541],[-61.9585,16.605],[-61.9405,16.653],[-61.9425,16.775],[-61.9615,16.8375],[-62.109,17.0055],[-62.14,17.0155],[-62.4475,16.7575]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d0/Flag_of_Montserrat.svg","name:en":"Montserrat","wikidata":"Q13353","ISO3166-1:alpha2":"MS","ISO3166-1:alpha3":"MSR","ISO3166-1:numeric":"500"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-174.7435,-4.66],[-174.7185,-4.765],[-174.677,-4.822],[-174.622,-4.861],[-174.5315,-4.893],[-174.457,-4.893],[-174.369,-4.854],[-174.323,-4.804],[-174.298,-4.747],[-174.2955,-4.653],[-174.3155,-4.597],[-174.373,-4.525],[-174.4355,-4.483],[-174.5035,-4.458],[-174.5615,-4.456],[-174.6385,-4.48],[-174.717,-4.558],[-174.7435,-4.66]]],[[[-174.3265,-3.592],[-174.3025,-3.691],[-174.252,-3.753],[-174.1585,-3.796],[-174.1005,-3.798],[-174.0235,-3.774],[-173.9455,-3.696],[-173.9215,-3.63],[-173.9245,-3.548],[-173.945,-3.495],[-174.0235,-3.417],[-174.1005,-3.393],[-174.16,-3.395],[-174.231,-3.422],[-174.3065,-3.506],[-174.3265,-3.592]]],[[[-172.426,-4.515],[-172.417,-4.577],[-172.3835,-4.644],[-172.3,-4.714],[-172.2335,-4.738],[-172.1685,-4.741],[-172.0435,-4.693],[-171.986,-4.647],[-171.959,-4.608],[-171.9355,-4.535],[-171.938,-4.451],[-171.9575,-4.399],[-172.0145,-4.334],[-172.0965,-4.295],[-172.185,-4.288],[-172.2375,-4.299],[-172.3515,-4.359],[-172.4035,-4.424],[-172.426,-4.515]]],[[[-171.9235,-2.789],[-171.895,-2.905],[-171.8675,-2.952],[-171.8195,-2.998],[-171.6555,-3.067],[-171.5765,-3.066],[-171.503,-3.035],[-171.456,-2.99],[-171.421,-2.916],[-171.415,-2.854],[-171.4435,-2.748],[-171.482,-2.679],[-171.5205,-2.643],[-171.6335,-2.579],[-171.6955,-2.566],[-171.758,-2.569],[-171.829,-2.598],[-171.9015,-2.681],[-171.9235,-2.789]]],[[[-171.7215,-3.58],[-171.695,-3.681],[-171.653,-3.736],[-171.556,-3.786],[-171.481,-3.787],[-171.43,-3.771],[-171.37,-3.728],[-171.3205,-3.64],[-171.3135,-3.584],[-171.33,-3.504],[-171.3645,-3.45],[-171.451,-3.392],[-171.514,-3.381],[-171.5865,-3.392],[-171.66,-3.437],[-171.7115,-3.52],[-171.7215,-3.58]]],[[[-171.4635,-4.456],[-171.4595,-4.496],[-171.424,-4.576],[-171.389,-4.615],[-171.331,-4.651],[-171.241,-4.668],[-171.1755,-4.66],[-171.099,-4.62],[-171.063,-4.582],[-171.033,-4.52],[-171.0295,-4.418],[-171.066,-4.337],[-171.138,-4.267],[-171.2065,-4.242],[-171.265,-4.24],[-171.33,-4.258],[-171.4145,-4.321],[-171.459,-4.41],[-171.4635,-4.456]]],[[[-171.292,-3.109],[-171.289,-3.173],[-171.2525,-3.263],[-171.2065,-3.309],[-171.1335,-3.342],[-171.0495,-3.344],[-170.9795,-3.32],[-170.9325,-3.284],[-170.8915,-3.223],[-170.8765,-3.113],[-170.9075,-3.006],[-170.9825,-2.931],[-171.0805,-2.904],[-171.1535,-2.915],[-171.2435,-2.977],[-171.279,-3.037],[-171.292,-3.109]]],[[[-170.9185,-3.716],[-170.91,-3.776],[-170.8735,-3.846],[-170.7975,-3.907],[-170.747,-3.923],[-170.676,-3.923],[-170.612,-3.9],[-170.5325,-3.82],[-170.509,-3.749],[-170.526,-3.637],[-170.5645,-3.581],[-170.6575,-3.525],[-170.711,-3.517],[-170.7835,-3.528],[-170.8295,-3.551],[-170.8995,-3.632],[-170.9185,-3.716]]],[[[-160.61,4.689],[-160.5865,4.596],[-160.5295,4.528],[-160.4545,4.486],[-160.3935,4.471],[-160.3235,4.473],[-160.2305,4.514],[-160.1655,4.597],[-160.149,4.677],[-160.157,4.735],[-160.183,4.794],[-160.2505,4.859],[-160.321,4.888],[-160.403,4.9],[-160.466,4.889],[-160.5555,4.831],[-160.5855,4.789],[-160.61,4.689]]],[[[-159.595,3.899],[-159.5785,3.817],[-159.511,3.71],[-159.4355,3.639],[-159.3665,3.612],[-159.2885,3.607],[-159.196,3.629],[-159.1045,3.691],[-159.051,3.779],[-159.041,3.873],[-159.0685,3.962],[-159.098,4.004],[-159.1965,4.087],[-159.27,4.127],[-159.339,4.14],[-159.4315,4.125],[-159.492,4.092],[-159.541,4.046],[-159.5855,3.961],[-159.595,3.899]]],[[[-157.7615,1.877],[-157.7475,1.799],[-157.686,1.708],[-157.634,1.669],[-157.4985,1.599],[-157.4055,1.576],[-157.328,1.522],[-157.2335,1.494],[-157.138,1.5],[-157.0455,1.542],[-156.9945,1.599],[-156.971,1.655],[-156.966,1.761],[-156.9885,1.841],[-157.051,1.919],[-157.1015,1.951],[-157.116,2.045],[-157.1445,2.097],[-157.19,2.145],[-157.289,2.188],[-157.334,2.219],[-157.4285,2.248],[-157.5495,2.238],[-157.605,2.211],[-157.6685,2.144],[-157.6925,2.083],[-157.697,2.025],[-157.744,1.957],[-157.7615,1.877]]],[[[-156.1355,-5.631],[-156.1205,-5.709],[-156.0745,-5.778],[-155.9495,-5.85],[-155.855,-5.862],[-155.7945,-5.846],[-155.6995,-5.787],[-155.6525,-5.717],[-155.6385,-5.665],[-155.6575,-5.551],[-155.718,-5.476],[-155.783,-5.438],[-155.888,-5.421],[-156.006,-5.445],[-156.093,-5.509],[-156.1225,-5.561],[-156.1355,-5.631]]],[[[-155.171,-4.014],[-155.1415,-4.12],[-155.1085,-4.161],[-155.009,-4.233],[-154.9585,-4.249],[-154.876,-4.247],[-154.802,-4.217],[-154.7305,-4.145],[-154.707,-4.085],[-154.691,-3.977],[-154.7015,-3.925],[-154.734,-3.865],[-154.777,-3.824],[-154.85,-3.792],[-154.934,-3.79],[-155.011,-3.804],[-155.0685,-3.827],[-155.132,-3.885],[-155.1575,-3.934],[-155.171,-4.014]]],[[[-152.515,-10.063],[-152.487,-10.164],[-152.407,-10.2385],[-152.3165,-10.2615],[-152.246,-10.2505],[-152.1685,-10.2025],[-152.12,-10.1235],[-152.1105,-10.069],[-152.121,-9.998],[-152.1755,-9.9145],[-152.2755,-9.8655],[-152.3465,-9.8655],[-152.422,-9.895],[-152.4745,-9.9435],[-152.499,-9.986],[-152.515,-10.063]]],[[[-152.0305,-11.427],[-152.0075,-11.518],[-151.9565,-11.588],[-151.8995,-11.627],[-151.848,-11.643],[-151.775,-11.643],[-151.7095,-11.62],[-151.6295,-11.539],[-151.606,-11.448],[-151.614,-11.39],[-151.655,-11.301],[-151.694,-11.26],[-151.789,-11.217],[-151.871,-11.219],[-151.944,-11.251],[-151.9945,-11.302],[-152.018,-11.347],[-152.0305,-11.427]]],[[[-150.4375,-10.001],[-150.4235,-10.077],[-150.3505,-10.169],[-150.2785,-10.203],[-150.189,-10.207],[-150.1205,-10.182],[-150.056,-10.122],[-150.0135,-10.047],[-149.9925,-9.945],[-150.0015,-9.873],[-150.0395,-9.789],[-150.097,-9.736],[-150.1675,-9.708],[-150.2735,-9.714],[-150.368,-9.778],[-150.402,-9.839],[-150.4375,-10.001]]],[[[169.3205,-0.854],[169.3245,-0.894],[169.3605,-0.978],[169.399,-1.02],[169.4765,-1.063],[169.5465,-1.074],[169.6095,-1.063],[169.6775,-1.023],[169.712,-0.984],[169.746,-0.899],[169.7445,-0.811],[169.72,-0.749],[169.648,-0.676],[169.558,-0.646],[169.4895,-0.649],[169.4375,-0.667],[169.3735,-0.715],[169.336,-0.776],[169.3205,-0.854]]],[[[172.4805,3.273],[172.523,3.069],[172.5795,2.95],[172.608,2.908],[172.665,2.863],[172.756,2.84],[172.8285,2.851],[172.897,2.891],[172.9745,2.906],[173.025,2.935],[173.1505,3.087],[173.1725,3.168],[173.165,3.242],[173.209,3.365],[173.2025,3.447],[173.181,3.496],[173.1275,3.555],[173.052,3.59],[172.9935,3.595],[172.911,3.576],[172.851,3.534],[172.79,3.447],[172.7215,3.472],[172.641,3.472],[172.5545,3.431],[172.507,3.375],[172.4805,3.273]]],[[[172.58,1.898],[172.6065,1.797],[172.6515,1.741],[172.7485,1.691],[172.744,1.608],[172.781,1.503],[172.7405,1.448],[172.7195,1.34],[172.7365,1.272],[172.7705,1.22],[172.8505,1.166],[172.8125,1.109],[172.794,1.015],[172.8015,0.967],[172.776,0.904],[172.7715,0.848],[172.7875,0.774],[172.8125,0.729],[172.874,0.671],[172.981,0.639],[173.0945,0.67],[173.1535,0.721],[173.2595,0.896],[173.272,0.96],[173.26,1.044],[173.2265,1.104],[173.1705,1.159],[173.269,1.191],[173.343,1.273],[173.365,1.357],[173.361,1.413],[173.3375,1.475],[173.301,1.522],[173.1935,1.612],[173.2255,1.666],[173.249,1.773],[173.3475,1.776],[173.409,1.801],[173.449,1.833],[173.4925,1.898],[173.5065,2.012],[173.469,2.122],[173.437,2.178],[173.352,2.244],[173.293,2.259],[173.2235,2.255],[173.13,2.205],[173.089,2.153],[173.064,2.09],[172.9965,2.136],[172.911,2.168],[172.845,2.172],[172.751,2.14],[172.648,2.051],[172.6085,2.003],[172.58,1.898]]],[[[173.1795,0.26],[173.195,0.184],[173.2585,0.077],[173.313,0.026],[173.39,-0.003],[173.4535,-0.005],[173.533,-0.065],[173.617,-0.082],[173.68,-0.071],[173.7605,-0.023],[173.8005,0.025],[173.838,0.119],[173.935,0.105],[174.0275,0.128],[174.0885,0.176],[174.119,0.22],[174.1455,0.315],[174.1435,0.373],[174.098,0.515],[174.057,0.575],[173.9665,0.651],[173.893,0.687],[173.828,0.694],[173.7165,0.661],[173.6625,0.614],[173.582,0.478],[173.561,0.392],[173.459,0.458],[173.3665,0.47],[173.2995,0.451],[173.2545,0.421],[173.1965,0.342],[173.1795,0.26]]],[[[173.9845,-0.553],[174.01,-0.652],[174.081,-0.737],[174.13,-0.764],[174.227,-0.777],[174.2435,-0.861],[174.3225,-0.964],[174.38,-1.0],[174.4795,-1.014],[174.454,-1.084],[174.45,-1.162],[174.482,-1.261],[174.567,-1.35],[174.5945,-1.426],[174.6525,-1.517],[174.7165,-1.565],[174.776,-1.583],[174.866,-1.63],[174.9605,-1.711],[175.0635,-1.733],[175.168,-1.708],[175.23,-1.66],[175.2765,-1.586],[175.288,-1.522],[175.2795,-1.424],[175.255,-1.371],[175.171,-1.287],[175.0485,-1.228],[174.986,-1.174],[174.9505,-1.109],[174.888,-1.027],[174.802,-0.95],[174.709,-0.911],[174.649,-0.906],[174.676,-0.836],[174.6815,-0.78],[174.659,-0.646],[174.6305,-0.558],[174.592,-0.508],[174.478,-0.409],[174.3805,-0.365],[174.279,-0.336],[174.188,-0.334],[174.084,-0.376],[174.025,-0.433],[173.9995,-0.477],[173.9845,-0.553]]],[[[175.309,-1.811],[175.3255,-1.933],[175.347,-1.978],[175.427,-2.087],[175.526,-2.139],[175.634,-2.14],[175.7205,-2.102],[175.8015,-1.994],[175.819,-1.928],[175.817,-1.85],[175.776,-1.752],[175.6965,-1.66],[175.6015,-1.597],[175.5065,-1.584],[175.43,-1.608],[175.384,-1.643],[175.3205,-1.743],[175.309,-1.811]]],[[[175.7445,-1.29],[175.7705,-1.391],[175.851,-1.496],[175.9105,-1.541],[175.9875,-1.572],[176.062,-1.571],[176.163,-1.519],[176.196,-1.481],[176.226,-1.4085],[176.344,-1.544],[176.4365,-1.597],[176.4955,-1.604],[176.576,-1.586],[176.644,-1.535],[176.6745,-1.489],[176.693,-1.425],[176.69,-1.359],[176.653,-1.272],[176.5975,-1.19],[176.5045,-1.126],[176.4185,-1.112],[176.351,-1.12],[176.298,-1.143],[176.237,-1.202],[176.2065,-1.275],[176.125,-1.154],[176.0635,-1.101],[176.019,-1.08],[175.9515,-1.07],[175.9015,-1.077],[175.8395,-1.106],[175.793,-1.151],[175.7485,-1.246],[175.7445,-1.29]]],[[[175.765,-2.486],[175.774,-2.55],[175.796,-2.597],[175.8595,-2.666],[175.9215,-2.702],[175.972,-2.714],[176.0755,-2.7],[176.133,-2.664],[176.192,-2.57],[176.199,-2.488],[176.1745,-2.412],[176.1045,-2.332],[176.055,-2.302],[175.9745,-2.284],[175.915,-2.291],[175.8555,-2.318],[175.787,-2.395],[175.765,-2.486]]],[[[176.59,-2.612],[176.601,-2.68],[176.6245,-2.726],[176.721,-2.828],[176.825,-2.87],[176.9285,-2.854],[176.9845,-2.817],[177.039,-2.73],[177.0465,-2.646],[177.007,-2.54],[176.9545,-2.476],[176.8825,-2.431],[176.813,-2.414],[176.7275,-2.423],[176.659,-2.462],[176.6155,-2.515],[176.59,-2.612]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d3/Flag_of_Kiribati.svg","name:en":"Kiribati","wikidata":"Q710","ISO3166-1:alpha2":"KI","ISO3166-1:alpha3":"KIR","ISO3166-1:numeric":"296"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-166.0855,-10.888],[-166.0585,-10.993],[-166.0105,-11.048],[-165.942,-11.093],[-165.857,-11.115],[-165.757,-11.1],[-165.6845,-11.054],[-165.6445,-10.999],[-165.626,-10.941],[-165.6295,-10.812],[-165.653,-10.749],[-165.7115,-10.6855],[-165.801,-10.649],[-165.868,-10.6485],[-165.951,-10.6765],[-166.002,-10.716],[-166.052,-10.7845],[-166.0855,-10.888]]],[[[-165.628,-11.559],[-165.594,-11.6685],[-165.5405,-11.7245],[-165.4375,-11.7655],[-165.3575,-11.7595],[-165.2765,-11.7185],[-165.2135,-11.632],[-165.202,-11.542],[-165.242,-11.442],[-165.3015,-11.388],[-165.395,-11.357],[-165.464,-11.3615],[-165.5175,-11.3815],[-165.5745,-11.425],[-165.614,-11.4865],[-165.628,-11.559]]],[[[-163.408,-13.2745],[-163.3965,-13.3395],[-163.3295,-13.4315],[-163.2065,-13.5115],[-163.1385,-13.531],[-163.0665,-13.5275],[-163.0015,-13.501],[-162.933,-13.456],[-162.861,-13.377],[-162.838,-13.2995],[-162.844,-13.228],[-162.8755,-13.163],[-163.005,-13.0435],[-163.0995,-13.007],[-163.168,-13.0095],[-163.298,-13.072],[-163.3475,-13.117],[-163.386,-13.1815],[-163.408,-13.2745]]],[[[-163.4015,-18.056],[-163.376,-18.1545],[-163.307,-18.243],[-163.2445,-18.277],[-163.19,-18.29],[-163.118,-18.2885],[-163.05,-18.2635],[-162.9695,-18.194],[-162.9365,-18.1465],[-162.911,-18.077],[-162.917,-17.9745],[-162.951,-17.912],[-163.017,-17.8445],[-163.0915,-17.8015],[-163.1665,-17.7905],[-163.2315,-17.801],[-163.29,-17.8315],[-163.334,-17.8755],[-163.3895,-17.9895],[-163.4015,-18.056]]],[[[-161.311,-10.032],[-161.2955,-10.11],[-161.256,-10.17],[-161.1705,-10.245],[-161.2265,-10.3395],[-161.2445,-10.4515],[-161.2285,-10.517],[-161.195,-10.5705],[-161.1455,-10.6115],[-161.065,-10.652],[-161.006,-10.662],[-160.8975,-10.643],[-160.836,-10.611],[-160.7895,-10.567],[-160.7535,-10.494],[-160.7565,-10.3695],[-160.794,-10.301],[-160.867,-10.223],[-160.946,-10.18],[-160.8965,-10.114],[-160.874,-10.0465],[-160.8715,-9.9825],[-160.9015,-9.899],[-160.927,-9.8665],[-161.0255,-9.807],[-161.112,-9.7965],[-161.2045,-9.826],[-161.2585,-9.872],[-161.29,-9.9245],[-161.311,-10.032]]],[[[-160.045,-18.9185],[-160.0205,-19.0075],[-159.9645,-19.0725],[-159.9095,-19.102],[-159.771,-19.1475],[-159.6965,-19.1475],[-159.6315,-19.125],[-159.567,-19.0705],[-159.536,-19.015],[-159.5215,-18.8925],[-159.534,-18.8355],[-159.579,-18.739],[-159.612,-18.6935],[-159.674,-18.647],[-159.735,-18.627],[-159.8035,-18.6265],[-159.871,-18.648],[-159.953,-18.7175],[-160.0225,-18.823],[-160.045,-18.9185]]],[[[-160.038,-21.2145],[-160.0135,-21.329],[-159.9345,-21.4215],[-159.794,-21.4695],[-159.711,-21.4685],[-159.627,-21.437],[-159.577,-21.398],[-159.5425,-21.3505],[-159.5175,-21.2645],[-159.523,-21.197],[-159.57,-21.095],[-159.6385,-21.0375],[-159.6825,-21.016],[-159.7875,-20.9995],[-159.8985,-21.018],[-159.9725,-21.0695],[-160.022,-21.1415],[-160.038,-21.2145]]],[[[-159.1775,-19.2845],[-159.157,-19.3575],[-159.1015,-19.4285],[-159.0485,-19.465],[-158.976,-19.4865],[-158.902,-19.4835],[-158.8205,-19.4565],[-158.7625,-19.4135],[-158.72,-19.3455],[-158.707,-19.2615],[-158.726,-19.1825],[-158.75,-19.143],[-158.8095,-19.087],[-158.89,-19.057],[-158.9585,-19.0555],[-159.0265,-19.072],[-159.0895,-19.1025],[-159.1395,-19.1505],[-159.17,-19.212],[-159.1775,-19.2845]]],[[[-158.5045,-19.8125],[-158.48,-19.9065],[-158.4105,-19.9795],[-158.3485,-20.007],[-158.3185,-20.1045],[-158.255,-20.18],[-158.198,-20.2135],[-158.1305,-20.2285],[-158.0645,-20.2225],[-157.975,-20.1805],[-157.897,-20.096],[-157.876,-20.019],[-157.835,-20.0575],[-157.769,-20.089],[-157.6955,-20.0965],[-157.576,-20.0565],[-157.4965,-19.9705],[-157.474,-19.89],[-157.492,-19.771],[-157.534,-19.7095],[-157.583,-19.673],[-157.6875,-19.641],[-157.7695,-19.648],[-157.838,-19.68],[-157.894,-19.737],[-157.922,-19.804],[-157.9295,-19.862],[-158.004,-19.7975],[-158.079,-19.7705],[-158.0975,-19.7185],[-158.1385,-19.6655],[-158.1895,-19.6305],[-158.257,-19.6095],[-158.334,-19.6125],[-158.409,-19.643],[-158.46,-19.688],[-158.493,-19.7465],[-158.5045,-19.8125]]],[[[-158.263,-8.975],[-158.251,-9.0465],[-158.1995,-9.1385],[-158.0935,-9.243],[-158.0255,-9.276],[-157.9025,-9.2825],[-157.8395,-9.266],[-157.775,-9.222],[-157.7135,-9.1485],[-157.6795,-9.0725],[-157.6765,-9.001],[-157.7,-8.932],[-157.7545,-8.8315],[-157.808,-8.777],[-157.874,-8.744],[-157.9535,-8.7215],[-158.079,-8.722],[-158.1485,-8.7455],[-158.2295,-8.82],[-158.2565,-8.888],[-158.263,-8.975]]],[[[-158.1675,-21.937],[-158.1505,-22.016],[-158.108,-22.081],[-158.0335,-22.1335],[-157.9645,-22.1555],[-157.8555,-22.154],[-157.8005,-22.14],[-157.7415,-22.1045],[-157.6955,-22.051],[-157.6675,-21.96],[-157.673,-21.8925],[-157.6985,-21.8295],[-157.7915,-21.7255],[-157.8555,-21.6975],[-157.974,-21.698],[-158.055,-21.729],[-158.134,-21.813],[-158.1675,-21.937]]],[[[-157.5735,-20.15],[-157.566,-20.219],[-157.5025,-20.3235],[-157.434,-20.368],[-157.351,-20.3885],[-157.242,-20.367],[-157.176,-20.322],[-157.125,-20.2475],[-157.109,-20.154],[-157.1225,-20.0845],[-157.1785,-20.0005],[-157.2275,-19.967],[-157.3295,-19.934],[-157.4285,-19.9455],[-157.5025,-19.9895],[-157.541,-20.036],[-157.5735,-20.15]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/35/Flag_of_the_Cook_Islands.svg","name:en":"Cook Islands","wikidata":"Q26988","ISO3166-1:alpha2":"CK","ISO3166-1:alpha3":"COK","ISO3166-1:numeric":"184"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-57.649,-30.194],[-57.584,-30.177],[-57.4805,-30.123],[-57.414,-30.037],[-57.3625,-30.013],[-57.328,-29.972],[-57.326,-29.876],[-57.242,-29.788],[-57.121,-29.765],[-57.033,-29.696],[-56.9665,-29.6295],[-56.97,-29.604],[-56.899,-29.532],[-56.819,-29.488],[-56.7805,-29.439],[-56.766,-29.3775],[-56.7015,-29.359],[-56.648,-29.2585],[-56.644,-29.196],[-56.5915,-29.124],[-56.5005,-29.0925],[-56.4255,-29.082],[-56.3985,-29.021],[-56.3905,-28.955],[-56.302,-28.901],[-56.301,-28.8155],[-56.259,-28.7785],[-56.192,-28.7735],[-56.1185,-28.6815],[-56.002,-28.596],[-56.022,-28.52],[-55.986,-28.4975],[-55.881,-28.4695],[-55.9025,-28.412],[-55.8515,-28.355],[-55.739,-28.3765],[-55.6915,-28.4175],[-55.67,-28.331],[-55.776,-28.271],[-55.769,-28.2405],[-55.7,-28.222],[-55.635,-28.1785],[-55.6115,-28.1185],[-55.552,-28.1605],[-55.536,-28.119],[-55.4835,-28.0775],[-55.4475,-28.099],[-55.3815,-28.0395],[-55.385,-27.985],[-55.3365,-27.9615],[-55.31,-27.9215],[-55.269,-27.931],[-55.197,-27.857],[-55.1255,-27.859],[-55.0325,-27.825],[-55.043,-27.775],[-54.9345,-27.769],[-54.903,-27.721],[-54.907,-27.6465],[-54.858,-27.629],[-54.814,-27.533],[-54.786,-27.576],[-54.742,-27.561],[-54.689,-27.574],[-54.614,-27.5315],[-54.5715,-27.4545],[-54.52,-27.5025],[-54.4495,-27.476],[-54.4645,-27.424],[-54.3965,-27.4125],[-54.3535,-27.4665],[-54.3085,-27.423],[-54.2175,-27.386],[-54.192,-27.3075],[-54.1585,-27.2935],[-54.08,-27.2985],[-54.021,-27.2485],[-54.0055,-27.197],[-53.959,-27.158],[-53.9,-27.173],[-53.8455,-27.161],[-53.806,-27.1125],[-53.7965,-27.0395],[-53.7665,-27.044],[-53.718,-26.9845],[-53.726,-26.9585],[-53.682,-26.9165],[-53.7145,-26.7535],[-53.749,-26.7415],[-53.717,-26.6815],[-53.742,-26.648],[-53.7225,-26.5815],[-53.731,-26.5095],[-53.698,-26.4865],[-53.689,-26.4435],[-53.7075,-26.3955],[-53.6855,-26.335],[-53.646,-26.2865],[-53.6435,-26.2165],[-53.711,-26.1305],[-53.742,-26.1155],[-53.7355,-26.042],[-53.772,-26.0315],[-53.8425,-25.9325],[-53.825,-25.871],[-53.849,-25.835],[-53.8225,-25.793],[-53.8645,-25.7455],[-53.862,-25.659],[-54.0285,-25.566],[-54.0835,-25.561],[-54.1045,-25.4945],[-54.166,-25.543],[-54.1905,-25.5345],[-54.255,-25.596],[-54.28,-25.556],[-54.3745,-25.594],[-54.4255,-25.663],[-54.5545,-25.5875],[-54.5935,-25.593],[-54.581,-25.658],[-54.6495,-25.6635],[-54.658,-25.6875],[-54.6225,-25.7835],[-54.5915,-25.818],[-54.6225,-25.9145],[-54.6085,-25.955],[-54.655,-25.979],[-54.677,-26.017],[-54.647,-26.075],[-54.6695,-26.164],[-54.6185,-26.209],[-54.6795,-26.2645],[-54.649,-26.312],[-54.698,-26.382],[-54.7155,-26.4595],[-54.7815,-26.51],[-54.807,-26.554],[-54.7845,-26.605],[-54.8125,-26.668],[-54.86,-26.656],[-54.9165,-26.6685],[-54.9485,-26.702],[-54.9635,-26.7875],[-55.0525,-26.7975],[-55.1435,-26.8685],[-55.135,-26.9485],[-55.195,-26.9685],[-55.238,-26.9425],[-55.306,-26.9625],[-55.3705,-26.9645],[-55.423,-26.9955],[-55.481,-27.1115],[-55.549,-27.098],[-55.5745,-27.169],[-55.6155,-27.21],[-55.581,-27.235],[-55.6005,-27.279],[-55.599,-27.3375],[-55.675,-27.373],[-55.737,-27.445],[-55.787,-27.4405],[-55.8395,-27.407],[-55.8965,-27.3425],[-55.9845,-27.353],[-56.037,-27.3125],[-56.0875,-27.3085],[-56.176,-27.3435],[-56.243,-27.4045],[-56.2945,-27.42],[-56.2935,-27.494],[-56.339,-27.5245],[-56.3565,-27.567],[-56.401,-27.599],[-56.463,-27.5695],[-56.542,-27.4385],[-56.6065,-27.4315],[-56.6495,-27.4605],[-56.721,-27.468],[-56.6765,-27.5505],[-56.689,-27.579],[-56.747,-27.605],[-56.796,-27.587],[-56.8495,-27.6065],[-56.942,-27.5585],[-56.9655,-27.502],[-57.0425,-27.481],[-57.1425,-27.4835],[-57.232,-27.4665],[-57.3115,-27.418],[-57.4125,-27.425],[-57.4875,-27.444],[-57.667,-27.356],[-57.702,-27.3245],[-57.8145,-27.334],[-57.8775,-27.2785],[-57.9145,-27.2655],[-58.0595,-27.2625],[-58.198,-27.2735],[-58.26,-27.257],[-58.3035,-27.274],[-58.4135,-27.2865],[-58.489,-27.2735],[-58.599,-27.3],[-58.5925,-27.23],[-58.6565,-27.192],[-58.6625,-27.1625],[-58.6315,-27.115],[-58.555,-27.109],[-58.555,-27.058],[-58.5195,-27.004],[-58.4685,-26.9865],[-58.4935,-26.94],[-58.325,-26.8695],[-58.337,-26.817],[-58.282,-26.7915],[-58.246,-26.7425],[-58.2635,-26.6515],[-58.197,-26.6535],[-58.193,-26.5945],[-58.2225,-26.5425],[-58.2085,-26.505],[-58.2235,-26.4635],[-58.2105,-26.3775],[-58.1655,-26.3575],[-58.158,-26.2605],[-58.1065,-26.2375],[-58.1195,-26.1525],[-58.0845,-26.112],[-58.0415,-26.1125],[-57.9495,-26.0675],[-57.926,-26.037],[-57.8555,-26.009],[-57.8935,-25.964],[-57.842,-25.921],[-57.8585,-25.858],[-57.7965,-25.825],[-57.8275,-25.759],[-57.774,-25.758],[-57.733,-25.725],[-57.7675,-25.6995],[-57.7405,-25.6505],[-57.6705,-25.6505],[-57.677,-25.6055],[-57.6085,-25.6065],[-57.5795,-25.569],[-57.555,-25.4485],[-57.601,-25.398],[-57.6365,-25.3855],[-57.6985,-25.3165],[-57.695,-25.2885],[-57.752,-25.2275],[-57.7765,-25.1555],[-57.8215,-25.142],[-57.8825,-25.076],[-57.9885,-25.0815],[-58.0105,-25.038],[-58.077,-25.038],[-58.1345,-25.01],[-58.153,-24.973],[-58.194,-24.968],[-58.236,-24.927],[-58.341,-24.9965],[-58.393,-24.9515],[-58.4405,-24.8795],[-58.573,-24.821],[-58.6725,-24.832],[-58.726,-24.7745],[-58.804,-24.7735],[-58.8785,-24.7335],[-58.9705,-24.663],[-59.0235,-24.66],[-59.0565,-24.6315],[-59.13,-24.617],[-59.186,-24.5635],[-59.239,-24.5465],[-59.354,-24.478],[-59.373,-24.423],[-59.4215,-24.409],[-59.4745,-24.3315],[-59.7555,-24.1705],[-60.039,-24.0115],[-60.197,-24.045],[-60.309,-24.038],[-60.436,-23.9665],[-60.4875,-23.951],[-60.572,-23.96],[-60.595,-23.9175],[-60.7095,-23.892],[-60.732,-23.872],[-60.8595,-23.8695],[-60.9215,-23.823],[-60.99,-23.818],[-61.0125,-23.7615],[-61.0805,-23.691],[-61.096,-23.613],[-61.1735,-23.5605],[-61.2195,-23.5575],[-61.3255,-23.4665],[-61.417,-23.4485],[-61.445,-23.42],[-61.501,-23.4105],[-61.5235,-23.3675],[-61.627,-23.2775],[-61.736,-23.2345],[-61.753,-23.173],[-61.7905,-23.1645],[-61.85,-23.1045],[-61.9975,-23.0075],[-61.997,-22.9485],[-62.036,-22.8785],[-62.099,-22.806],[-62.158,-22.76],[-62.1985,-22.706],[-62.2025,-22.6705],[-62.2545,-22.6115],[-62.228,-22.5445],[-62.2815,-22.5245],[-62.2875,-22.4775],[-62.336,-22.4815],[-62.4505,-22.4195],[-62.4655,-22.383],[-62.51,-22.3805],[-62.571,-22.327],[-62.63,-22.303],[-62.6405,-22.236],[-62.717,-22.2035],[-62.813,-22.0965],[-62.799,-22.06],[-62.811,-21.999],[-63.3505,-21.9995],[-63.9315,-21.9995],[-63.986,-22.0935],[-64.009,-22.1655],[-64.0625,-22.2385],[-64.1165,-22.362],[-64.1155,-22.4045],[-64.1995,-22.491],[-64.2425,-22.56],[-64.272,-22.71],[-64.2655,-22.7665],[-64.2825,-22.8245],[-64.329,-22.8565],[-64.3445,-22.739],[-64.393,-22.7195],[-64.397,-22.6835],[-64.4335,-22.6485],[-64.4125,-22.5375],[-64.4455,-22.523],[-64.4905,-22.4385],[-64.5655,-22.372],[-64.542,-22.279],[-64.651,-22.1895],[-64.7375,-22.1935],[-64.8625,-22.123],[-64.8855,-22.129],[-64.9935,-22.0825],[-65.475,-22.0895],[-65.5745,-22.076],[-65.5825,-22.094],[-65.6755,-22.1115],[-65.7485,-22.109],[-65.805,-22.075],[-65.925,-21.9335],[-66.048,-21.919],[-66.0435,-21.872],[-66.0765,-21.835],[-66.241,-21.789],[-66.236,-21.8495],[-66.2605,-21.906],[-66.2925,-22.0345],[-66.288,-22.086],[-66.348,-22.128],[-66.686,-22.2095],[-66.7395,-22.2385],[-66.7825,-22.438],[-66.9285,-22.492],[-66.973,-22.5405],[-67.027,-22.541],[-67.014,-22.655],[-67.1275,-22.722],[-67.1805,-22.814],[-66.9905,-23.0005],[-67.112,-23.3805],[-67.3225,-24.0345],[-67.961,-24.285],[-68.2455,-24.396],[-68.2965,-24.455],[-68.309,-24.492],[-68.387,-24.484],[-68.431,-24.5775],[-68.475,-24.6215],[-68.5155,-24.703],[-68.5445,-24.7245],[-68.568,-24.7975],[-68.531,-24.8585],[-68.4865,-24.8955],[-68.432,-24.9055],[-68.407,-25.0285],[-68.3795,-25.0415],[-68.3435,-25.1105],[-68.4095,-25.1455],[-68.4595,-25.126],[-68.511,-25.181],[-68.5435,-25.3025],[-68.5225,-25.3745],[-68.5865,-25.4275],[-68.573,-25.4935],[-68.525,-25.567],[-68.5415,-25.6385],[-68.479,-25.708],[-68.381,-26.178],[-68.5575,-26.287],[-68.5875,-26.493],[-68.3655,-26.801],[-68.281,-26.9035],[-68.315,-26.974],[-68.2985,-27.039],[-68.3475,-27.0335],[-68.4325,-27.0655],[-68.4965,-27.135],[-68.5565,-27.1145],[-68.573,-27.175],[-68.6185,-27.17],[-68.6685,-27.104],[-68.778,-27.099],[-68.8085,-27.1465],[-68.841,-27.1545],[-68.8685,-27.2605],[-68.8245,-27.294],[-68.8685,-27.317],[-68.9365,-27.4445],[-68.9635,-27.4685],[-68.943,-27.515],[-68.9765,-27.5605],[-69.053,-27.617],[-69.0455,-27.699],[-69.0935,-27.7175],[-69.089,-27.7855],[-69.134,-27.909],[-69.182,-27.972],[-69.2105,-27.9645],[-69.2675,-28.0105],[-69.264,-28.0465],[-69.299,-28.0695],[-69.3275,-28.1555],[-69.3795,-28.1815],[-69.384,-28.213],[-69.457,-28.1835],[-69.5325,-28.3345],[-69.65,-28.396],[-69.642,-28.442],[-69.6645,-28.482],[-69.6645,-28.5815],[-69.6955,-28.599],[-69.7265,-28.6705],[-69.7295,-28.736],[-69.693,-28.771],[-69.7665,-28.924],[-69.7535,-28.957],[-69.787,-29.0285],[-69.787,-29.13],[-69.883,-29.144],[-69.9335,-29.189],[-69.946,-29.263],[-69.9855,-29.277],[-70.0205,-29.3305],[-70.0215,-29.3835],[-69.967,-29.4145],[-69.9815,-29.452],[-69.948,-29.491],[-69.9555,-29.535],[-69.9315,-29.5845],[-69.942,-29.646],[-69.8795,-29.7335],[-69.9135,-29.807],[-69.8775,-29.8615],[-69.896,-29.8795],[-69.8985,-29.958],[-69.926,-30.0005],[-69.912,-30.028],[-69.961,-30.0685],[-69.905,-30.1065],[-69.8095,-30.1455],[-69.8355,-30.219],[-69.8855,-30.2185],[-69.9035,-30.3435],[-69.955,-30.3855],[-70.0705,-30.391],[-70.1035,-30.3565],[-70.144,-30.372],[-70.117,-30.431],[-70.195,-30.4915],[-70.1795,-30.5115],[-70.247,-30.635],[-70.278,-30.81],[-70.3215,-30.9245],[-70.2985,-30.9625],[-70.319,-31.043],[-70.408,-31.1665],[-70.432,-31.102],[-70.4985,-31.125],[-70.5235,-31.19],[-70.517,-31.234],[-70.545,-31.3065],[-70.53,-31.3755],[-70.5695,-31.4735],[-70.553,-31.51],[-70.567,-31.583],[-70.513,-31.6895],[-70.4675,-31.7005],[-70.455,-31.8485],[-70.392,-31.8795],[-70.272,-31.8905],[-70.2085,-31.978],[-70.2485,-32.024],[-70.322,-32.0505],[-70.3415,-32.1095],[-70.305,-32.1425],[-70.301,-32.207],[-70.319,-32.2735],[-70.2225,-32.325],[-70.2365,-32.429],[-70.149,-32.466],[-70.123,-32.5355],[-70.1705,-32.6255],[-70.108,-32.8005],[-70.0545,-32.832],[-70.0445,-32.87],[-70.001,-32.8845],[-70.0015,-32.946],[-70.0215,-33.008],[-70.081,-33.029],[-70.066,-33.104],[-70.0645,-33.205],[-70.0275,-33.2325],[-70.039,-33.27],[-70.003,-33.323],[-69.9175,-33.267],[-69.7995,-33.2885],[-69.7705,-33.361],[-69.82,-33.4325],[-69.835,-33.515],[-69.872,-33.5395],[-69.8655,-33.583],[-69.8845,-33.6845],[-69.865,-33.715],[-69.904,-33.7715],[-69.899,-33.8475],[-69.8555,-33.8925],[-69.892,-33.925],[-69.8395,-34.007],[-69.869,-34.1405],[-69.7915,-34.2005],[-69.795,-34.243],[-69.9315,-34.2815],[-69.967,-34.251],[-70.031,-34.288],[-70.0145,-34.3385],[-70.015,-34.413],[-70.1035,-34.482],[-70.223,-34.64],[-70.2145,-34.681],[-70.309,-34.768],[-70.257,-34.8205],[-70.3195,-34.9315],[-70.3345,-34.9915],[-70.3755,-35.046],[-70.362,-35.1365],[-70.388,-35.171],[-70.4705,-35.203],[-70.5425,-35.2025],[-70.583,-35.28],[-70.4965,-35.323],[-70.439,-35.316],[-70.4185,-35.3545],[-70.4525,-35.3925],[-70.4465,-35.464],[-70.382,-35.5125],[-70.4135,-35.529],[-70.3865,-35.604],[-70.418,-35.63],[-70.3695,-35.7375],[-70.3795,-35.787],[-70.3155,-35.819],[-70.421,-35.874],[-70.425,-35.9135],[-70.3905,-35.951],[-70.3715,-36.0435],[-70.4105,-36.056],[-70.421,-36.156],[-70.4905,-36.179],[-70.5345,-36.1415],[-70.5835,-36.147],[-70.6395,-36.243],[-70.705,-36.2725],[-70.6845,-36.3045],[-70.7125,-36.3395],[-70.6795,-36.3895],[-70.713,-36.427],[-70.7875,-36.429],[-70.89,-36.4035],[-70.894,-36.4715],[-70.956,-36.5045],[-71.0385,-36.4775],[-71.0555,-36.5665],[-71.0405,-36.6495],[-71.013,-36.6975],[-71.1115,-36.7285],[-71.1325,-36.8155],[-71.122,-36.864],[-71.1445,-36.929],[-71.1065,-36.9715],[-71.1705,-36.9965],[-71.0925,-37.1145],[-71.136,-37.1645],[-71.114,-37.205],[-71.2155,-37.2725],[-71.199,-37.3795],[-71.15,-37.4025],[-71.1195,-37.4865],[-71.1265,-37.5825],[-71.194,-37.6305],[-71.212,-37.689],[-71.156,-37.7575],[-71.1705,-37.78],[-71.108,-37.8925],[-71.1145,-37.953],[-71.051,-37.9925],[-71.053,-38.0455],[-70.9865,-38.1045],[-71.0435,-38.124],[-70.997,-38.17],[-71.0195,-38.218],[-70.985,-38.361],[-70.976,-38.442],[-70.911,-38.5035],[-70.8405,-38.5415],[-70.829,-38.5995],[-70.904,-38.667],[-70.892,-38.721],[-70.925,-38.763],[-71.0275,-38.7585],[-71.1075,-38.772],[-71.2065,-38.8175],[-71.2605,-38.815],[-71.2675,-38.8515],[-71.4255,-38.9215],[-71.441,-38.9775],[-71.4075,-39.096],[-71.3885,-39.2655],[-71.4015,-39.352],[-71.4465,-39.3965],[-71.473,-39.4935],[-71.5345,-39.5305],[-71.461,-39.583],[-71.518,-39.6255],[-71.61,-39.628],[-71.618,-39.5985],[-71.6985,-39.588],[-71.684,-39.692],[-71.7125,-39.7245],[-71.6805,-39.769],[-71.691,-39.8425],[-71.6465,-39.853],[-71.5925,-39.901],[-71.6895,-40.0365],[-71.6665,-40.0745],[-71.7,-40.117],[-71.814,-40.0805],[-71.7995,-40.136],[-71.828,-40.208],[-71.736,-40.3055],[-71.6715,-40.311],[-71.7085,-40.4225],[-71.8035,-40.406],[-71.8415,-40.452],[-71.87,-40.5725],[-71.8375,-40.61],[-71.866,-40.658],[-71.9495,-40.719],[-71.9595,-40.7585],[-71.923,-40.783],[-71.9275,-40.8335],[-71.8685,-40.892],[-71.855,-40.9445],[-71.9085,-40.973],[-71.8195,-41.0605],[-71.8515,-41.0985],[-71.841,-41.15],[-71.888,-41.161],[-71.8575,-41.212],[-71.8755,-41.2965],[-71.911,-41.358],[-71.8345,-41.4335],[-71.851,-41.515],[-71.837,-41.566],[-71.886,-41.605],[-71.784,-41.735],[-71.7645,-41.817],[-71.7895,-41.831],[-71.775,-41.9075],[-71.7935,-41.9625],[-71.73,-42.122],[-71.927,-42.19],[-71.9705,-42.145],[-72.049,-42.116],[-72.0925,-42.154],[-72.173,-42.1405],[-72.1585,-42.1935],[-72.168,-42.2605],[-72.137,-42.328],[-72.133,-42.384],[-72.023,-42.4175],[-72.04,-42.4595],[-72.028,-42.515],[-72.1035,-42.6005],[-72.096,-42.642],[-72.1795,-42.698],[-72.129,-42.7325],[-72.165,-42.799],[-72.143,-42.898],[-72.076,-42.949],[-72.0365,-43.0135],[-71.9445,-43.046],[-71.924,-43.101],[-71.8875,-43.1265],[-71.7605,-43.1645],[-71.73,-43.201],[-71.757,-43.228],[-71.7315,-43.3075],[-71.7915,-43.298],[-71.8425,-43.335],[-71.8965,-43.32],[-71.889,-43.45],[-71.8485,-43.4835],[-71.871,-43.553],[-71.7765,-43.5385],[-71.716,-43.5775],[-71.69,-43.6265],[-71.608,-43.6285],[-71.581,-43.6515],[-71.6105,-43.701],[-71.703,-43.7395],[-71.7515,-43.7855],[-71.759,-43.8445],[-71.6655,-43.902],[-71.649,-43.9455],[-71.682,-43.9745],[-71.752,-44.094],[-71.8515,-44.1295],[-71.8075,-44.1875],[-71.811,-44.254],[-71.791,-44.3145],[-71.8405,-44.35],[-71.8075,-44.4205],[-71.695,-44.384],[-71.6545,-44.4095],[-71.5725,-44.409],[-71.4755,-44.391],[-71.3645,-44.3895],[-71.332,-44.429],[-71.24,-44.4175],[-71.137,-44.4695],[-71.092,-44.533],[-71.1215,-44.597],[-71.1865,-44.593],[-71.2355,-44.641],[-71.198,-44.689],[-71.207,-44.75],[-71.2765,-44.8095],[-71.396,-44.79],[-71.494,-44.737],[-71.6755,-44.7865],[-71.7835,-44.751],[-71.8565,-44.802],[-71.9135,-44.777],[-72.0065,-44.7835],[-72.069,-44.851],[-72.05,-44.882],[-71.979,-44.9035],[-71.933,-44.9425],[-71.812,-44.921],[-71.6845,-44.9695],[-71.5605,-44.9785],[-71.5565,-45.0325],[-71.511,-45.061],[-71.4705,-45.1485],[-71.4185,-45.1755],[-71.331,-45.2505],[-71.333,-45.319],[-71.3845,-45.3495],[-71.532,-45.397],[-71.477,-45.497],[-71.5605,-45.5235],[-71.6815,-45.514],[-71.746,-45.5415],[-71.727,-45.577],[-71.7955,-45.6645],[-71.798,-45.711],[-71.741,-45.807],[-71.769,-45.8465],[-71.656,-45.886],[-71.6145,-45.9625],[-71.6465,-45.983],[-71.676,-46.049],[-71.762,-46.112],[-71.889,-46.1275],[-71.8645,-46.1895],[-71.7995,-46.1915],[-71.751,-46.229],[-71.724,-46.2845],[-71.757,-46.346],[-71.669,-46.528],[-71.6685,-46.613],[-71.6485,-46.689],[-71.839,-46.799],[-71.9485,-46.81],[-71.931,-46.8685],[-71.9705,-46.904],[-71.9525,-46.959],[-71.88,-47.011],[-71.9495,-47.087],[-71.881,-47.107],[-71.8505,-47.162],[-71.8905,-47.1865],[-71.879,-47.2295],[-71.9735,-47.2095],[-72.0155,-47.25],[-71.997,-47.295],[-72.046,-47.3405],[-72.127,-47.328],[-72.1985,-47.416],[-72.266,-47.4155],[-72.31,-47.479],[-72.291,-47.512],[-72.3455,-47.6325],[-72.4205,-47.661],[-72.4345,-47.7345],[-72.504,-47.7635],[-72.467,-47.8135],[-72.475,-47.865],[-72.523,-47.902],[-72.503,-47.9585],[-72.427,-47.966],[-72.414,-48.079],[-72.361,-48.0745],[-72.2985,-48.16],[-72.3155,-48.2355],[-72.2345,-48.316],[-72.277,-48.3635],[-72.4055,-48.417],[-72.3735,-48.4495],[-72.44,-48.514],[-72.5025,-48.5175],[-72.586,-48.4875],[-72.542,-48.631],[-72.534,-48.8005],[-72.6965,-48.8965],[-72.747,-48.908],[-72.7945,-48.9645],[-72.85,-48.965],[-72.923,-48.9345],[-73.001,-49.0595],[-73.042,-49.0775],[-73.0785,-49.1405],[-73.1395,-49.178],[-73.149,-49.284],[-73.473,-49.2045],[-73.4965,-49.2685],[-73.432,-49.314],[-73.4275,-49.3935],[-73.489,-49.4345],[-73.4635,-49.4825],[-73.5445,-49.5045],[-73.5355,-49.564],[-73.4605,-49.6245],[-73.506,-49.666],[-73.504,-49.7095],[-73.4385,-49.758],[-73.4905,-49.8165],[-73.5105,-49.8795],[-73.5585,-49.947],[-73.477,-49.97],[-73.436,-50.011],[-73.522,-50.1535],[-73.4615,-50.185],[-73.4525,-50.2415],[-73.3615,-50.3015],[-73.3975,-50.3705],[-73.3575,-50.429],[-73.3645,-50.518],[-73.2875,-50.59],[-73.1935,-50.6155],[-73.153,-50.658],[-73.1965,-50.7415],[-73.094,-50.773],[-72.957,-50.741],[-72.893,-50.67],[-72.7325,-50.6155],[-72.697,-50.645],[-72.5835,-50.6585],[-72.4955,-50.602],[-72.4105,-50.6375],[-72.286,-50.66],[-72.349,-50.7545],[-72.2605,-50.836],[-72.246,-50.9],[-72.267,-50.965],[-72.2655,-51.035],[-72.374,-51.023],[-72.4015,-51.1355],[-72.347,-51.2025],[-72.2585,-51.244],[-72.274,-51.286],[-72.3575,-51.318],[-72.308,-51.3775],[-72.337,-51.4215],[-72.3245,-51.476],[-72.391,-51.515],[-72.441,-51.584],[-72.3385,-51.584],[-72.2875,-51.6325],[-72.299,-51.6975],[-72.2,-51.713],[-72.121,-51.7445],[-71.949,-51.8735],[-71.9385,-51.9085],[-72.0265,-51.9555],[-71.9225,-52.0],[-71.0645,-52.0],[-70.667,-52.0],[-69.9955,-52.0005],[-69.4865,-52.1515],[-69.19,-52.1505],[-68.987,-52.2035],[-68.838,-52.278],[-68.7095,-52.2855],[-68.586,-52.3065],[-68.5735,-52.325],[-68.4185,-52.3325],[-68.4325,-52.3975],[-68.607,-52.659],[-68.6065,-52.9755],[-68.6065,-53.48],[-68.608,-54.1885],[-68.6075,-54.5555],[-68.5955,-54.8085],[-68.6095,-54.9135],[-68.4115,-54.8825],[-68.253,-54.8855],[-67.9505,-54.8735],[-67.705,-54.9055],[-67.5725,-54.8945],[-67.477,-54.9235],[-67.273,-54.905],[-67.1325,-54.9275],[-67.032,-54.9765],[-66.958,-54.9715],[-66.8525,-54.988],[-66.7915,-55.0135],[-66.671,-55.119],[-66.4165,-55.1215],[-66.0785,-55.1835],[-65.9115,-55.1715],[-65.646,-55.1775],[-65.438,-55.1385],[-65.254,-55.1265],[-65.0735,-55.065],[-65.0095,-55.0165],[-64.883,-55.0885],[-64.7515,-55.1145],[-64.606,-55.1155],[-64.347,-55.0955],[-64.0325,-55.061],[-63.783,-54.9845],[-63.736,-54.978],[-63.5895,-54.921],[-63.491,-54.8365],[-63.4535,-54.7275],[-63.474,-54.659],[-63.5425,-54.592],[-63.688,-54.533],[-63.857,-54.517],[-64.02,-54.4575],[-64.115,-54.4445],[-64.224,-54.45],[-64.439,-54.4995],[-64.502,-54.5265],[-64.6595,-54.5335],[-64.776,-54.572],[-64.903,-54.5],[-65.081,-54.441],[-65.21,-54.42],[-65.421,-54.4425],[-65.6575,-54.459],[-66.0845,-54.3545],[-66.261,-54.303],[-66.363,-54.2375],[-66.429,-54.157],[-66.5295,-54.104],[-66.708,-54.048],[-66.924,-53.9565],[-67.045,-53.922],[-67.071,-53.8925],[-67.2095,-53.828],[-67.218,-53.7875],[-67.2835,-53.7145],[-67.4685,-53.6255],[-67.6555,-53.5215],[-67.728,-53.44],[-67.7525,-53.349],[-67.811,-53.2595],[-67.884,-53.2005],[-67.881,-53.0845],[-67.992,-52.868],[-68.275,-52.614],[-68.308,-52.578],[-68.219,-52.549],[-68.071,-52.4345],[-68.0325,-52.37],[-68.037,-52.27],[-68.1395,-52.1545],[-68.2875,-52.024],[-68.4155,-51.896],[-68.5595,-51.692],[-68.6295,-51.6075],[-68.635,-51.5],[-68.7165,-51.3255],[-68.776,-51.165],[-68.8175,-51.0805],[-68.843,-50.967],[-68.8205,-50.925],[-68.8245,-50.751],[-68.77,-50.6245],[-68.7025,-50.526],[-68.622,-50.469],[-68.252,-50.3615],[-68.158,-50.316],[-68.034,-50.2935],[-67.886,-50.243],[-67.695,-50.1465],[-67.624,-50.0975],[-67.522,-49.987],[-67.4415,-49.85],[-67.4145,-49.758],[-67.412,-49.6695],[-67.3825,-49.5025],[-67.301,-49.3175],[-67.313,-49.1685],[-67.293,-49.126],[-67.1805,-49.0515],[-67.023,-48.994],[-66.919,-48.8995],[-66.8675,-48.812],[-66.6865,-48.7535],[-66.548,-48.631],[-66.409,-48.6115],[-66.161,-48.5165],[-66.0725,-48.4435],[-66.0435,-48.3755],[-65.9275,-48.3165],[-65.832,-48.3065],[-65.7305,-48.2685],[-65.6405,-48.186],[-65.6105,-48.119],[-65.5005,-48.041],[-65.4695,-47.966],[-65.481,-47.8545],[-65.551,-47.774],[-65.481,-47.6305],[-65.451,-47.549],[-65.4255,-47.3345],[-65.45,-47.2375],[-65.458,-47.141],[-65.514,-47.0725],[-65.565,-47.0425],[-65.4075,-46.04],[-65.3245,-45.4895],[-65.2465,-44.9635],[-65.2565,-44.8565],[-65.3105,-44.788],[-65.183,-44.7405],[-65.0295,-44.608],[-65.0035,-44.496],[-64.9565,-44.4375],[-64.939,-44.3805],[-64.961,-44.273],[-64.982,-44.239],[-64.9495,-44.1815],[-64.911,-44.0495],[-64.9255,-43.971],[-65.0255,-43.8395],[-65.059,-43.719],[-65.0085,-43.6885],[-64.923,-43.5925],[-64.807,-43.493],[-64.7715,-43.3955],[-64.5785,-43.313],[-64.3345,-43.244],[-64.147,-43.151],[-64.073,-43.0785],[-63.637,-43.0045],[-63.537,-42.9665],[-63.4475,-42.916],[-63.3785,-42.8465],[-63.354,-42.777],[-63.362,-42.7235],[-63.316,-42.6175],[-63.341,-42.422],[-63.331,-42.331],[-63.355,-42.24],[-63.466,-42.0795],[-63.4085,-42.0035],[-62.48,-41.317],[-62.12,-41.0215],[-62.0735,-40.967],[-61.995,-40.7845],[-61.7815,-40.65],[-61.7365,-40.5995],[-61.712,-40.5275],[-61.708,-40.3935],[-61.7105,-40.172],[-61.7195,-40.122],[-61.852,-39.8035],[-61.8375,-39.7165],[-61.8395,-39.6285],[-61.804,-39.5825],[-61.402,-39.1895],[-61.205,-39.202],[-61.0945,-39.2005],[-60.9785,-39.184],[-60.7865,-39.1765],[-60.6605,-39.1515],[-60.39,-39.121],[-60.223,-39.0885],[-60.049,-39.0625],[-59.732,-39.0245],[-59.5845,-38.9855],[-59.3455,-38.95],[-59.184,-38.915],[-59.0435,-38.896],[-58.936,-38.8725],[-58.6625,-38.784],[-58.4405,-38.7365],[-58.181,-38.652],[-57.954,-38.5655],[-57.838,-38.5115],[-57.7585,-38.485],[-57.522,-38.3595],[-57.4225,-38.277],[-57.3615,-38.239],[-57.312,-38.175],[-57.269,-38.032],[-57.2755,-37.929],[-57.213,-37.8605],[-57.073,-37.748],[-56.9355,-37.6235],[-56.878,-37.554],[-56.786,-37.4035],[-56.488,-37.029],[-56.4355,-36.9505],[-56.4225,-36.9055],[-56.4295,-36.699],[-56.4575,-36.3935],[-56.471,-36.336],[-55.7035,-35.7825],[-55.869,-35.632],[-56.705,-35.173],[-57.0,-35.189],[-57.328,-34.863],[-57.508,-34.781],[-57.9345,-34.685],[-57.9345,-34.452],[-58.0,-34.3765],[-58.1805,-34.233],[-58.295,-34.178],[-58.3445,-34.007],[-58.377,-34.0],[-58.4235,-33.9165],[-58.4345,-33.7205],[-58.474,-33.6525],[-58.494,-33.5815],[-58.4435,-33.5375],[-58.432,-33.366],[-58.3905,-33.277],[-58.41,-33.231],[-58.4045,-33.1805],[-58.3605,-33.12],[-58.257,-33.1025],[-58.176,-33.077],[-58.0845,-33.001],[-58.1145,-32.924],[-58.1205,-32.8115],[-58.15,-32.727],[-58.1455,-32.667],[-58.1605,-32.5725],[-58.204,-32.465],[-58.182,-32.3745],[-58.103,-32.3095],[-58.109,-32.24],[-58.18,-32.1655],[-58.168,-32.0935],[-58.142,-32.0565],[-58.1385,-32.002],[-58.207,-31.871],[-58.191,-31.853],[-58.0845,-31.82],[-58.0385,-31.7595],[-57.981,-31.5905],[-58.004,-31.532],[-58.075,-31.492],[-58.0815,-31.4545],[-57.98,-31.3895],[-57.997,-31.36],[-57.9385,-31.2755],[-57.913,-31.212],[-57.9165,-31.1225],[-57.8715,-31.0705],[-57.8615,-31.0325],[-57.915,-30.921],[-57.822,-30.908],[-57.7995,-30.853],[-57.809,-30.6955],[-57.844,-30.6585],[-57.848,-30.6185],[-57.883,-30.593],[-57.8885,-30.509],[-57.7675,-30.4185],[-57.6435,-30.3425],[-57.615,-30.256],[-57.649,-30.194]]],[[[-57.593,-27.392],[-57.4925,-27.4385],[-57.4885,-27.406],[-57.593,-27.392]]],[[[-57.032,-27.481],[-56.967,-27.495],[-56.911,-27.5315],[-56.8735,-27.5815],[-56.833,-27.596],[-56.749,-27.5615],[-56.726,-27.5125],[-56.77,-27.502],[-56.885,-27.4245],[-56.9225,-27.4225],[-57.032,-27.481]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/1a/Flag_of_Argentina.svg","name:en":"Argentina","wikidata":"Q414","ISO3166-1:alpha2":"AR","ISO3166-1:alpha3":"ARG","ISO3166-1:numeric":"032"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-92.207,1.6835],[-92.2045,1.6485],[-91.848,-0.431],[-91.6835,-1.038],[-91.646,-1.1115],[-91.5585,-1.1735],[-90.975,-1.385],[-90.522,-1.5495],[-90.4665,-1.561],[-89.639,-1.612],[-89.564,-1.602],[-89.505,-1.5705],[-89.4535,-1.51],[-89.266,-1.1755],[-89.064,-0.8155],[-89.0475,-0.7775],[-89.0395,-0.696],[-89.0725,-0.6045],[-89.2765,-0.3],[-89.787,0.46],[-89.8275,0.504],[-89.9225,0.55],[-90.3765,0.7145],[-90.662,0.8175],[-91.061,1.1515],[-91.8795,1.837],[-91.9255,1.866],[-91.996,1.8835],[-92.053,1.8785],[-92.1195,1.849],[-92.1815,1.7815],[-92.207,1.6835]]],[[[-75.258,-0.116],[-75.3665,-0.091],[-75.427,-0.087],[-75.469,-0.05],[-75.667,0.0455],[-75.7525,0.037],[-75.8145,0.075],[-75.8615,0.1345],[-75.952,0.2015],[-76.009,0.3005],[-76.0565,0.333],[-76.1205,0.324],[-76.129,0.368],[-76.1785,0.372],[-76.2615,0.427],[-76.314,0.434],[-76.3345,0.387],[-76.412,0.38],[-76.411,0.2425],[-76.504,0.2365],[-76.533,0.2585],[-76.5825,0.2295],[-76.6255,0.262],[-76.733,0.2885],[-76.769,0.243],[-76.7975,0.2645],[-76.8475,0.239],[-76.905,0.2455],[-76.9445,0.269],[-76.985,0.266],[-77.011,0.294],[-77.078,0.2855],[-77.1135,0.3725],[-77.178,0.3905],[-77.2145,0.363],[-77.302,0.3675],[-77.3265,0.3885],[-77.412,0.409],[-77.421,0.436],[-77.521,0.4165],[-77.522,0.5195],[-77.481,0.6045],[-77.4915,0.662],[-77.548,0.649],[-77.698,0.7415],[-77.6635,0.812],[-77.713,0.8505],[-77.7745,0.8415],[-77.8195,0.8085],[-77.8685,0.802],[-77.936,0.8165],[-77.9845,0.8535],[-78.0675,0.8955],[-78.1245,0.9365],[-78.1855,0.938],[-78.202,0.997],[-78.282,1.056],[-78.2765,1.109],[-78.3115,1.191],[-78.417,1.1605],[-78.437,1.1905],[-78.4885,1.186],[-78.5525,1.2505],[-78.615,1.246],[-78.71,1.359],[-78.7795,1.392],[-78.7995,1.4245],[-78.8685,1.4695],[-79.174,1.4695],[-79.56,1.4695],[-80.174,0.985],[-80.2295,0.9165],[-81.2605,-1.169],[-81.281,-1.274],[-81.2125,-2.1855],[-81.343,-3.37],[-81.3445,-3.392],[-80.671,-3.392],[-80.3065,-3.3925],[-80.2495,-3.4125],[-80.243,-3.4665],[-80.2065,-3.5345],[-80.212,-3.592],[-80.1855,-3.648],[-80.1995,-3.689],[-80.182,-3.7855],[-80.159,-3.809],[-80.1585,-3.8715],[-80.14,-3.913],[-80.1865,-3.929],[-80.299,-4.0175],[-80.3385,-3.993],[-80.398,-3.981],[-80.469,-4.038],[-80.4845,-4.09],[-80.449,-4.125],[-80.45,-4.2095],[-80.41,-4.218],[-80.372,-4.198],[-80.3315,-4.2255],[-80.3675,-4.28],[-80.412,-4.316],[-80.4535,-4.3825],[-80.4405,-4.4555],[-80.3885,-4.486],[-80.3295,-4.4655],[-80.2825,-4.4065],[-80.1555,-4.304],[-80.1065,-4.2915],[-80.0615,-4.311],[-80.0335,-4.3495],[-79.9525,-4.398],[-79.92,-4.386],[-79.8715,-4.413],[-79.8135,-4.492],[-79.745,-4.483],[-79.6555,-4.434],[-79.6175,-4.4455],[-79.5445,-4.525],[-79.505,-4.5175],[-79.477,-4.5685],[-79.488,-4.6275],[-79.463,-4.6545],[-79.458,-4.702],[-79.4315,-4.737],[-79.399,-4.829],[-79.361,-4.844],[-79.352,-4.8805],[-79.3065,-4.901],[-79.2655,-4.967],[-79.1805,-4.9605],[-79.102,-4.9785],[-79.076,-4.97],[-79.0125,-5.012],[-78.9725,-4.894],[-78.917,-4.892],[-78.891,-4.8085],[-78.889,-4.715],[-78.8425,-4.6545],[-78.805,-4.638],[-78.706,-4.624],[-78.661,-4.586],[-78.661,-4.53],[-78.6375,-4.501],[-78.634,-4.415],[-78.6525,-4.3325],[-78.618,-4.294],[-78.5985,-4.208],[-78.5785,-4.198],[-78.5815,-4.1355],[-78.541,-4.074],[-78.5625,-4.05],[-78.566,-3.9935],[-78.5245,-3.9375],[-78.4885,-3.9335],[-78.4765,-3.8585],[-78.414,-3.7925],[-78.4025,-3.7355],[-78.4225,-3.691],[-78.401,-3.6515],[-78.3875,-3.555],[-78.365,-3.5295],[-78.359,-3.4695],[-78.324,-3.391],[-78.248,-3.398],[-78.229,-3.5055],[-78.148,-3.479],[-78.155,-3.4265],[-78.1975,-3.365],[-78.17,-3.35],[-78.107,-3.2765],[-78.0785,-3.2265],[-78.0325,-3.189],[-77.971,-3.118],[-77.944,-3.0655],[-77.844,-3.0005],[-77.7105,-2.951],[-77.1745,-2.7715],[-76.6325,-2.591],[-76.0445,-2.125],[-75.5865,-1.5535],[-75.5555,-1.547],[-75.386,-0.9375],[-75.3135,-0.9845],[-75.26,-0.989],[-75.195,-0.9705],[-75.2315,-0.939],[-75.225,-0.8625],[-75.243,-0.7975],[-75.2775,-0.73],[-75.2515,-0.6915],[-75.2575,-0.6435],[-75.2235,-0.629],[-75.2415,-0.5245],[-75.313,-0.4945],[-75.3275,-0.4595],[-75.3855,-0.437],[-75.4735,-0.297],[-75.482,-0.237],[-75.531,-0.181],[-75.6105,-0.1925],[-75.6105,-0.1135],[-75.567,-0.1305],[-75.504,-0.127],[-75.4035,-0.17],[-75.3045,-0.154],[-75.258,-0.116]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/e8/Flag_of_Ecuador.svg","name:en":"Ecuador","wikidata":"Q736","ISO3166-1:alpha2":"EC","ISO3166-1:alpha3":"ECU","ISO3166-1:numeric":"218"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-5.4005,36.1485],[-5.364,36.061],[-5.2905,36.0855],[-5.277,36.1495],[-5.4005,36.1485]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/02/Flag_of_Gibraltar.svg","name:en":"Gibraltar","wikidata":"Q1410","ISO3166-1:alpha2":"GI","ISO3166-1:alpha3":"GIB","ISO3166-1:numeric":"292"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-58.1695,-20.167],[-58.055,-20.109],[-58.0215,-20.0585],[-57.9795,-20.057],[-57.961,-20.0245],[-57.905,-20.043],[-57.858,-19.9705],[-58.131,-19.758],[-57.9875,-19.46],[-57.784,-19.033],[-57.71,-19.0345],[-57.719,-18.899],[-57.766,-18.899],[-57.557,-18.24],[-57.505,-18.2005],[-57.574,-18.131],[-57.6,-18.046],[-57.723,-17.8305],[-57.685,-17.8105],[-57.791,-17.5865],[-57.746,-17.551],[-57.883,-17.449],[-57.98,-17.51],[-58.043,-17.492],[-58.06,-17.45],[-58.116,-17.451],[-58.151,-17.384],[-58.202,-17.357],[-58.254,-17.352],[-58.276,-17.3],[-58.317,-17.268],[-58.356,-17.212],[-58.396,-17.181],[-58.398,-17.104],[-58.425,-17.112],[-58.431,-16.99],[-58.462,-16.958],[-58.461,-16.841],[-58.476,-16.827],[-58.47,-16.703],[-58.436,-16.592],[-58.343,-16.517],[-58.334,-16.478],[-58.356,-16.428],[-58.344,-16.389],[-58.308,-16.3715],[-58.3035,-16.31],[-58.3295,-16.272],[-58.3665,-16.2745],[-58.431,-16.3225],[-58.785,-16.306],[-59.47,-16.279],[-60.174,-16.2665],[-60.203,-15.885],[-60.239,-15.475],[-60.575,-15.0975],[-60.245,-15.0975],[-60.2735,-14.621],[-60.323,-14.6105],[-60.34,-14.51],[-60.379,-14.4645],[-60.407,-14.4015],[-60.3945,-14.369],[-60.4545,-14.3135],[-60.4845,-14.1145],[-60.4475,-14.0845],[-60.4045,-13.9985],[-60.4185,-13.9375],[-60.4505,-13.927],[-60.453,-13.881],[-60.5205,-13.77],[-60.569,-13.7685],[-60.6245,-13.719],[-60.654,-13.7355],[-60.687,-13.7005],[-60.771,-13.6795],[-60.832,-13.627],[-60.877,-13.62],[-60.927,-13.5395],[-61.009,-13.5495],[-61.0425,-13.5085],[-61.086,-13.492],[-61.149,-13.532],[-61.2345,-13.5255],[-61.2545,-13.4965],[-61.331,-13.5035],[-61.4665,-13.554],[-61.578,-13.5115],[-61.6645,-13.503],[-61.7865,-13.5375],[-61.869,-13.526],[-61.8885,-13.441],[-61.926,-13.4285],[-61.976,-13.367],[-62.017,-13.35],[-62.115,-13.256],[-62.1215,-13.151],[-62.1985,-13.152],[-62.2155,-13.1105],[-62.2645,-13.147],[-62.394,-13.1335],[-62.459,-13.077],[-62.539,-13.074],[-62.6145,-13.038],[-62.631,-12.99],[-62.6615,-12.9685],[-62.7005,-13.002],[-62.7965,-12.993],[-62.8045,-12.943],[-62.8745,-12.905],[-62.9215,-12.84],[-62.9705,-12.8595],[-63.0465,-12.741],[-63.084,-12.7165],[-63.09,-12.638],[-63.1825,-12.6275],[-63.24,-12.6905],[-63.297,-12.6825],[-63.3885,-12.6495],[-63.436,-12.5665],[-63.507,-12.5645],[-63.559,-12.504],[-63.623,-12.4725],[-63.7835,-12.428],[-63.831,-12.461],[-63.8765,-12.445],[-63.901,-12.503],[-63.9455,-12.528],[-63.99,-12.503],[-64.0365,-12.517],[-64.0555,-12.4935],[-64.174,-12.503],[-64.1725,-12.47],[-64.228,-12.4545],[-64.277,-12.4985],[-64.2995,-12.4565],[-64.3625,-12.4605],[-64.428,-12.432],[-64.4555,-12.3865],[-64.5035,-12.3655],[-64.4955,-12.2675],[-64.509,-12.2285],[-64.5605,-12.2365],[-64.693,-12.1895],[-64.7405,-12.1445],[-64.7605,-12.0945],[-64.8035,-12.0865],[-64.832,-12.016],[-64.9175,-12.0265],[-64.944,-11.995],[-64.9805,-12.0175],[-65.035,-11.9945],[-65.0275,-11.892],[-65.0725,-11.857],[-65.064,-11.7715],[-65.099,-11.703],[-65.1845,-11.721],[-65.221,-11.7415],[-65.2585,-11.697],[-65.22,-11.6635],[-65.234,-11.605],[-65.224,-11.516],[-65.3085,-11.496],[-65.339,-11.311],[-65.3585,-11.2685],[-65.353,-11.124],[-65.287,-11.094],[-65.2985,-11.026],[-65.2515,-10.99],[-65.2705,-10.96],[-65.275,-10.873],[-65.321,-10.8225],[-65.363,-10.801],[-65.343,-10.7675],[-65.354,-10.6615],[-65.4055,-10.6395],[-65.399,-10.5815],[-65.4295,-10.4795],[-65.381,-10.429],[-65.3905,-10.371],[-65.324,-10.301],[-65.2885,-10.2145],[-65.306,-10.165],[-65.301,-10.098],[-65.329,-10.046],[-65.32,-10.001],[-65.331,-9.929],[-65.2885,-9.869],[-65.355,-9.7225],[-65.393,-9.687],[-65.442,-9.6905],[-65.5015,-9.7305],[-65.5125,-9.7935],[-65.627,-9.838],[-65.6695,-9.7815],[-65.727,-9.7585],[-65.7845,-9.7875],[-65.8205,-9.7675],[-65.855,-9.7955],[-65.886,-9.752],[-65.975,-9.7765],[-65.984,-9.8045],[-66.0385,-9.785],[-66.173,-9.7925],[-66.212,-9.835],[-66.2375,-9.8135],[-66.284,-9.844],[-66.312,-9.831],[-66.3585,-9.864],[-66.4885,-9.868],[-66.62,-9.8935],[-66.6365,-9.943],[-66.7285,-9.98],[-66.8335,-10.061],[-66.8435,-10.087],[-66.898,-10.111],[-66.9825,-10.1965],[-67.016,-10.2585],[-67.079,-10.2895],[-67.125,-10.2905],[-67.17,-10.3375],[-67.232,-10.315],[-67.2625,-10.331],[-67.3165,-10.32],[-67.318,-10.3665],[-67.4195,-10.388],[-67.4135,-10.427],[-67.451,-10.4585],[-67.579,-10.503],[-67.612,-10.5635],[-67.6755,-10.605],[-67.7075,-10.71],[-67.73,-10.7135],[-67.8275,-10.6515],[-67.866,-10.6405],[-67.977,-10.662],[-68.015,-10.649],[-68.105,-10.715],[-68.0975,-10.7485],[-68.1255,-10.7985],[-68.196,-10.8605],[-68.236,-10.9605],[-68.2765,-10.99],[-68.429,-11.036],[-68.4835,-11.0775],[-68.5905,-11.1155],[-68.7155,-11.1475],[-68.73,-11.0985],[-68.7655,-11.0665],[-68.748,-11.009],[-68.8345,-10.993],[-68.9125,-11.0215],[-68.9685,-10.9875],[-68.9975,-11.0025],[-69.0715,-10.967],[-69.148,-10.972],[-69.2325,-10.94],[-69.2915,-10.95],[-69.401,-10.9305],[-69.479,-10.955],[-69.522,-10.936],[-69.572,-10.9455],[-69.379,-11.264],[-69.255,-11.459],[-68.9705,-11.913],[-68.652,-12.498],[-68.713,-12.603],[-68.7415,-12.6355],[-68.713,-12.6815],[-68.743,-12.726],[-68.788,-12.757],[-68.8345,-12.813],[-68.836,-12.859],[-68.8715,-12.8855],[-68.8705,-13.019],[-68.85,-13.037],[-68.8745,-13.1155],[-68.8455,-13.246],[-68.917,-13.4865],[-68.993,-13.65],[-69.061,-13.657],[-69.066,-13.6885],[-68.977,-13.758],[-68.9805,-13.7835],[-68.9175,-13.8135],[-68.951,-13.8785],[-68.982,-13.894],[-68.9785,-13.976],[-68.942,-14.0215],[-68.8955,-14.0415],[-68.856,-14.1625],[-68.828,-14.22],[-68.9625,-14.22],[-69.015,-14.3145],[-68.9855,-14.3745],[-69.031,-14.426],[-69.084,-14.452],[-69.1405,-14.5135],[-69.15,-14.5805],[-69.22,-14.5815],[-69.2295,-14.6565],[-69.258,-14.683],[-69.2255,-14.7405],[-69.301,-14.7655],[-69.354,-14.8015],[-69.344,-14.8825],[-69.3615,-14.949],[-69.299,-15.0495],[-69.285,-15.095],[-69.236,-15.1205],[-69.133,-15.221],[-69.1315,-15.246],[-69.2155,-15.317],[-69.2485,-15.371],[-69.2535,-15.469],[-69.29,-15.437],[-69.3295,-15.537],[-69.402,-15.606],[-69.1965,-16.168],[-69.1025,-16.225],[-69.0555,-16.1975],[-69.013,-16.22],[-68.9615,-16.1945],[-68.893,-16.2595],[-68.7955,-16.3315],[-68.8005,-16.349],[-68.9885,-16.4255],[-69.04,-16.5735],[-69.017,-16.638],[-69.0455,-16.6885],[-69.17,-16.7275],[-69.219,-16.836],[-69.3045,-16.916],[-69.3435,-16.987],[-69.3945,-17.024],[-69.3735,-17.073],[-69.4065,-17.1005],[-69.449,-17.098],[-69.512,-17.146],[-69.557,-17.1595],[-69.616,-17.2615],[-69.4685,-17.374],[-69.4685,-17.4985],[-69.468,-17.605],[-69.337,-17.7315],[-69.3075,-17.8055],[-69.3085,-17.896],[-69.281,-17.9695],[-69.121,-18.005],[-69.057,-18.0615],[-69.146,-18.163],[-69.064,-18.2325],[-69.075,-18.2925],[-69.053,-18.347],[-69.0475,-18.4265],[-69.0045,-18.476],[-69.0325,-18.629],[-68.988,-18.674],[-68.977,-18.738],[-68.9255,-18.843],[-68.92,-18.885],[-68.9505,-18.9365],[-68.8895,-19.0435],[-68.806,-19.0835],[-68.6285,-19.251],[-68.62,-19.2765],[-68.5425,-19.297],[-68.4515,-19.368],[-68.405,-19.416],[-68.441,-19.4405],[-68.557,-19.5965],[-68.5985,-19.641],[-68.625,-19.702],[-68.6825,-19.718],[-68.69,-19.75],[-68.6205,-19.7895],[-68.5995,-19.828],[-68.5285,-19.8455],[-68.5425,-19.8895],[-68.5215,-19.9245],[-68.5665,-20.05],[-68.64,-20.049],[-68.7625,-20.0785],[-68.7735,-20.1295],[-68.705,-20.1335],[-68.7135,-20.23],[-68.6605,-20.3195],[-68.672,-20.355],[-68.7415,-20.3685],[-68.7385,-20.457],[-68.6665,-20.5165],[-68.6135,-20.5365],[-68.5735,-20.575],[-68.535,-20.5695],[-68.4505,-20.6475],[-68.526,-20.6965],[-68.5535,-20.7285],[-68.541,-20.7965],[-68.553,-20.8765],[-68.493,-20.946],[-68.4125,-20.9345],[-68.295,-21.0975],[-68.179,-21.3025],[-68.1805,-21.6045],[-68.1175,-21.6785],[-68.0655,-21.7675],[-68.063,-21.8525],[-68.0715,-21.979],[-67.971,-22.0545],[-67.939,-22.108],[-67.957,-22.1515],[-67.933,-22.2015],[-67.9215,-22.2755],[-67.9385,-22.2945],[-67.9335,-22.391],[-67.903,-22.4035],[-67.893,-22.4995],[-67.8455,-22.531],[-67.8385,-22.5885],[-67.874,-22.64],[-67.8675,-22.6795],[-67.89,-22.719],[-67.8675,-22.745],[-67.8815,-22.834],[-67.7985,-22.881],[-67.5675,-22.8985],[-67.1805,-22.814],[-67.1275,-22.722],[-67.014,-22.655],[-67.027,-22.541],[-66.973,-22.5405],[-66.9285,-22.492],[-66.7825,-22.438],[-66.7395,-22.2385],[-66.686,-22.2095],[-66.348,-22.128],[-66.288,-22.086],[-66.2925,-22.0345],[-66.2605,-21.906],[-66.236,-21.8495],[-66.241,-21.789],[-66.0765,-21.835],[-66.0435,-21.872],[-66.048,-21.919],[-65.925,-21.9335],[-65.805,-22.075],[-65.7485,-22.109],[-65.6755,-22.1115],[-65.5825,-22.094],[-65.5745,-22.076],[-65.475,-22.0895],[-64.9935,-22.0825],[-64.8855,-22.129],[-64.8625,-22.123],[-64.7375,-22.1935],[-64.651,-22.1895],[-64.542,-22.279],[-64.5655,-22.372],[-64.4905,-22.4385],[-64.4455,-22.523],[-64.4125,-22.5375],[-64.4335,-22.6485],[-64.397,-22.6835],[-64.393,-22.7195],[-64.3445,-22.739],[-64.329,-22.8565],[-64.2825,-22.8245],[-64.2655,-22.7665],[-64.272,-22.71],[-64.2425,-22.56],[-64.1995,-22.491],[-64.1155,-22.4045],[-64.1165,-22.362],[-64.0625,-22.2385],[-64.009,-22.1655],[-63.986,-22.0935],[-63.9315,-21.9995],[-63.3505,-21.9995],[-62.811,-21.999],[-62.799,-22.06],[-62.813,-22.0965],[-62.717,-22.2035],[-62.6405,-22.236],[-62.458,-21.6725],[-62.261,-21.0585],[-62.258,-20.9895],[-62.2665,-20.563],[-61.9225,-20.089],[-61.735,-19.6345],[-60.9985,-19.519],[-60.6045,-19.4565],[-59.978,-19.2945],[-59.5,-19.291],[-59.069,-19.2875],[-58.6965,-19.5055],[-58.236,-19.775],[-58.164,-19.827],[-58.202,-19.8625],[-58.197,-19.925],[-58.1655,-20.0645],[-58.138,-20.124],[-58.1695,-20.167]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/48/Flag_of_Bolivia.svg","name:en":"Bolivia","wikidata":"Q750","ISO3166-1:alpha2":"BO","ISO3166-1:alpha3":"BOL","ISO3166-1:numeric":"068"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-87.295,5.519],[-87.288,5.4665],[-87.2525,5.3925],[-87.201,5.342],[-87.1505,5.3135],[-87.074,5.298],[-86.985,5.319],[-86.911,5.3655],[-86.864,5.4155],[-86.838,5.468],[-86.827,5.5345],[-86.859,5.65],[-86.911,5.709],[-87.009,5.7545],[-87.1145,5.747],[-87.1915,5.7085],[-87.25,5.6535],[-87.2765,5.608],[-87.295,5.519]]],[[[-82.506,9.654],[-82.5555,9.6985],[-82.7035,9.721],[-82.728,9.7955],[-82.753,9.8125],[-82.9185,10.0215],[-82.9975,10.072],[-83.2485,10.4015],[-83.307,10.4585],[-83.4525,10.7655],[-83.489,10.899],[-83.5405,10.9685],[-83.6285,10.998],[-83.658,11.0295],[-83.7035,10.9425],[-83.667,10.8815],[-83.671,10.797],[-83.7945,10.7675],[-83.8625,10.7175],[-83.9135,10.711],[-83.9685,10.731],[-84.006,10.7665],[-84.0705,10.76],[-84.14,10.7875],[-84.222,10.8005],[-84.226,10.8745],[-84.319,10.916],[-84.3585,10.996],[-84.4205,10.9555],[-84.485,11.0],[-84.606,11.0385],[-84.6805,11.0845],[-84.91,10.9435],[-85.23,11.064],[-85.357,11.126],[-85.4235,11.129],[-85.5235,11.168],[-85.5605,11.21],[-85.61,11.2195],[-85.693,11.079],[-85.706,11.069],[-85.898,11.076],[-86.042,10.919],[-86.045,10.82],[-86.0005,10.7635],[-85.883,10.751],[-85.802,10.7175],[-85.8,10.651],[-85.9375,10.529],[-85.952,10.4585],[-85.9915,10.369],[-85.9865,10.317],[-85.929,10.0835],[-85.769,9.8845],[-85.7385,9.836],[-85.6895,9.7995],[-85.5715,9.7835],[-85.389,9.732],[-85.3705,9.6995],[-85.2725,9.591],[-85.2445,9.5345],[-85.139,9.463],[-85.055,9.4725],[-85.0105,9.5235],[-84.9695,9.6015],[-84.9165,9.6255],[-84.8395,9.688],[-84.776,9.7605],[-84.7555,9.7345],[-84.7825,9.6695],[-84.7675,9.566],[-84.551,9.409],[-84.464,9.4175],[-84.25,9.359],[-84.2105,9.3015],[-84.0735,9.2425],[-83.9065,9.109],[-83.796,9.0415],[-83.765,8.9685],[-83.7595,8.764],[-83.7815,8.75],[-83.869,8.773],[-83.9395,8.7395],[-83.9465,8.6935],[-83.8395,8.5695],[-83.8055,8.5045],[-83.634,8.3475],[-83.5585,8.3175],[-83.497,8.3345],[-83.2775,8.2595],[-83.2275,8.294],[-83.2085,8.252],[-83.0875,8.1855],[-83.0125,8.0655],[-82.991,7.9635],[-82.9455,7.9355],[-82.8875,8.077],[-82.9295,8.2455],[-82.975,8.2895],[-83.046,8.318],[-82.9355,8.424],[-82.8685,8.4395],[-82.832,8.5045],[-82.822,8.567],[-82.83,8.636],[-82.8705,8.6985],[-82.916,8.7415],[-82.9145,8.7695],[-82.8665,8.8055],[-82.855,8.856],[-82.7605,8.885],[-82.712,8.9225],[-82.7505,8.9925],[-82.874,9.0575],[-82.887,9.088],[-82.936,9.0785],[-82.936,9.473],[-82.8445,9.5],[-82.885,9.5705],[-82.857,9.62],[-82.7595,9.587],[-82.6845,9.5065],[-82.6355,9.494],[-82.565,9.572],[-82.506,9.654]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/f2/Flag_of_Costa_Rica.svg","name:en":"Costa Rica","wikidata":"Q800","ISO3166-1:alpha2":"CR","ISO3166-1:alpha3":"CRI","ISO3166-1:numeric":"188"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-88.1755,15.9075],[-88.4985,16.07],[-88.686,16.051],[-88.7705,15.9585],[-88.9575,15.8885],[-89.022,15.9115],[-89.103,15.9125],[-89.2275,15.896],[-89.2085,16.1795],[-89.1735,16.7115],[-89.15,17.0575],[-89.151,17.4485],[-89.152,17.8155],[-89.5155,17.8155],[-90.2455,17.816],[-90.9875,17.8155],[-90.9875,17.2515],[-91.439,17.251],[-91.429,17.2185],[-91.3255,17.1825],[-91.278,17.179],[-91.2685,17.11],[-91.2325,17.1],[-91.2035,17.045],[-91.0645,16.925],[-91.009,16.887],[-90.9575,16.897],[-90.92,16.821],[-90.8825,16.825],[-90.847,16.796],[-90.7645,16.7635],[-90.683,16.6895],[-90.659,16.6425],[-90.637,16.5135],[-90.586,16.47],[-90.5035,16.4685],[-90.4885,16.4285],[-90.42,16.4245],[-90.3985,16.3745],[-90.4105,16.3095],[-90.4395,16.3045],[-90.455,16.189],[-90.4265,16.1625],[-90.442,16.074],[-90.961,16.074],[-91.7315,16.074],[-92.017,15.5895],[-92.2105,15.2605],[-92.0595,15.071],[-92.102,15.011],[-92.133,15.0115],[-92.1485,14.9485],[-92.136,14.893],[-92.185,14.8285],[-92.159,14.769],[-92.147,14.6585],[-92.186,14.5725],[-92.3105,14.451],[-92.1135,14.281],[-91.9455,14.13],[-91.7395,14.0025],[-91.5005,13.88],[-91.272,13.8145],[-91.111,13.7895],[-90.733,13.7995],[-90.536,13.7735],[-90.179,13.6345],[-90.118,13.789],[-90.113,13.824],[-90.023,13.939],[-89.9615,13.9705],[-89.8915,14.0435],[-89.8165,14.063],[-89.776,14.0345],[-89.746,14.0635],[-89.714,14.138],[-89.667,14.1855],[-89.523,14.2245],[-89.5605,14.3125],[-89.588,14.346],[-89.574,14.417],[-89.4295,14.415],[-89.3895,14.449],[-89.356,14.4205],[-89.355,14.47],[-89.303,14.4965],[-89.2385,14.585],[-89.1595,14.5745],[-89.1575,14.67],[-89.1315,14.7125],[-89.1655,14.731],[-89.169,14.775],[-89.224,14.8345],[-89.228,14.8825],[-89.1815,14.911],[-89.1565,14.98],[-89.1845,15.0025],[-89.1535,15.0685],[-88.974,15.136],[-88.8455,15.243],[-88.678,15.3425],[-88.605,15.413],[-88.5525,15.4465],[-88.3455,15.6175],[-88.3255,15.6705],[-88.2475,15.69],[-88.2215,15.7235],[-88.1755,15.9075]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/ec/Flag_of_Guatemala.svg","name:en":"Guatemala","wikidata":"Q774","ISO3166-1:alpha2":"GT","ISO3166-1:alpha3":"GTM","ISO3166-1:numeric":"320"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-131.435,54.6895],[-131.436,54.567],[-131.391,54.483],[-131.288,54.41],[-131.2495,54.355],[-131.125,54.277],[-131.164,54.183],[-131.1655,54.129],[-131.1265,54.058],[-131.05,54.002],[-131.0675,53.916],[-131.037,53.796],[-130.895,53.665],[-130.9105,53.601],[-130.9535,53.537],[-130.963,53.475],[-130.927,53.393],[-130.8745,53.347],[-130.7525,53.289],[-130.5735,53.263],[-130.4645,53.207],[-130.3915,53.185],[-130.352,53.143],[-130.2055,53.03],[-130.125,52.992],[-130.032,52.97],[-129.9405,52.872],[-129.817,52.789],[-129.726,52.761],[-129.7525,52.658],[-129.742,52.494],[-129.6975,52.413],[-129.6225,52.36],[-129.54,52.33],[-129.402,52.314],[-129.2775,52.324],[-129.213,52.279],[-128.9935,52.199],[-128.9035,52.136],[-128.823,52.102],[-128.7625,52.033],[-128.8025,51.927],[-128.7835,51.845],[-128.7245,51.779],[-128.637,51.733],[-128.5245,51.695],[-128.47,51.651],[-128.4565,51.545],[-128.3925,51.424],[-128.325,51.347],[-128.1515,51.272],[-128.856,51.0155],[-128.9905,51.0665],[-129.1645,51.068],[-129.292,51.024],[-129.3755,50.9585],[-129.403,50.9065],[-129.4075,50.819],[-129.3365,50.7235],[-129.25,50.6785],[-128.986,50.6185],[-128.829,50.621],[-128.7555,50.604],[-128.6145,50.547],[-128.453,50.406],[-128.3,50.317],[-128.257,50.237],[-128.212,50.193],[-128.2315,50.133],[-128.2155,50.055],[-128.1705,49.995],[-128.032,49.92],[-127.8885,49.885],[-127.8035,49.88],[-127.6915,49.892],[-127.589,49.825],[-127.4635,49.796],[-127.406,49.74],[-127.2655,49.671],[-127.212,49.609],[-127.0475,49.485],[-126.964,49.442],[-126.8795,49.418],[-126.83,49.306],[-126.756,49.248],[-126.614,49.191],[-126.4915,49.178],[-126.4455,49.14],[-126.3395,49.086],[-126.284,49.036],[-125.881,48.847],[-125.7475,48.775],[-125.6475,48.736],[-125.5225,48.719],[-125.4105,48.635],[-125.217,48.542],[-125.0035,48.495],[-124.8405,48.506],[-124.727,48.4935],[-124.012,48.2965],[-123.679,48.24],[-123.541,48.2245],[-123.2485,48.284],[-123.115,48.423],[-123.16,48.4535],[-123.219,48.5485],[-123.268,48.694],[-123.0085,48.767],[-123.0085,48.831],[-123.322,49.002],[-122.787,49.002],[-122.098,49.0025],[-121.7595,48.9975],[-121.335,49.001],[-120.6695,49.0],[-120.0025,48.9995],[-119.047,49.0],[-118.3895,49.0],[-117.992,49.0005],[-117.135,48.999],[-116.499,49.0],[-115.8615,49.001],[-115.2625,48.9995],[-114.362,49.001],[-113.6945,48.9975],[-112.924,48.9985],[-112.393,48.9985],[-111.5755,48.9965],[-110.88,48.998],[-110.326,48.999],[-109.4205,49.0005],[-108.8355,48.9995],[-107.886,49.0],[-106.986,49.0],[-106.2295,48.9995],[-105.561,48.9995],[-104.685,48.999],[-103.807,48.9995],[-103.3775,48.999],[-102.4565,48.999],[-101.981,48.999],[-101.17,48.9995],[-100.471,48.9995],[-99.5405,48.9995],[-98.98,49.0],[-98.2105,49.0005],[-97.4815,49.0005],[-96.6525,49.0],[-95.633,48.9995],[-95.153,48.999],[-95.1535,49.3845],[-95.0585,49.3535],[-94.9575,49.37],[-94.854,49.3245],[-94.8255,49.2945],[-94.773,49.1205],[-94.7495,49.099],[-94.6835,48.884],[-94.7045,48.824],[-94.691,48.778],[-94.5875,48.7175],[-94.439,48.695],[-94.416,48.711],[-94.261,48.6965],[-94.2445,48.6535],[-93.848,48.631],[-93.804,48.57],[-93.7935,48.5165],[-93.6475,48.5175],[-93.4675,48.5465],[-93.467,48.5885],[-93.3485,48.6265],[-93.2075,48.6425],[-93.1785,48.6235],[-92.9845,48.624],[-92.7285,48.5395],[-92.635,48.5425],[-92.637,48.4995],[-92.7125,48.463],[-92.656,48.4365],[-92.5075,48.448],[-92.4565,48.401],[-92.47,48.352],[-92.375,48.226],[-92.2805,48.2445],[-92.3015,48.2885],[-92.289,48.343],[-92.055,48.3595],[-92.0,48.321],[-92.0065,48.2655],[-91.9585,48.233],[-91.893,48.238],[-91.8645,48.207],[-91.7155,48.1995],[-91.7115,48.1145],[-91.5695,48.0935],[-91.5755,48.049],[-91.488,48.0685],[-91.4295,48.0485],[-91.371,48.0695],[-91.2495,48.084],[-91.0825,48.181],[-90.8855,48.246],[-90.8395,48.2395],[-90.8325,48.1735],[-90.7615,48.0985],[-90.6415,48.1035],[-90.375,48.091],[-90.306,48.105],[-90.1325,48.1115],[-90.0235,48.085],[-89.994,48.028],[-89.899,47.988],[-89.7675,48.023],[-89.5855,48.002],[-89.489,48.0145],[-89.3375,47.9745],[-88.6775,48.2455],[-88.37,48.306],[-87.6475,48.029],[-87.1125,47.818],[-85.319,47.0795],[-84.86,46.889],[-84.763,46.6345],[-84.557,46.461],[-84.4755,46.453],[-84.445,46.489],[-84.374,46.509],[-84.2935,46.493],[-84.226,46.534],[-84.129,46.5305],[-84.111,46.504],[-84.146,46.419],[-84.108,46.2415],[-84.077,46.1875],[-84.006,46.1495],[-83.9555,46.057],[-83.9035,46.0605],[-83.8265,46.119],[-83.76,46.1025],[-83.655,46.1215],[-83.572,46.106],[-83.434,45.998],[-83.597,45.8215],[-82.757,45.4455],[-82.5185,45.3385],[-82.4085,44.8585],[-82.25,44.165],[-82.123,43.591],[-82.414,43.011],[-82.413,42.9775],[-82.4555,42.927],[-82.482,42.8085],[-82.4675,42.7625],[-82.5235,42.6075],[-82.6055,42.5485],[-82.668,42.5335],[-82.83,42.3735],[-83.0635,42.318],[-83.128,42.2385],[-83.1215,42.1255],[-83.1495,42.041],[-83.069,41.8635],[-82.6795,41.6765],[-82.3975,41.6765],[-82.269,41.7415],[-81.8545,41.933],[-81.799,41.953],[-81.2455,42.2075],[-80.521,42.323],[-80.08,42.3935],[-79.5705,42.5875],[-78.9355,42.8285],[-78.906,42.9],[-78.9325,42.956],[-79.0165,42.984],[-79.011,43.066],[-79.074,43.078],[-79.0425,43.1435],[-79.056,43.2545],[-79.0775,43.2725],[-79.2005,43.4505],[-78.6905,43.631],[-77.928,43.631],[-77.442,43.631],[-76.7965,43.631],[-76.439,44.094],[-76.353,44.1345],[-76.3125,44.199],[-76.2455,44.204],[-76.1645,44.24],[-76.162,44.2805],[-76.001,44.348],[-75.913,44.368],[-75.8215,44.432],[-75.767,44.5155],[-75.505,44.7055],[-75.2185,44.878],[-75.14,44.897],[-74.9725,44.9835],[-74.908,44.9835],[-74.8265,45.016],[-74.731,44.9905],[-74.6835,44.9995],[-74.15,44.9915],[-73.883,45.001],[-73.65,45.003],[-73.0655,45.016],[-72.6345,45.0145],[-72.312,45.004],[-71.914,45.0075],[-71.501,45.0135],[-71.4955,45.065],[-71.4295,45.1225],[-71.3975,45.2055],[-71.3565,45.254],[-71.288,45.301],[-71.2305,45.2495],[-71.133,45.2445],[-71.098,45.3015],[-70.989,45.334],[-70.918,45.3115],[-70.8845,45.235],[-70.806,45.3215],[-70.8255,45.4005],[-70.7815,45.431],[-70.712,45.3905],[-70.6505,45.377],[-70.6245,45.406],[-70.681,45.452],[-70.7225,45.5135],[-70.6455,45.6065],[-70.553,45.668],[-70.4005,45.7195],[-70.417,45.7955],[-70.254,45.9025],[-70.2395,45.94],[-70.3125,45.962],[-70.3135,46.0225],[-70.2895,46.0945],[-70.2395,46.149],[-70.2925,46.1915],[-70.209,46.3295],[-70.149,46.359],[-70.095,46.41],[-70.0565,46.4165],[-69.997,46.6955],[-69.587,47.1045],[-69.2245,47.46],[-69.176,47.457],[-69.0355,47.415],[-69.0545,47.3765],[-69.041,47.245],[-68.905,47.1805],[-68.8115,47.215],[-68.687,47.2445],[-68.6075,47.247],[-68.58,47.287],[-68.517,47.296],[-68.378,47.2875],[-68.3845,47.324],[-68.3355,47.3595],[-68.2235,47.3445],[-67.952,47.1945],[-67.883,47.1045],[-67.79,47.067],[-67.7885,46.6015],[-67.782,46.263],[-67.7815,45.9435],[-67.751,45.918],[-67.8045,45.869],[-67.7555,45.8235],[-67.802,45.803],[-67.8035,45.6775],[-67.713,45.681],[-67.675,45.6305],[-67.499,45.5865],[-67.4565,45.6045],[-67.4205,45.5495],[-67.4275,45.501],[-67.5,45.491],[-67.4725,45.423],[-67.427,45.39],[-67.429,45.3445],[-67.4765,45.2755],[-67.464,45.245],[-67.3805,45.152],[-67.321,45.1315],[-67.291,45.1875],[-67.2275,45.1635],[-67.159,45.162],[-67.1125,45.1125],[-67.0215,44.954],[-66.9665,44.91],[-66.9655,44.829],[-66.8855,44.794],[-66.987,44.6955],[-67.039,44.6245],[-67.157,44.5825],[-67.2525,44.4965],[-67.304,44.3935],[-67.301,44.3595],[-67.165,44.305],[-67.05,44.303],[-66.947,44.3325],[-66.871,44.3855],[-66.788,44.37],[-66.61,44.378],[-66.6495,44.335],[-66.677,44.263],[-66.6695,44.193],[-66.6425,44.139],[-66.5435,44.057],[-66.482,44.032],[-66.449,43.977],[-66.4495,43.791],[-66.434,43.739],[-66.334,43.609],[-66.29,43.535],[-66.204,43.473],[-66.06,43.442],[-66.0485,43.385],[-65.958,43.326],[-65.7615,43.222],[-65.701,43.204],[-65.5935,43.197],[-65.5275,43.208],[-65.3465,43.276],[-65.1935,43.347],[-65.1425,43.381],[-65.087,43.452],[-64.9785,43.473],[-64.8785,43.511],[-64.7805,43.588],[-64.662,43.655],[-64.584,43.718],[-64.4915,43.829],[-64.354,43.926],[-64.294,43.987],[-64.1805,44.024],[-64.05,44.106],[-63.9425,44.202],[-63.906,44.262],[-63.8165,44.24],[-63.6215,44.232],[-63.476,44.247],[-63.403,44.273],[-63.3365,44.32],[-63.2655,44.427],[-63.188,44.439],[-63.0995,44.476],[-62.8775,44.472],[-62.726,44.505],[-62.616,44.555],[-62.4045,44.615],[-62.2255,44.68],[-62.1805,44.705],[-62.1125,44.709],[-61.901,44.761],[-61.752,44.845],[-61.7075,44.881],[-61.4545,44.939],[-61.3945,44.968],[-61.191,45.016],[-61.101,45.022],[-60.9135,45.062],[-60.8105,45.109],[-60.7115,45.2],[-60.677,45.276],[-60.6845,45.372],[-60.594,45.385],[-60.486,45.433],[-60.308,45.471],[-60.08,45.56],[-60.012,45.608],[-59.8895,45.669],[-59.8345,45.724],[-59.779,45.754],[-59.698,45.77],[-59.611,45.806],[-59.493,45.88],[-59.414,45.998],[-59.408,46.07],[-59.457,46.159],[-59.542,46.229],[-59.6005,46.311],[-59.6795,46.361],[-59.916,46.434],[-60.0895,46.471],[-60.129,46.499],[-60.0785,46.581],[-60.0685,46.669],[-60.0225,46.847],[-60.0415,46.929],[-60.068,46.968],[-59.9295,47.075],[-59.309,47.383],[-59.212,47.368],[-58.972,47.374],[-58.856,47.397],[-58.671,47.406],[-58.49,47.447],[-58.3025,47.454],[-58.145,47.479],[-57.961,47.457],[-57.8615,47.428],[-57.655,47.402],[-57.604,47.364],[-57.5165,47.329],[-57.3815,47.317],[-57.213,47.347],[-57.1465,47.377],[-57.069,47.38],[-56.92,47.335],[-56.8045,47.334],[-56.705,47.351],[-56.5915,47.391],[-56.4885,47.41],[-56.4945,47.365],[-56.255,47.3055],[-56.105,47.1005],[-56.1295,46.931],[-56.0995,46.86],[-55.9825,46.803],[-55.9035,46.646],[-55.7085,46.654],[-55.5545,46.69],[-55.46,46.679],[-55.263,46.694],[-55.173,46.721],[-55.044,46.799],[-54.929,46.88],[-54.8075,47.039],[-54.653,47.076],[-54.589,47.118],[-54.4465,47.157],[-54.3905,47.136],[-54.479,47.01],[-54.4965,46.95],[-54.5035,46.838],[-54.481,46.754],[-54.411,46.685],[-54.3145,46.637],[-54.1965,46.608],[-54.066,46.6],[-53.9305,46.623],[-53.895,46.555],[-53.8125,46.484],[-53.6985,46.434],[-53.625,46.418],[-53.5155,46.419],[-53.353,46.458],[-53.2085,46.429],[-53.09,46.436],[-52.9255,46.491],[-52.876,46.526],[-52.7565,46.645],[-52.69,46.723],[-52.6395,46.813],[-52.631,46.865],[-52.5795,46.997],[-52.5685,47.08],[-52.542,47.119],[-52.4565,47.297],[-52.3655,47.4],[-52.3275,47.49],[-52.328,47.56],[-52.373,47.694],[-52.4535,47.836],[-52.558,47.939],[-52.6,47.966],[-52.5265,48.035],[-52.5015,48.101],[-52.506,48.187],[-52.531,48.245],[-52.638,48.328],[-52.795,48.361],[-52.722,48.484],[-52.715,48.602],[-52.748,48.704],[-52.794,48.767],[-52.879,48.839],[-52.9435,48.872],[-53.0355,48.895],[-53.157,48.895],[-53.3005,48.848],[-53.3095,48.894],[-53.273,49.014],[-53.2005,49.126],[-53.1655,49.24],[-53.172,49.3],[-53.2285,49.384],[-53.272,49.419],[-53.4785,49.518],[-53.46,49.626],[-53.5055,49.702],[-53.581,49.754],[-53.6955,49.788],[-53.7705,49.793],[-53.919,49.902],[-54.0225,49.936],[-54.202,49.946],[-54.4465,49.895],[-54.5995,49.896],[-54.6745,49.88],[-54.8775,49.878],[-54.981,49.849],[-55.0435,49.813],[-55.1105,49.744],[-55.3005,49.741],[-55.1925,49.879],[-55.172,49.991],[-55.1945,50.045],[-55.498,50.358],[-55.638,50.419],[-55.718,50.431],[-55.989,50.427],[-55.9525,50.477],[-55.8665,50.539],[-55.6845,50.505],[-55.5395,50.496],[-55.4595,50.508],[-55.3355,50.559],[-55.2445,50.626],[-55.195,50.681],[-55.153,50.789],[-55.1785,50.877],[-55.234,50.931],[-55.2365,51.007],[-55.283,51.087],[-55.3575,51.14],[-55.32,51.191],[-55.2335,51.247],[-55.1795,51.321],[-55.0985,51.539],[-55.098,51.669],[-55.1365,51.73],[-55.0665,51.787],[-54.967,51.93],[-54.9555,52.044],[-55.0225,52.131],[-55.134,52.186],[-55.258,52.207],[-55.2475,52.265],[-55.294,52.367],[-55.2995,52.475],[-55.373,52.571],[-55.421,52.713],[-55.471,52.809],[-55.4075,53.043],[-55.407,53.123],[-55.3635,53.219],[-55.3735,53.295],[-55.4195,53.378],[-55.3865,53.464],[-55.391,53.534],[-55.4325,53.608],[-55.5445,53.683],[-55.571,53.749],[-55.6675,53.831],[-55.805,53.875],[-55.911,53.883],[-56.017,53.873],[-56.056,53.91],[-56.298,54.079],[-56.477,54.138],[-56.6135,54.14],[-56.7815,54.092],[-56.7845,54.186],[-56.8355,54.263],[-56.681,54.31],[-56.5795,54.398],[-56.564,54.496],[-56.63,54.586],[-56.5325,54.69],[-56.525,54.74],[-56.5615,54.821],[-56.6875,54.902],[-56.8785,54.933],[-57.0275,54.913],[-57.308,54.835],[-57.485,54.857],[-57.5565,54.978],[-57.6545,55.052],[-57.772,55.095],[-57.9315,55.122],[-58.045,55.174],[-58.15,55.203],[-58.402,55.251],[-58.4905,55.345],[-58.568,55.38],[-58.6745,55.403],[-58.8905,55.423],[-59.017,55.424],[-59.085,55.462],[-59.2175,55.501],[-59.3085,55.509],[-59.478,55.565],[-59.5765,55.652],[-59.6795,55.69],[-59.7755,55.705],[-59.7965,55.775],[-59.8515,55.832],[-59.939,55.879],[-60.017,55.969],[-60.0985,56.009],[-60.282,56.045],[-60.3935,56.139],[-60.3315,56.251],[-60.374,56.353],[-60.4505,56.407],[-60.5275,56.438],[-60.5335,56.496],[-60.58,56.562],[-60.5205,56.728],[-60.527,56.792],[-60.587,56.868],[-60.7685,57.007],[-60.828,57.04],[-60.9785,57.082],[-60.9655,57.15],[-60.9905,57.21],[-61.0915,57.293],[-61.11,57.333],[-61.0485,57.417],[-60.9675,57.482],[-60.9365,57.56],[-60.986,57.668],[-61.0425,57.708],[-61.18,57.766],[-61.279,57.794],[-61.263,57.9],[-61.277,57.964],[-61.3545,58.045],[-61.4335,58.085],[-61.5465,58.114],[-61.7435,58.119],[-61.7095,58.209],[-61.7735,58.315],[-61.9005,58.382],[-62.044,58.409],[-62.1015,58.469],[-62.1895,58.519],[-62.214,58.561],[-62.3245,58.666],[-62.472,58.738],[-62.5555,58.902],[-62.7405,59.032],[-62.747,59.084],[-62.8125,59.162],[-62.9695,59.232],[-63.0215,59.352],[-63.104,59.415],[-63.295,59.525],[-63.424,59.646],[-63.509,59.692],[-63.554,59.811],[-63.687,59.952],[-63.748,59.995],[-63.77,60.077],[-63.81,60.127],[-63.8785,60.281],[-63.9365,60.343],[-64.0225,60.388],[-64.175,60.724],[-64.334,61.2815],[-64.163,61.441],[-64.1235,61.495],[-64.132,61.587],[-64.2765,61.8075],[-64.0545,62.192],[-63.743,62.408],[-63.3925,62.653],[-63.3305,62.7195],[-63.257,62.8675],[-63.2465,62.9205],[-63.455,63.4645],[-63.4725,63.629],[-63.4935,63.6805],[-63.7065,63.971],[-63.821,64.1115],[-63.9625,64.2535],[-62.9225,64.7355],[-62.82,64.807],[-62.52,65.17],[-61.7935,65.493],[-61.6925,65.5595],[-61.6435,65.6295],[-61.4755,65.9395],[-61.0395,66.254],[-60.9985,66.2915],[-60.779,66.563],[-60.7605,66.639],[-60.844,66.769],[-61.313,67.0535],[-61.6905,67.243],[-62.1325,67.388],[-62.2575,67.4165],[-62.785,67.4995],[-63.5445,67.7805],[-64.3085,68.1075],[-64.679,68.2165],[-65.366,68.3585],[-66.093,68.6415],[-66.1,69.2455],[-66.1385,69.329],[-66.5465,69.794],[-66.6425,69.909],[-66.6595,70.0085],[-66.784,70.1265],[-66.894,70.1725],[-67.1175,70.2975],[-67.2545,70.3435],[-67.6735,70.606],[-67.8675,70.6965],[-68.088,70.7645],[-68.501,70.8325],[-68.6935,70.8825],[-68.8715,70.946],[-69.4575,71.0365],[-70.3575,71.2675],[-70.5755,71.3445],[-70.7685,71.4825],[-70.956,71.5795],[-71.2315,71.668],[-71.5885,71.7375],[-71.9165,71.7735],[-72.208,71.8295],[-72.43,71.8565],[-72.9355,71.885],[-73.143,71.923],[-73.695,72.19],[-74.193,72.3645],[-74.447,72.4175],[-74.5095,72.54],[-74.687,72.632],[-75.026,72.6945],[-75.3755,72.913],[-75.4265,73.0155],[-75.568,73.076],[-75.5715,73.125],[-75.665,73.1995],[-75.8795,73.2675],[-76.0445,73.3955],[-76.2305,73.476],[-76.439,73.5135],[-76.505,73.5735],[-76.7535,73.669],[-77.102,73.733],[-77.3255,73.7555],[-77.7345,73.839],[-77.923,73.862],[-78.274,73.872],[-78.945,73.864],[-79.2375,73.8445],[-79.335,73.8565],[-79.452,74.5295],[-78.98,74.6665],[-78.6955,74.769],[-78.5845,74.8285],[-78.5595,74.912],[-78.709,75.196],[-78.655,75.2935],[-78.697,75.35],[-78.068,75.7735],[-78.016,75.841],[-78.0055,76.0245],[-77.6055,76.377],[-77.368,76.475],[-77.0865,76.548],[-76.949,76.6275],[-76.871,76.7995],[-76.9085,76.885],[-77.1165,77.038],[-77.2705,77.347],[-77.0605,77.453],[-75.252,77.8075],[-75.055,77.8565],[-74.726,77.976],[-74.6715,78.0285],[-74.155,78.2395],[-73.8875,78.3835],[-73.7165,78.4765],[-73.5015,78.5555],[-73.11,78.754],[-73.362,79.081],[-73.449,79.1355],[-72.372,79.388],[-71.504,79.5045],[-70.9685,79.5345],[-70.259,79.6475],[-70.0965,79.6885],[-69.8155,79.807],[-68.713,80.183],[-68.647,80.2245],[-68.335,80.416],[-67.1205,80.749],[-66.2685,80.834],[-64.1855,81.2995],[-62.1625,81.8675],[-59.995,82.2175],[-59.6265,82.2995],[-59.646,82.3965],[-59.8905,82.5055],[-60.267,82.5885],[-62.253,82.9475],[-62.806,83.006],[-64.148,83.0885],[-64.54,83.1045],[-66.2235,83.1425],[-69.0405,83.2955],[-69.7645,83.311],[-71.4855,83.3],[-73.953,83.335],[-74.3235,83.335],[-77.1625,83.286],[-77.4615,83.2765],[-80.77,83.1155],[-82.39,82.983],[-86.676,82.621],[-87.103,82.5595],[-87.962,82.356],[-88.778,82.277],[-91.2055,82.125],[-91.7335,82.064],[-92.7695,81.8605],[-95.2835,81.4715],[-95.5205,81.416],[-96.4145,81.081],[-97.2605,80.746],[-97.438,80.5795],[-97.5495,80.514],[-99.7445,80.372],[-100.4285,80.313],[-102.829,80.017],[-106.1455,79.546],[-107.6465,79.3935],[-110.008,79.1295],[-111.3205,78.969],[-112.77,78.789],[-113.1865,78.725],[-113.718,78.6265],[-114.7045,78.513],[-115.02,78.4535],[-115.852,78.2],[-116.259,78.006],[-116.633,77.9035],[-116.8755,77.79],[-117.3265,77.7255],[-117.717,77.696],[-118.5525,77.595],[-119.025,77.568],[-119.643,77.496],[-119.878,77.451],[-120.47,77.284],[-121.9,76.864],[-122.718,76.6095],[-123.3535,76.535],[-123.6085,76.4885],[-123.775,76.4185],[-123.8135,76.3615],[-123.8205,76.223],[-123.9045,76.0795],[-123.897,76.014],[-123.798,75.936],[-123.6,75.8645],[-124.517,75.1865],[-125.3745,74.4895],[-125.55,74.4545],[-125.766,74.363],[-125.7845,74.2485],[-125.694,74.191],[-125.3015,74.019],[-125.267,73.938],[-125.4135,73.76],[-125.415,73.673],[-125.234,73.569],[-125.0205,73.522],[-125.1995,73.379],[-125.2285,73.3155],[-125.3495,73.271],[-125.5325,73.16],[-125.723,72.976],[-125.858,72.9095],[-125.895,72.8305],[-125.7655,72.725],[-125.806,72.668],[-125.931,72.631],[-126.034,72.5725],[-126.1215,72.401],[-126.289,72.316],[-126.3305,72.2455],[-126.437,72.1645],[-126.4455,72.1275],[-126.6025,72.0485],[-127.3685,71.6125],[-128.0355,71.2135],[-128.7405,70.771],[-130.03,70.452],[-130.4955,70.4075],[-130.785,70.4025],[-131.001,70.3685],[-131.1905,70.315],[-131.806,70.1095],[-132.5705,69.9395],[-132.8235,69.911],[-133.452,69.787],[-133.883,69.9255],[-134.0575,69.964],[-134.3345,69.9865],[-134.643,69.965],[-135.132,69.8825],[-135.6485,69.8305],[-135.852,69.793],[-136.258,69.626],[-136.33,69.5875],[-136.588,69.403],[-138.033,69.4815],[-138.3845,69.698],[-138.6575,69.7905],[-138.956,69.8325],[-139.1665,69.838],[-140.1795,69.8055],[-140.3555,69.79],[-140.5285,69.801],[-140.873,69.8405],[-141.0025,69.846],[-141.0005,69.419],[-141.001,68.8115],[-141.001,68.095],[-141.001,67.5985],[-141.001,66.8875],[-141.0015,66.179],[-141.0015,65.494],[-141.0015,64.992],[-141.0015,64.543],[-141.0015,63.9315],[-141.0015,63.5185],[-141.0015,63.022],[-141.0015,62.615],[-141.0015,62.224],[-141.0015,61.597],[-141.002,61.249],[-141.002,60.8185],[-141.002,60.3065],[-140.769,60.26],[-140.5205,60.22],[-140.458,60.308],[-139.981,60.182],[-139.693,60.335],[-139.075,60.3525],[-139.072,60.3195],[-139.199,60.0885],[-139.0535,59.995],[-138.7075,59.9065],[-138.6685,59.8095],[-138.626,59.7685],[-137.6075,59.244],[-137.542,59.1065],[-137.5,58.9855],[-137.526,58.9065],[-137.4515,58.9085],[-137.283,59.0],[-136.9695,59.101],[-136.8285,59.16],[-136.5845,59.166],[-136.469,59.284],[-136.477,59.466],[-136.368,59.449],[-136.305,59.4645],[-136.2385,59.5245],[-136.24,59.5595],[-136.3465,59.6005],[-136.193,59.64],[-135.9525,59.662],[-135.479,59.798],[-135.253,59.6985],[-135.1575,59.6255],[-135.029,59.5635],[-135.0275,59.4745],[-135.101,59.4275],[-134.9895,59.387],[-135.0305,59.3465],[-134.96,59.281],[-134.7005,59.249],[-134.679,59.1925],[-134.5655,59.131],[-134.4835,59.1315],[-134.382,59.039],[-134.407,58.979],[-134.3135,58.962],[-134.3365,58.9235],[-134.258,58.861],[-133.8405,58.7295],[-133.707,58.6125],[-133.558,58.523],[-133.3775,58.4305],[-133.4615,58.3875],[-133.3455,58.2765],[-133.1725,58.1535],[-133.07,58.0005],[-132.8695,57.8395],[-132.751,57.696],[-132.661,57.6165],[-132.5535,57.4965],[-132.37,57.3495],[-132.248,57.2115],[-132.369,57.0915],[-132.045,57.045],[-132.1235,56.8735],[-131.873,56.806],[-131.901,56.7535],[-131.8605,56.703],[-131.8355,56.599],[-131.581,56.612],[-131.472,56.5525],[-131.1735,56.4495],[-131.0875,56.406],[-130.782,56.367],[-130.6235,56.267],[-130.468,56.243],[-130.426,56.1415],[-130.246,56.0965],[-130.104,56.123],[-130.0035,56.008],[-130.0175,55.912],[-130.0835,55.8215],[-130.1525,55.7665],[-130.1115,55.683],[-130.128,55.581],[-130.09,55.498],[-130.041,55.4515],[-130.021,55.338],[-129.974,55.282],[-130.103,55.1925],[-130.1875,55.063],[-130.2735,54.974],[-130.346,54.9175],[-130.568,54.792],[-130.659,54.763],[-130.6155,54.7075],[-131.435,54.6895]]],[[[-133.527,53.937],[-133.509,53.867],[-133.4565,53.803],[-133.42,53.699],[-133.3355,53.602],[-133.338,53.506],[-133.2915,53.438],[-133.2335,53.388],[-133.0955,53.309],[-133.0765,53.247],[-132.9975,53.145],[-132.9175,53.091],[-132.894,53.027],[-132.8415,52.965],[-132.736,52.888],[-132.667,52.855],[-132.4735,52.669],[-132.2025,52.499],[-132.161,52.461],[-132.012,52.369],[-131.91,52.324],[-131.8685,52.254],[-131.659,52.08],[-131.477,51.962],[-131.287,51.815],[-131.1645,51.761],[-131.0845,51.744],[-130.955,51.744],[-130.857,51.767],[-130.7615,51.819],[-130.723,51.859],[-130.629,52.012],[-130.61,52.094],[-130.6815,52.266],[-130.714,52.312],[-130.907,52.457],[-130.9455,52.536],[-131.0505,52.654],[-131.1515,52.804],[-131.1645,52.92],[-131.195,52.97],[-131.277,53.038],[-131.329,53.167],[-131.438,53.265],[-131.484,53.34],[-131.576,53.412],[-131.5965,53.518],[-131.588,53.585],[-131.5445,53.65],[-131.5295,53.741],[-131.457,53.815],[-131.37,53.933],[-131.31,54.143],[-131.331,54.247],[-131.368,54.287],[-131.476,54.345],[-131.572,54.367],[-131.708,54.369],[-131.861,54.33],[-131.9695,54.252],[-132.02,54.238],[-132.085,54.268],[-132.2445,54.308],[-132.428,54.302],[-132.5545,54.342],[-132.6755,54.35],[-132.7905,54.415],[-132.8565,54.436],[-132.9975,54.455],[-133.12,54.451],[-133.2775,54.405],[-133.375,54.336],[-133.407,54.278],[-133.4295,54.082],[-133.518,53.985],[-133.527,53.937]]],[[[-60.442,43.9835],[-60.425,43.908],[-60.3735,43.845],[-60.2875,43.791],[-60.119,43.7375],[-60.0255,43.7265],[-59.901,43.7255],[-59.7395,43.7485],[-59.6455,43.781],[-59.4395,43.912],[-59.3855,43.971],[-59.364,44.0325],[-59.374,44.106],[-59.416,44.168],[-59.4845,44.2155],[-59.6075,44.2485],[-59.732,44.239],[-59.809,44.2095],[-59.9315,44.137],[-59.9955,44.1355],[-60.0725,44.1655],[-60.218,44.1735],[-60.3135,44.146],[-60.3885,44.0955],[-60.433,44.029],[-60.442,43.9835]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/cf/Flag_of_Canada.svg","name:en":"Canada","wikidata":"Q16","ISO3166-1:alpha2":"CA","ISO3166-1:alpha3":"CAN","ISO3166-1:numeric":"124"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-82.1245,12.1245],[-81.9155,11.9625],[-81.867,11.9485],[-81.77,11.957],[-81.7105,11.9845],[-81.6395,12.0625],[-81.6255,12.127],[-81.638,12.198],[-81.43,12.1995],[-81.3595,12.2235],[-81.295,12.279],[-81.261,12.334],[-81.2455,12.4025],[-81.252,12.4655],[-81.2985,12.5665],[-81.366,12.642],[-81.429,12.672],[-81.498,12.6805],[-81.5345,12.7455],[-81.594,12.798],[-81.6665,12.8265],[-81.743,12.827],[-81.812,12.8015],[-81.869,12.752],[-81.9325,12.643],[-81.9485,12.558],[-81.942,12.4585],[-81.9215,12.407],[-82.0335,12.3425],[-82.096,12.252],[-82.1245,12.1245]]],[[[-81.8155,3.977],[-81.7985,3.9105],[-81.7335,3.831],[-81.684,3.8045],[-81.616,3.7925],[-81.5475,3.8045],[-81.462,3.86],[-81.418,3.9275],[-81.4025,4.005],[-81.4135,4.074],[-81.453,4.145],[-81.54,4.204],[-81.633,4.214],[-81.696,4.1945],[-81.749,4.156],[-81.8095,4.046],[-81.8155,3.977]]],[[[-81.624,13.376],[-81.618,13.278],[-81.573,13.181],[-81.518,13.1325],[-81.4465,13.105],[-81.3655,13.103],[-81.286,13.1245],[-81.2125,13.1735],[-81.171,13.223],[-81.142,13.286],[-81.1295,13.386],[-81.103,13.493],[-81.119,13.5955],[-81.1585,13.654],[-81.216,13.6955],[-81.3235,13.728],[-81.3915,13.723],[-81.485,13.675],[-81.5315,13.617],[-81.5515,13.5585],[-81.5995,13.483],[-81.624,13.376]]],[[[-81.368,14.307],[-81.3535,14.209],[-81.3545,14.1405],[-81.3225,14.042],[-81.282,13.9875],[-81.2205,13.946],[-81.13,13.9255],[-81.059,13.9345],[-80.9865,13.9735],[-80.928,14.052],[-80.9115,14.126],[-80.917,14.1875],[-80.8735,14.3035],[-80.876,14.483],[-80.889,14.547],[-80.9255,14.6125],[-80.976,14.6565],[-81.038,14.682],[-81.109,14.687],[-81.1785,14.667],[-81.2375,14.625],[-81.29,14.5505],[-81.3245,14.483],[-81.353,14.3925],[-81.368,14.307]]],[[[-80.504,14.2915],[-80.495,14.2205],[-80.463,14.1525],[-80.423,14.111],[-80.304,14.064],[-80.223,14.0685],[-80.135,14.1175],[-80.0745,14.2055],[-80.063,14.276],[-80.022,14.2985],[-79.966,14.375],[-79.9495,14.4475],[-79.966,14.5445],[-80.0215,14.619],[-80.0825,14.6555],[-80.163,14.6725],[-80.255,14.6575],[-80.321,14.6175],[-80.3655,14.56],[-80.445,14.4815],[-80.504,14.2915]]],[[[-80.3,13.5685],[-80.287,13.499],[-80.233,13.381],[-80.1755,13.3205],[-80.1105,13.289],[-80.011,13.2825],[-79.9195,13.3225],[-79.8665,13.38],[-79.845,13.4225],[-79.83,13.5015],[-79.841,13.577],[-79.871,13.6375],[-79.9425,13.7165],[-80.019,13.761],[-80.1115,13.7715],[-80.1805,13.753],[-80.239,13.713],[-80.2885,13.635],[-80.3,13.5685]]],[[[-79.9675,15.835],[-79.9595,15.7725],[-79.933,15.7145],[-79.891,15.6665],[-79.8365,15.6315],[-79.774,15.6125],[-79.7085,15.6115],[-79.6455,15.6285],[-79.5895,15.6615],[-79.546,15.7085],[-79.5175,15.7655],[-79.5075,15.828],[-79.5155,15.8905],[-79.542,15.948],[-79.584,15.9965],[-79.6385,16.0315],[-79.701,16.05],[-79.7665,16.051],[-79.83,16.0345],[-79.8855,16.001],[-79.929,15.954],[-79.9575,15.8975],[-79.9675,15.835]]],[[[-71.147,12.5235],[-71.2105,12.5575],[-71.47,12.637],[-71.663,12.6655],[-71.7315,12.653],[-71.8155,12.62],[-72.2235,12.427],[-74.289,11.525],[-74.895,11.31],[-74.975,11.2735],[-75.3785,10.968],[-75.504,10.8805],[-75.6585,10.711],[-75.695,10.655],[-75.9665,10.2885],[-75.9845,10.2585],[-76.345,9.5365],[-77.272,8.976],[-77.3245,8.8105],[-77.3705,8.661],[-77.448,8.5465],[-77.4475,8.4725],[-77.412,8.475],[-77.3555,8.387],[-77.3495,8.296],[-77.247,8.1755],[-77.218,8.085],[-77.2135,8.0095],[-77.164,7.9705],[-77.1795,7.917],[-77.2535,7.92],[-77.3125,7.8975],[-77.364,7.825],[-77.3365,7.78],[-77.391,7.76],[-77.401,7.722],[-77.461,7.6565],[-77.491,7.567],[-77.519,7.5745],[-77.548,7.522],[-77.5835,7.5135],[-77.63,7.5945],[-77.645,7.6515],[-77.692,7.693],[-77.742,7.6795],[-77.7485,7.6165],[-77.7015,7.507],[-77.7745,7.46],[-77.818,7.4725],[-77.889,7.2285],[-78.0365,7.051],[-77.8785,6.711],[-77.6965,6.168],[-77.7485,5.4845],[-77.73,4.257],[-78.343,3.1005],[-78.3915,3.045],[-78.6045,2.698],[-78.621,2.6635],[-78.6765,2.6245],[-78.763,2.525],[-78.8045,2.452],[-78.8535,2.292],[-79.2115,1.73],[-79.232,1.687],[-79.2375,1.574],[-79.2075,1.5075],[-79.174,1.4695],[-78.8685,1.4695],[-78.7995,1.4245],[-78.7795,1.392],[-78.71,1.359],[-78.615,1.246],[-78.5525,1.2505],[-78.4885,1.186],[-78.437,1.1905],[-78.417,1.1605],[-78.3115,1.191],[-78.2765,1.109],[-78.282,1.056],[-78.202,0.997],[-78.1855,0.938],[-78.1245,0.9365],[-78.0675,0.8955],[-77.9845,0.8535],[-77.936,0.8165],[-77.8685,0.802],[-77.8195,0.8085],[-77.7745,0.8415],[-77.713,0.8505],[-77.6635,0.812],[-77.698,0.7415],[-77.548,0.649],[-77.4915,0.662],[-77.481,0.6045],[-77.522,0.5195],[-77.521,0.4165],[-77.421,0.436],[-77.412,0.409],[-77.3265,0.3885],[-77.302,0.3675],[-77.2145,0.363],[-77.178,0.3905],[-77.1135,0.3725],[-77.078,0.2855],[-77.011,0.294],[-76.985,0.266],[-76.9445,0.269],[-76.905,0.2455],[-76.8475,0.239],[-76.7975,0.2645],[-76.769,0.243],[-76.733,0.2885],[-76.6255,0.262],[-76.5825,0.2295],[-76.533,0.2585],[-76.504,0.2365],[-76.411,0.2425],[-76.412,0.38],[-76.3345,0.387],[-76.314,0.434],[-76.2615,0.427],[-76.1785,0.372],[-76.129,0.368],[-76.1205,0.324],[-76.0565,0.333],[-76.009,0.3005],[-75.952,0.2015],[-75.8615,0.1345],[-75.8145,0.075],[-75.7525,0.037],[-75.667,0.0455],[-75.469,-0.05],[-75.427,-0.087],[-75.3665,-0.091],[-75.258,-0.116],[-75.2095,-0.046],[-75.162,-0.0445],[-75.1315,-0.0705],[-75.0775,-0.082],[-75.012,-0.1485],[-74.9725,-0.155],[-74.931,-0.2215],[-74.8835,-0.2435],[-74.845,-0.231],[-74.7935,-0.189],[-74.7615,-0.2235],[-74.729,-0.2985],[-74.732,-0.334],[-74.676,-0.3655],[-74.6115,-0.351],[-74.544,-0.419],[-74.537,-0.4585],[-74.487,-0.4885],[-74.4195,-0.504],[-74.4245,-0.55],[-74.3865,-0.555],[-74.374,-0.6535],[-74.338,-0.6775],[-74.37,-0.737],[-74.318,-0.778],[-74.2655,-0.8415],[-74.2795,-0.899],[-74.262,-0.941],[-74.2725,-0.979],[-74.2255,-1.0085],[-74.1725,-1.0055],[-74.126,-1.0325],[-74.027,-1.0615],[-74.006,-1.099],[-73.958,-1.131],[-73.904,-1.1245],[-73.8955,-1.1695],[-73.853,-1.2335],[-73.674,-1.2405],[-73.6195,-1.264],[-73.611,-1.3185],[-73.5815,-1.346],[-73.531,-1.4375],[-73.491,-1.4835],[-73.4905,-1.571],[-73.463,-1.589],[-73.5165,-1.64],[-73.5385,-1.7005],[-73.466,-1.735],[-73.4325,-1.7875],[-73.3775,-1.776],[-73.3345,-1.802],[-73.3,-1.7775],[-73.252,-1.7805],[-73.212,-1.752],[-73.1945,-1.7885],[-73.1465,-1.8055],[-73.1505,-1.863],[-73.11,-1.877],[-73.1115,-1.95],[-73.1295,-1.9945],[-73.092,-2.0415],[-73.1135,-2.072],[-73.0705,-2.1055],[-73.1165,-2.1305],[-73.1255,-2.173],[-73.173,-2.195],[-73.155,-2.2585],[-73.096,-2.324],[-73.0435,-2.3575],[-73.003,-2.3605],[-72.9315,-2.418],[-72.8575,-2.4395],[-72.777,-2.3855],[-72.735,-2.424],[-72.6865,-2.4205],[-72.6065,-2.3625],[-72.5765,-2.3935],[-72.38,-2.453],[-72.3685,-2.491],[-72.2985,-2.4745],[-72.2595,-2.432],[-72.227,-2.447],[-72.129,-2.4095],[-72.06,-2.343],[-72.0055,-2.3705],[-71.9695,-2.3675],[-71.927,-2.3085],[-71.872,-2.2965],[-71.829,-2.1875],[-71.718,-2.1665],[-71.713,-2.23],[-71.6375,-2.202],[-71.599,-2.239],[-71.4675,-2.272],[-71.392,-2.355],[-71.3535,-2.3795],[-71.266,-2.377],[-71.231,-2.336],[-71.1935,-2.3825],[-71.1715,-2.3745],[-71.1435,-2.297],[-71.046,-2.265],[-70.978,-2.2125],[-70.9415,-2.2555],[-70.898,-2.224],[-70.8635,-2.226],[-70.843,-2.2855],[-70.768,-2.298],[-70.7615,-2.3205],[-70.6615,-2.363],[-70.674,-2.4105],[-70.626,-2.4075],[-70.6425,-2.4585],[-70.601,-2.4845],[-70.472,-2.454],[-70.4445,-2.512],[-70.4115,-2.526],[-70.354,-2.49],[-70.3335,-2.578],[-70.2675,-2.5455],[-70.229,-2.5815],[-70.219,-2.6455],[-70.1565,-2.647],[-70.1605,-2.691],[-70.1145,-2.697],[-70.0675,-2.6755],[-70.061,-2.7595],[-70.0805,-2.795],[-70.3355,-3.1975],[-70.713,-3.7955],[-70.6505,-3.8305],[-70.5605,-3.827],[-70.5235,-3.88],[-70.4535,-3.863],[-70.415,-3.828],[-70.3475,-3.8005],[-70.284,-3.824],[-70.2475,-3.8965],[-70.1805,-3.9305],[-70.161,-3.9875],[-70.1265,-4.035],[-70.062,-4.0825],[-70.0405,-4.125],[-69.9695,-4.1955],[-69.948,-4.2315],[-69.9335,-4.2195],[-69.836,-3.686],[-69.714,-3.0025],[-69.613,-2.44],[-69.535,-2.003],[-69.4505,-1.53],[-69.4565,-1.491],[-69.4325,-1.454],[-69.436,-1.402],[-69.3975,-1.365],[-69.407,-1.29],[-69.43,-1.235],[-69.397,-1.1145],[-69.4425,-1.034],[-69.4725,-0.9575],[-69.5295,-0.9175],[-69.5265,-0.8675],[-69.573,-0.8385],[-69.574,-0.81],[-69.622,-0.733],[-69.563,-0.6405],[-69.6015,-0.5965],[-69.591,-0.561],[-69.6085,-0.5045],[-69.6675,-0.4655],[-69.736,-0.4375],[-69.7735,-0.3915],[-69.8435,-0.3385],[-69.888,-0.3415],[-70.043,-0.19],[-70.043,0.069],[-70.047,0.26],[-70.047,0.563],[-69.935,0.553],[-69.9155,0.5795],[-69.8095,0.5765],[-69.6875,0.664],[-69.6335,0.6365],[-69.483,0.733],[-69.415,0.688],[-69.349,0.618],[-69.298,0.649],[-69.2705,0.602],[-69.213,0.61],[-69.1945,0.65],[-69.118,0.6555],[-69.19,0.7405],[-69.149,0.7675],[-69.1815,0.9025],[-69.207,0.965],[-69.1975,0.998],[-69.2585,1.054],[-69.321,1.0885],[-69.3825,1.0735],[-69.4875,1.079],[-69.5735,1.073],[-69.61,1.0985],[-69.703,1.075],[-69.7015,1.1175],[-69.746,1.1135],[-69.816,1.063],[-69.8425,1.0725],[-69.843,1.715],[-69.809,1.695],[-69.7445,1.7295],[-69.655,1.7175],[-69.534,1.7765],[-69.4825,1.751],[-69.391,1.7295],[-68.735,1.731],[-68.1865,1.73],[-68.178,1.7675],[-68.2445,1.7835],[-68.2375,1.8215],[-68.2855,1.836],[-68.245,1.9195],[-68.1845,1.986],[-68.1495,1.9825],[-68.096,1.903],[-68.0335,1.891],[-67.9885,1.849],[-67.9435,1.843],[-67.9015,1.8115],[-67.889,1.8645],[-67.8365,1.9025],[-67.835,1.947],[-67.762,2.014],[-67.729,2.029],[-67.6215,2.0275],[-67.551,2.0475],[-67.497,2.161],[-67.449,2.1695],[-67.4405,2.2155],[-67.3735,2.217],[-67.351,2.16],[-67.358,2.0965],[-67.3255,2.0545],[-67.351,2.012],[-67.346,1.974],[-67.3085,1.9085],[-67.2285,1.8415],[-67.154,1.8315],[-67.146,1.762],[-67.16,1.735],[-67.155,1.665],[-67.1275,1.64],[-67.128,1.543],[-67.0855,1.4635],[-67.083,1.373],[-67.134,1.344],[-67.135,1.3],[-67.0995,1.2585],[-67.087,1.167],[-66.851,1.229],[-66.881,1.2905],[-66.8555,1.3215],[-66.854,1.3675],[-66.913,1.4335],[-66.9075,1.4875],[-66.959,1.625],[-67.033,1.7865],[-67.0355,1.8455],[-67.0655,1.9205],[-67.123,1.977],[-67.097,2.0515],[-67.1145,2.1335],[-67.1615,2.133],[-67.2205,2.2505],[-67.2135,2.3095],[-67.188,2.3505],[-67.216,2.3925],[-67.2975,2.4425],[-67.352,2.5295],[-67.3935,2.5745],[-67.4705,2.624],[-67.5195,2.673],[-67.5685,2.666],[-67.563,2.723],[-67.584,2.7735],[-67.6215,2.814],[-67.6585,2.7965],[-67.735,2.817],[-67.7505,2.8375],[-67.824,2.826],[-67.864,2.7895],[-67.8645,2.866],[-67.435,3.2485],[-67.3825,3.2455],[-67.3815,3.2925],[-67.3345,3.3355],[-67.312,3.391],[-67.339,3.451],[-67.4015,3.48],[-67.412,3.539],[-67.444,3.585],[-67.439,3.6325],[-67.483,3.712],[-67.5015,3.7675],[-67.61,3.754],[-67.644,3.813],[-67.6445,3.8525],[-67.69,3.9365],[-67.7035,4.0395],[-67.742,4.139],[-67.7725,4.1655],[-67.797,4.2295],[-67.8055,4.315],[-67.773,4.419],[-67.798,4.437],[-67.8035,4.4895],[-67.857,4.5245],[-67.839,4.562],[-67.845,4.645],[-67.818,4.707],[-67.8235,4.84],[-67.8375,4.947],[-67.806,5.055],[-67.8545,5.138],[-67.8265,5.1915],[-67.8235,5.247],[-67.8495,5.304],[-67.797,5.3605],[-67.683,5.427],[-67.625,5.476],[-67.599,5.544],[-67.619,5.564],[-67.639,5.653],[-67.631,5.7325],[-67.597,5.828],[-67.527,5.9085],[-67.412,5.9885],[-67.4275,6.038],[-67.456,6.0585],[-67.4945,6.131],[-67.457,6.1935],[-67.535,6.26],[-67.584,6.262],[-67.6255,6.2915],[-67.7215,6.3005],[-67.769,6.294],[-67.811,6.324],[-67.845,6.3125],[-67.9245,6.2405],[-67.9775,6.2085],[-68.0415,6.198],[-68.101,6.232],[-68.2545,6.1975],[-68.2975,6.1655],[-68.3735,6.186],[-68.446,6.1875],[-68.5135,6.176],[-68.5335,6.1515],[-68.597,6.1635],[-68.6445,6.129],[-68.7065,6.152],[-68.7925,6.1475],[-68.8345,6.18],[-68.886,6.168],[-68.974,6.1965],[-69.017,6.194],[-69.0435,6.2215],[-69.123,6.1915],[-69.1775,6.1475],[-69.2375,6.08],[-69.312,6.0905],[-69.3355,6.1375],[-69.4305,6.108],[-69.763,6.528],[-70.118,6.9795],[-70.2245,6.9745],[-70.2935,6.934],[-70.344,6.9445],[-70.3715,6.98],[-70.4305,7.007],[-70.503,7.006],[-70.549,7.0745],[-70.6085,7.064],[-70.697,7.094],[-70.8395,7.079],[-70.927,7.0465],[-70.995,6.983],[-71.1115,6.99],[-71.131,7.033],[-71.1915,7.019],[-71.2675,7.0325],[-71.379,7.018],[-71.4295,7.038],[-71.459,7.0135],[-71.4915,7.031],[-71.593,7.031],[-71.633,7.056],[-71.656,7.0365],[-71.7145,7.0345],[-71.746,7.0655],[-71.811,7.0635],[-71.835,7.0255],[-71.941,7.0095],[-72.0475,7.039],[-72.174,7.252],[-72.156,7.33],[-72.1965,7.382],[-72.4395,7.4035],[-72.442,7.446],[-72.4765,7.4895],[-72.4525,7.563],[-72.476,7.629],[-72.47,7.789],[-72.4445,7.858],[-72.487,7.945],[-72.425,7.99],[-72.411,8.0345],[-72.3505,8.004],[-72.351,8.0805],[-72.371,8.094],[-72.367,8.185],[-72.392,8.256],[-72.382,8.3045],[-72.393,8.3585],[-72.4275,8.3815],[-72.6105,8.5785],[-72.655,8.6145],[-72.698,8.8065],[-72.719,8.9345],[-72.767,9.1065],[-72.881,9.133],[-72.8865,9.1035],[-72.9475,9.0915],[-72.973,9.1365],[-72.962,9.1845],[-73.011,9.2915],[-73.0735,9.243],[-73.1315,9.2285],[-73.1775,9.1765],[-73.248,9.1595],[-73.3475,9.1735],[-73.2655,9.3265],[-73.241,9.403],[-73.19,9.4445],[-73.2035,9.4675],[-73.128,9.5545],[-73.0855,9.556],[-73.055,9.6785],[-73.0055,9.741],[-72.994,9.783],[-72.9455,9.8375],[-72.971,9.855],[-72.999,9.924],[-72.953,10.0265],[-72.9545,10.065],[-72.9115,10.1125],[-72.9215,10.138],[-72.8975,10.2175],[-72.9065,10.353],[-72.8935,10.4465],[-72.8455,10.486],[-72.8205,10.57],[-72.735,10.6515],[-72.689,10.757],[-72.674,10.8215],[-72.64,10.8845],[-72.575,10.915],[-72.485,11.0615],[-72.4845,11.099],[-72.3495,11.1515],[-72.2455,11.145],[-71.9715,11.6465],[-71.7825,11.687],[-71.644,11.734],[-71.3835,11.8105],[-71.345,11.848],[-70.927,11.961],[-70.927,12.1575],[-71.0135,12.2345],[-71.0615,12.315],[-71.08,12.402],[-71.147,12.5235]]],[[[-78.9045,15.828],[-78.884,15.7435],[-78.8315,15.673],[-78.755,15.627],[-78.666,15.613],[-78.621,15.6185],[-78.539,15.6545],[-78.4775,15.7175],[-78.4455,15.799],[-78.4485,15.8855],[-78.4855,15.9645],[-78.5515,16.0235],[-78.6355,16.054],[-78.7255,16.0515],[-78.808,16.016],[-78.8695,15.9525],[-78.9015,15.8715],[-78.9045,15.828]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/21/Flag_of_Colombia.svg","name:en":"Colombia","wikidata":"Q739","ISO3166-1:alpha2":"CO","ISO3166-1:alpha3":"COL","ISO3166-1:numeric":"170"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-89.356,14.4205],[-89.3895,14.449],[-89.4295,14.415],[-89.574,14.417],[-89.588,14.346],[-89.5605,14.3125],[-89.523,14.2245],[-89.667,14.1855],[-89.714,14.138],[-89.746,14.0635],[-89.776,14.0345],[-89.8165,14.063],[-89.8915,14.0435],[-89.9615,13.9705],[-90.023,13.939],[-90.113,13.824],[-90.118,13.789],[-90.179,13.6345],[-89.9515,13.5375],[-89.9085,13.4595],[-89.8445,13.399],[-89.7215,13.402],[-89.5525,13.3625],[-89.333,13.343],[-88.8005,13.1085],[-88.4025,13.0285],[-88.2535,13.041],[-88.149,13.041],[-87.94,12.9935],[-87.9015,12.976],[-87.811,13.055],[-87.72,13.109],[-87.663,13.1275],[-87.635,13.1975],[-87.678,13.235],[-87.739,13.3245],[-87.7435,13.349],[-87.799,13.3815],[-87.7905,13.414],[-87.723,13.4435],[-87.722,13.506],[-87.792,13.5265],[-87.7555,13.603],[-87.7545,13.691],[-87.7085,13.804],[-87.7465,13.854],[-87.831,13.9185],[-87.854,13.8945],[-87.9625,13.8975],[-88.0125,13.8715],[-88.063,13.9435],[-88.0725,13.9925],[-88.16,13.9835],[-88.23,14.0],[-88.227,13.938],[-88.2655,13.937],[-88.3175,13.89],[-88.417,13.8845],[-88.452,13.859],[-88.4925,13.8625],[-88.5055,13.9085],[-88.5,13.967],[-88.569,13.978],[-88.6075,14.0055],[-88.709,14.0465],[-88.7375,14.131],[-88.799,14.1645],[-88.831,14.108],[-88.8665,14.1785],[-88.9025,14.2055],[-88.97,14.212],[-88.9925,14.259],[-89.026,14.2725],[-89.0355,14.332],[-89.083,14.3415],[-89.109,14.4085],[-89.1685,14.366],[-89.216,14.389],[-89.356,14.4205]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/34/Flag_of_El_Salvador.svg","name:en":"El Salvador","wikidata":"Q792","ISO3166-1:alpha2":"SV","ISO3166-1:alpha3":"SLV","ISO3166-1:numeric":"222"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-89.152,17.8155],[-89.151,17.4485],[-89.15,17.0575],[-89.1735,16.7115],[-89.2085,16.1795],[-89.2275,15.896],[-89.103,15.9125],[-89.022,15.9115],[-88.9575,15.8885],[-88.7705,15.9585],[-88.686,16.051],[-88.4985,16.07],[-88.2465,16.3725],[-88.145,16.417],[-88.147,16.622],[-88.11,16.779],[-88.019,16.913],[-87.9325,17.057],[-87.6315,17.008],[-87.427,17.063],[-87.359,17.129],[-87.3205,17.237],[-87.31,17.5055],[-87.453,17.632],[-87.7055,17.739],[-87.9605,17.753],[-87.9105,17.868],[-87.84,17.9545],[-87.809,18.031],[-87.744,18.1165],[-87.868,18.198],[-87.91,18.1505],[-87.9325,18.1675],[-88.031,18.1675],[-88.0305,18.415],[-88.2495,18.4155],[-88.25,18.473],[-88.3275,18.4905],[-88.4435,18.48],[-88.478,18.4935],[-88.517,18.464],[-88.548,18.355],[-88.606,18.2915],[-88.6155,18.2235],[-88.6735,18.194],[-88.7185,18.108],[-88.7165,18.061],[-88.767,18.016],[-88.8165,17.9495],[-88.8575,17.9245],[-88.9205,17.916],[-88.9455,17.9525],[-88.993,17.9595],[-89.0375,18.005],[-89.1325,17.9685],[-89.152,17.9395],[-89.152,17.8155]]],[[[-87.891,16.7245],[-87.8665,16.7075],[-87.7675,16.7545],[-87.736,16.803],[-87.6965,16.9165],[-87.7965,16.9235],[-87.8455,16.8395],[-87.891,16.7245]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/e7/Flag_of_Belize.svg","name:en":"Belize","wikidata":"Q242","ISO3166-1:alpha2":"BZ","ISO3166-1:alpha3":"BLZ","ISO3166-1:numeric":"084"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-72.0315,19.9245],[-72.125,19.936],[-72.251,19.9335],[-72.456,20.071],[-72.6845,20.1955],[-72.8425,20.218],[-73.229,20.0675],[-73.497,19.966],[-73.6715,19.8755],[-73.728,19.7005],[-73.664,19.547],[-73.4805,19.415],[-73.221,19.4285],[-73.117,19.444],[-72.944,19.3715],[-72.9595,19.19],[-73.021,19.071],[-73.283,19.106],[-73.3545,19.0805],[-73.446,18.9725],[-73.446,18.869],[-73.3435,18.7635],[-73.4335,18.693],[-73.775,18.7585],[-74.135,18.8195],[-74.446,18.765],[-74.5505,18.705],[-74.618,18.6425],[-74.6575,18.4225],[-74.589,18.247],[-74.476,18.182],[-74.381,18.1365],[-74.2025,18.175],[-74.0145,17.9535],[-73.928,17.91],[-73.772,17.91],[-73.6905,17.966],[-73.618,17.9955],[-73.5335,17.9985],[-73.476,18.0735],[-73.494,18.163],[-73.352,18.117],[-73.1675,18.102],[-72.8885,18.041],[-72.2635,18.09],[-72.098,18.107],[-71.977,18.091],[-71.8295,17.954],[-71.744,18.053],[-71.745,18.137],[-71.7835,18.17],[-71.768,18.2205],[-71.696,18.3405],[-71.83,18.399],[-71.844,18.429],[-71.9045,18.457],[-71.881,18.504],[-71.9815,18.612],[-71.9665,18.6565],[-71.8445,18.629],[-71.807,18.6355],[-71.804,18.6855],[-71.738,18.722],[-71.722,18.804],[-71.725,18.8755],[-71.768,18.907],[-71.783,18.9485],[-71.8355,18.97],[-71.771,19.0315],[-71.723,19.0535],[-71.6525,19.139],[-71.631,19.1835],[-71.639,19.2315],[-71.691,19.239],[-71.7405,19.277],[-71.778,19.3345],[-71.712,19.3715],[-71.6795,19.434],[-71.7135,19.552],[-71.743,19.5845],[-71.7455,19.634],[-71.7735,19.7315],[-71.804,19.733],[-71.914,19.7965],[-72.0315,19.9245]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/5/56/Flag_of_Haiti.svg","name:en":"Haiti","wikidata":"Q790","ISO3166-1:alpha2":"HT","ISO3166-1:alpha3":"HTI","ISO3166-1:numeric":"332"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-57.015,6.225],[-57.045,6.271],[-57.1465,6.374],[-57.2805,6.469],[-57.349,6.502],[-57.432,6.523],[-57.4775,6.579],[-57.684,6.776],[-57.8825,6.946],[-57.94,6.982],[-58.0545,7.02],[-58.1415,7.027],[-58.2685,7.144],[-58.2795,7.224],[-58.2745,7.32],[-58.2835,7.376],[-58.3135,7.452],[-58.5305,7.737],[-58.6375,7.804],[-58.7135,7.896],[-58.7915,7.962],[-58.866,8.051],[-58.9895,8.179],[-59.0555,8.229],[-59.3805,8.423],[-59.5,8.483],[-59.6515,8.574],[-59.7705,8.604],[-59.955,8.5405],[-59.981,8.5185],[-59.815,8.3115],[-59.8015,8.275],[-59.8485,8.225],[-59.946,8.2135],[-59.995,8.154],[-60.006,8.06],[-60.066,8.024],[-60.1005,8.0355],[-60.1835,7.9645],[-60.219,7.9575],[-60.2455,7.913],[-60.333,7.8415],[-60.405,7.821],[-60.4375,7.836],[-60.4995,7.834],[-60.563,7.761],[-60.57,7.7035],[-60.618,7.6425],[-60.6605,7.5645],[-60.7045,7.56],[-60.7055,7.4955],[-60.614,7.395],[-60.588,7.3145],[-60.6155,7.2985],[-60.638,7.246],[-60.6245,7.211],[-60.5585,7.1545],[-60.4495,7.205],[-60.4205,7.1765],[-60.367,7.185],[-60.2935,7.1335],[-60.2875,7.0875],[-60.3205,7.0215],[-60.3665,6.998],[-60.4015,6.948],[-60.461,6.9245],[-60.5005,6.882],[-60.5555,6.856],[-60.6635,6.8295],[-60.7185,6.7555],[-60.8095,6.794],[-60.846,6.7805],[-60.895,6.811],[-60.9235,6.807],[-60.9095,6.747],[-60.9555,6.7155],[-61.013,6.7285],[-61.0855,6.7045],[-61.1385,6.7215],[-61.183,6.671],[-61.2195,6.566],[-61.1725,6.5195],[-61.1825,6.472],[-61.1525,6.4575],[-61.148,6.413],[-61.1755,6.327],[-61.126,6.262],[-61.142,6.215],[-61.1795,6.187],[-61.2095,6.112],[-61.2745,6.105],[-61.312,6.052],[-61.331,5.999],[-61.392,5.944],[-61.0845,5.599],[-60.749,5.2225],[-60.7375,5.202],[-60.682,5.232],[-60.614,5.219],[-60.569,5.196],[-60.527,5.208],[-60.434,5.182],[-60.384,5.222],[-60.313,5.211],[-60.2195,5.269],[-60.183,5.2275],[-60.132,5.225],[-60.106,5.194],[-60.094,5.14],[-59.9825,5.0825],[-59.9795,5.0155],[-60.0135,4.843],[-60.026,4.7055],[-60.0755,4.6555],[-60.07,4.618],[-60.1235,4.605],[-60.161,4.5175],[-60.071,4.4935],[-60.0015,4.495],[-59.971,4.5095],[-59.911,4.4765],[-59.805,4.466],[-59.735,4.4235],[-59.6755,4.3465],[-59.732,4.286],[-59.7305,4.181],[-59.685,4.147],[-59.6195,4.133],[-59.6425,4.0675],[-59.602,4.03],[-59.5865,3.9755],[-59.5265,3.969],[-59.523,3.932],[-59.593,3.8855],[-59.5775,3.832],[-59.5955,3.7955],[-59.6655,3.7815],[-59.6685,3.703],[-59.7425,3.66],[-59.7655,3.626],[-59.8505,3.6045],[-59.859,3.571],[-59.802,3.493],[-59.84,3.4435],[-59.805,3.357],[-59.8815,3.259],[-59.8745,3.229],[-59.9125,3.195],[-59.9015,3.1605],[-59.9555,3.082],[-59.9475,3.003],[-59.984,2.9295],[-59.9935,2.7825],[-59.991,2.683],[-59.978,2.642],[-59.93,2.5655],[-59.8935,2.4645],[-59.905,2.3755],[-59.7805,2.2855],[-59.741,2.293],[-59.72,2.237],[-59.7405,2.1795],[-59.723,2.0985],[-59.7425,2.068],[-59.73,2.0305],[-59.745,1.929],[-59.746,1.852],[-59.691,1.854],[-59.678,1.807],[-59.69,1.757],[-59.632,1.741],[-59.617,1.716],[-59.566,1.736],[-59.493,1.674],[-59.483,1.627],[-59.447,1.62],[-59.431,1.567],[-59.378,1.521],[-59.339,1.519],[-59.253,1.389],[-59.048,1.323],[-58.918,1.317],[-58.904,1.251],[-58.8545,1.1835],[-58.821,1.171],[-58.7395,1.2],[-58.71,1.2835],[-58.6285,1.2885],[-58.5885,1.27],[-58.496,1.268],[-58.471,1.3195],[-58.47,1.371],[-58.505,1.403],[-58.5085,1.463],[-58.3855,1.47],[-58.3935,1.52],[-58.317,1.5685],[-58.1605,1.56],[-58.1295,1.499],[-58.061,1.5255],[-58.0055,1.517],[-57.9785,1.573],[-57.981,1.6595],[-57.927,1.645],[-57.895,1.6745],[-57.8,1.685],[-57.7925,1.7265],[-57.705,1.731],[-57.6505,1.6825],[-57.6295,1.6985],[-57.553,1.6925],[-57.4975,1.767],[-57.451,1.8065],[-57.432,1.8585],[-57.4335,1.906],[-57.3675,1.9235],[-57.3455,1.9805],[-57.277,1.983],[-57.2555,1.9505],[-57.12,2.01],[-57.071,1.9965],[-57.071,1.9685],[-57.0005,1.9075],[-56.9195,1.9305],[-56.8935,1.8995],[-56.774,1.869],[-56.721,1.926],[-56.651,1.917],[-56.6215,1.946],[-56.58,1.906],[-56.469,1.949],[-56.537,2.0085],[-56.586,2.028],[-56.691,2.026],[-56.7265,2.0865],[-56.821,2.211],[-56.8265,2.2655],[-56.886,2.302],[-56.8855,2.3575],[-56.9345,2.397],[-56.93,2.436],[-56.9895,2.5025],[-56.997,2.5555],[-57.023,2.583],[-57.018,2.6345],[-57.0605,2.6715],[-57.103,2.7345],[-57.089,2.766],[-57.1465,2.7825],[-57.2085,2.8525],[-57.226,2.956],[-57.2045,3.0255],[-57.221,3.076],[-57.2465,3.088],[-57.2905,3.1815],[-57.2945,3.2785],[-57.279,3.3245],[-57.3,3.384],[-57.358,3.358],[-57.4275,3.361],[-57.4735,3.3385],[-57.5205,3.3645],[-57.5975,3.3415],[-57.6465,3.361],[-57.6655,3.401],[-57.637,3.4555],[-57.6565,3.5235],[-57.7065,3.556],[-57.755,3.623],[-57.821,3.653],[-57.8455,3.6895],[-57.849,3.741],[-57.876,3.8095],[-57.957,3.9205],[-58.0435,3.998],[-58.049,4.079],[-58.071,4.1595],[-58.034,4.217],[-57.9535,4.288],[-57.9585,4.4035],[-57.928,4.441],[-57.877,4.574],[-57.8445,4.6335],[-57.844,4.682],[-57.8905,4.772],[-57.9245,4.8165],[-57.844,4.9245],[-57.776,4.921],[-57.746,4.9675],[-57.688,5.0105],[-57.608,4.992],[-57.5495,5.004],[-57.476,4.991],[-57.4105,4.993],[-57.3515,5.0155],[-57.3005,5.1385],[-57.299,5.1735],[-57.251,5.1855],[-57.23,5.1365],[-57.183,5.168],[-57.227,5.262],[-57.2515,5.2765],[-57.2925,5.2405],[-57.2915,5.3215],[-57.327,5.351],[-57.2695,5.4035],[-57.2705,5.464],[-57.1985,5.553],[-57.1735,5.5995],[-57.1605,5.6995],[-57.183,5.74],[-57.1675,5.8095],[-57.135,5.8935],[-57.1435,5.989],[-57.1625,6.0515],[-57.0785,6.0845],[-57.032,6.1695],[-57.015,6.225]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/99/Flag_of_Guyana.svg","name:en":"Guyana","wikidata":"Q734","ISO3166-1:alpha2":"GY","ISO3166-1:alpha3":"GUY","ISO3166-1:numeric":"328"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-62.5535,16.8995],[-62.509,16.806],[-62.4475,16.7575],[-62.14,17.0155],[-62.1785,17.078],[-62.268,17.136],[-62.3305,17.147],[-62.5535,16.8995]]],[[[-61.9615,16.8375],[-61.899,16.815],[-61.7765,16.799],[-61.654,16.818],[-61.56,16.874],[-61.5195,16.911],[-61.463,16.994],[-61.4505,17.102],[-61.5075,17.217],[-61.582,17.296],[-61.625,17.326],[-61.6365,17.366],[-61.5835,17.403],[-61.5255,17.508],[-61.5255,17.646],[-61.5485,17.72],[-61.5805,17.772],[-61.643,17.84],[-61.7565,17.911],[-61.8685,17.928],[-61.935,17.911],[-62.0025,17.871],[-62.066,17.802],[-62.0965,17.704],[-62.0705,17.6],[-62.06,17.522],[-62.0335,17.47],[-61.9815,17.419],[-61.8695,17.371],[-61.979,17.331],[-62.022,17.297],[-62.0955,17.19],[-62.123,17.098],[-62.109,17.0055],[-61.9615,16.8375]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/89/Flag_of_Antigua_and_Barbuda.svg","name:en":"Antigua and Barbuda","wikidata":"Q781","ISO3166-1:alpha2":"AG","ISO3166-1:alpha3":"ATG","ISO3166-1:numeric":"028"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-78.773,22.8685],[-79.3055,23.023],[-79.672,23.131],[-80.2245,23.404],[-80.3035,23.4265],[-80.876,23.4815],[-81.109,23.4665],[-81.6885,23.4135],[-81.9965,23.3855],[-82.238,23.3805],[-82.316,23.373],[-82.4215,23.3455],[-83.2595,23.1915],[-83.6655,23.096],[-83.707,23.082],[-84.109,22.8985],[-84.22,22.8585],[-84.317,22.803],[-84.384,22.743],[-84.5755,22.6525],[-84.6205,22.623],[-84.819,22.448],[-84.9175,22.3325],[-84.9485,22.2435],[-85.1185,22.008],[-85.1505,21.946],[-85.168,21.8665],[-85.1565,21.797],[-85.108,21.715],[-85.018,21.65],[-84.952,21.629],[-84.822,21.611],[-84.555,21.5575],[-84.457,21.5575],[-84.3705,21.577],[-84.0465,21.689],[-83.566,21.469],[-83.168,21.2865],[-83.1095,21.269],[-82.933,21.2405],[-82.82,21.2435],[-82.203,21.273],[-82.17,21.2765],[-81.725,21.3625],[-81.488,21.3955],[-81.0725,21.4275],[-80.623,21.421],[-79.956,21.4115],[-79.7315,21.105],[-79.5995,20.9695],[-79.546,20.9305],[-79.452,20.882],[-79.179,20.7065],[-79.06,20.6245],[-78.82,20.4795],[-78.7785,20.46],[-78.4605,20.3495],[-77.9135,19.7095],[-77.814,19.644],[-77.7025,19.6275],[-77.522,19.6445],[-77.3965,19.646],[-77.31,19.6565],[-77.123,19.69],[-77.016,19.6845],[-76.927,19.6935],[-76.7465,19.736],[-76.346,19.7545],[-76.194,19.7785],[-76.0295,19.7555],[-75.8825,19.761],[-75.641,19.696],[-75.5465,19.6825],[-75.3445,19.6765],[-75.1415,19.6895],[-74.991,19.708],[-74.923,19.727],[-74.2665,19.8675],[-74.147,19.9015],[-74.092,19.941],[-73.9885,20.0425],[-73.943,20.109],[-73.926,20.1575],[-73.9245,20.2575],[-73.9495,20.3325],[-73.99,20.391],[-74.0705,20.4635],[-74.5665,20.7455],[-74.618,20.7865],[-74.809,20.872],[-75.5115,21.2445],[-75.61,21.301],[-75.667,21.322],[-76.28,21.4575],[-77.008,21.8225],[-77.215,22.033],[-77.5715,22.2585],[-77.6295,22.292],[-77.8015,22.42],[-78.1155,22.596],[-78.213,22.6425],[-78.4825,22.7535],[-78.773,22.8685]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/bd/Flag_of_Cuba.svg","name:en":"Cuba","wikidata":"Q241","ISO3166-1:alpha2":"CU","ISO3166-1:alpha3":"CUB","ISO3166-1:numeric":"192"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-10.28,8.485],[-10.2265,8.4805],[-10.12,8.5345],[-10.043,8.489],[-10.038,8.4285],[-9.9895,8.4455],[-9.9235,8.496],[-9.806,8.5015],[-9.7755,8.55],[-9.725,8.5255],[-9.734,8.472],[-9.6635,8.5015],[-9.62,8.453],[-9.6575,8.405],[-9.5675,8.4095],[-9.5525,8.382],[-9.497,8.3735],[-9.507,8.3295],[-9.489,8.2655],[-9.512,8.2415],[-9.494,8.1755],[-9.441,8.084],[-9.4055,8.0495],[-9.4375,7.988],[-9.432,7.8595],[-9.3785,7.809],[-9.349,7.7465],[-9.3745,7.6915],[-9.359,7.613],[-9.3785,7.5885],[-9.4205,7.484],[-9.468,7.43],[-9.3845,7.394],[-9.3605,7.4335],[-9.2955,7.4275],[-9.286,7.377],[-9.2025,7.386],[-9.203,7.3185],[-9.072,7.202],[-9.026,7.2465],[-8.9705,7.253],[-8.93,7.2885],[-8.872,7.257],[-8.8375,7.2765],[-8.8535,7.347],[-8.81,7.4045],[-8.7095,7.5135],[-8.723,7.5575],[-8.7065,7.6445],[-8.6765,7.6955],[-8.554,7.696],[-8.559,7.626],[-8.471,7.557],[-8.407,7.6235],[-8.3545,7.5875],[-8.308,7.612],[-8.2185,7.5385],[-8.1705,7.5845],[-8.185,7.608],[-8.143,7.662],[-8.0955,7.7975],[-8.1245,7.835],[-8.135,7.883],[-8.0685,8.002],[-8.0625,8.0365],[-7.9945,8.047],[-7.95,8.0325],[-7.995,8.124],[-7.9975,8.198],[-8.062,8.157],[-8.1005,8.182],[-8.1735,8.185],[-8.24,8.2345],[-8.2175,8.2965],[-8.215,8.358],[-8.238,8.4155],[-8.189,8.5065],[-8.111,8.4835],[-8.063,8.511],[-7.9755,8.4895],[-7.908,8.4905],[-7.9075,8.4405],[-7.865,8.424],[-7.8285,8.44],[-7.798,8.4855],[-7.742,8.3785],[-7.65,8.378],[-7.6765,8.4415],[-7.659,8.4985],[-7.6955,8.557],[-7.6715,8.618],[-7.692,8.6495],[-7.7325,8.659],[-7.7615,8.731],[-7.809,8.7685],[-7.911,8.7745],[-7.9485,8.7885],[-7.9745,8.831],[-7.9375,8.849],[-7.9395,8.9025],[-7.9165,8.9355],[-7.9345,8.982],[-7.8565,9.0325],[-7.8405,9.066],[-7.7825,9.106],[-7.85,9.179],[-7.8825,9.1615],[-7.9195,9.221],[-7.875,9.298],[-7.882,9.3395],[-7.8495,9.4215],[-7.9165,9.422],[-7.975,9.3985],[-8.036,9.3965],[-8.0845,9.44],[-8.1295,9.5105],[-8.144,9.5925],[-8.1155,9.651],[-8.1255,9.6785],[-8.0955,9.8085],[-8.095,9.8635],[-8.1605,9.9315],[-8.121,10.05],[-8.032,10.091],[-7.9795,10.1705],[-7.945,10.2525],[-7.985,10.3435],[-8.071,10.341],[-8.105,10.3935],[-8.1005,10.4495],[-8.1825,10.415],[-8.2305,10.4195],[-8.246,10.5135],[-8.2875,10.5955],[-8.325,10.5905],[-8.32,10.674],[-8.2885,10.6985],[-8.2895,10.758],[-8.3295,10.7715],[-8.3035,10.818],[-8.306,10.8565],[-8.26,10.8915],[-8.2785,10.994],[-8.3425,11.0625],[-8.481,11.0715],[-8.5115,10.999],[-8.5795,10.9605],[-8.665,10.953],[-8.675,11.0035],[-8.6345,11.039],[-8.601,11.0955],[-8.6045,11.1305],[-8.559,11.138],[-8.5505,11.2115],[-8.4735,11.2315],[-8.4655,11.29],[-8.391,11.276],[-8.3535,11.3155],[-8.404,11.332],[-8.372,11.3885],[-8.498,11.4165],[-8.52,11.484],[-8.601,11.4755],[-8.655,11.523],[-8.6415,11.548],[-8.7015,11.6455],[-8.751,11.6245],[-8.849,11.6545],[-8.82,11.781],[-8.7985,11.8285],[-8.772,11.977],[-8.8285,12.0395],[-8.8625,12.0305],[-8.9065,12.0575],[-8.9115,12.0945],[-8.8835,12.1515],[-8.918,12.1975],[-8.971,12.1925],[-8.9885,12.239],[-8.9335,12.325],[-9.007,12.396],[-9.0145,12.4205],[-9.0835,12.441],[-9.135,12.48],[-9.25,12.506],[-9.284,12.4855],[-9.3555,12.5015],[-9.3795,12.4305],[-9.302,12.367],[-9.3185,12.281],[-9.3735,12.2505],[-9.4225,12.2645],[-9.4845,12.257],[-9.5325,12.199],[-9.572,12.1975],[-9.6465,12.1685],[-9.683,12.1185],[-9.6885,12.0345],[-9.7445,12.0305],[-9.8295,12.057],[-9.872,12.058],[-9.914,12.104],[-9.968,12.103],[-10.091,12.1695],[-10.149,12.1765],[-10.1925,12.2055],[-10.342,12.223],[-10.3585,12.178],[-10.396,12.1825],[-10.41,12.145],[-10.453,12.12],[-10.513,12.126],[-10.5265,12.0375],[-10.5795,12.025],[-10.5675,11.9895],[-10.609,11.9745],[-10.634,11.906],[-10.7075,11.909],[-10.7735,11.9475],[-10.765,11.9715],[-10.8015,12.0415],[-10.792,12.1155],[-10.841,12.1305],[-10.9225,12.2255],[-10.9855,12.2155],[-11.0475,12.2215],[-11.0735,12.137],[-11.1755,12.024],[-11.225,11.998],[-11.311,12.024],[-11.3585,12.0665],[-11.367,12.1075],[-11.4545,12.134],[-11.493,12.179],[-11.4955,12.2135],[-11.4305,12.283],[-11.4365,12.3825],[-11.377,12.4125],[-11.476,12.4525],[-11.668,12.426],[-11.686,12.405],[-11.77,12.382],[-11.805,12.4035],[-11.8445,12.3925],[-11.9,12.449],[-11.968,12.4055],[-12.0105,12.4015],[-12.0595,12.433],[-12.118,12.4135],[-12.1775,12.3605],[-12.302,12.3505],[-12.3565,12.315],[-12.403,12.385],[-12.486,12.4045],[-12.565,12.3665],[-12.6265,12.436],[-12.765,12.4335],[-12.752,12.467],[-12.845,12.495],[-12.895,12.554],[-12.942,12.5375],[-12.9445,12.4755],[-13.0615,12.485],[-13.0465,12.5755],[-13.0515,12.6255],[-13.1385,12.645],[-13.2185,12.6515],[-13.332,12.645],[-13.345,12.6585],[-13.4835,12.674],[-13.708,12.6755],[-13.7095,12.608],[-13.6715,12.52],[-13.6415,12.49],[-13.6365,12.433],[-13.666,12.3905],[-13.6565,12.324],[-13.687,12.308],[-13.718,12.249],[-13.7645,12.275],[-13.851,12.2825],[-13.857,12.2425],[-13.916,12.2355],[-13.9495,12.1595],[-13.876,12.137],[-13.868,12.108],[-13.8065,12.0885],[-13.762,12.03],[-13.6965,12.008],[-13.7125,11.8805],[-13.7055,11.8325],[-13.708,11.7075],[-13.772,11.682],[-13.856,11.705],[-13.872,11.6605],[-13.918,11.68],[-13.9885,11.6415],[-14.039,11.655],[-14.081,11.6275],[-14.135,11.6645],[-14.267,11.677],[-14.3195,11.6025],[-14.4505,11.537],[-14.5115,11.4965],[-14.6635,11.505],[-14.69,11.483],[-14.7725,11.3645],[-14.8375,11.2025],[-14.936,11.0265],[-14.9575,10.977],[-15.002,10.9555],[-15.074,10.892],[-15.1015,10.8845],[-15.3415,10.6665],[-15.562,10.6665],[-15.563,10.5685],[-15.537,10.509],[-15.495,10.461],[-15.4395,10.4285],[-15.377,10.415],[-15.313,10.4215],[-15.233,10.462],[-15.187,10.507],[-15.1575,10.5655],[-15.1485,10.6295],[-15.078,10.589],[-14.9865,10.57],[-14.9145,10.573],[-14.871,10.531],[-14.8615,10.425],[-14.8095,10.318],[-14.7655,10.282],[-14.6835,10.253],[-14.6745,10.199],[-14.6315,10.111],[-14.5755,10.048],[-14.474,10.009],[-14.408,10.011],[-14.247,9.871],[-14.1575,9.841],[-14.0605,9.794],[-13.9955,9.748],[-13.939,9.667],[-13.995,9.605],[-14.034,9.525],[-14.0405,9.439],[-14.008,9.352],[-13.957,9.292],[-13.8615,9.241],[-13.7655,9.233],[-13.688,9.249],[-13.634,9.279],[-13.608,9.171],[-13.5795,9.132],[-13.521,9.088],[-13.518,9.028],[-13.496,8.966],[-13.301,9.036],[-13.2755,9.063],[-13.185,9.095],[-13.141,9.0565],[-13.082,9.0485],[-13.0735,9.08],[-13.0015,9.1165],[-12.9715,9.227],[-12.9415,9.2905],[-12.907,9.262],[-12.8795,9.2935],[-12.807,9.29],[-12.7855,9.3035],[-12.7555,9.392],[-12.684,9.417],[-12.654,9.54],[-12.6175,9.604],[-12.5875,9.6015],[-12.5645,9.7025],[-12.5285,9.717],[-12.471,9.85],[-12.4355,9.881],[-12.2225,9.933],[-12.2175,9.909],[-12.12,9.8725],[-11.907,9.94],[-11.8945,10.0],[-11.208,10.0],[-11.1725,9.961],[-11.1575,9.868],[-11.096,9.8575],[-11.039,9.7875],[-10.993,9.7525],[-10.964,9.664],[-10.918,9.655],[-10.911,9.602],[-10.881,9.5915],[-10.8365,9.4395],[-10.815,9.39],[-10.7315,9.3795],[-10.7085,9.332],[-10.6635,9.3105],[-10.667,9.1995],[-10.722,9.187],[-10.731,9.0815],[-10.6675,9.0855],[-10.574,9.0445],[-10.5905,8.9775],[-10.559,8.8445],[-10.562,8.818],[-10.52,8.7825],[-10.511,8.7425],[-10.466,8.674],[-10.4925,8.6255],[-10.5765,8.596],[-10.616,8.542],[-10.641,8.4745],[-10.6425,8.3545],[-10.5775,8.334],[-10.556,8.3055],[-10.491,8.35],[-10.478,8.3765],[-10.3995,8.4495],[-10.386,8.488],[-10.322,8.506],[-10.28,8.485]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/ed/Flag_of_Guinea.svg","name:en":"Guinea","wikidata":"Q1006","ISO3166-1:alpha2":"GN","ISO3166-1:alpha3":"GIN","ISO3166-1:numeric":"324"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-7.527,4.162],[-7.4095,4.1715],[-7.3365,4.196],[-7.1015,4.332],[-6.981,4.3535],[-6.915,4.3865],[-6.8,4.481],[-6.672,4.492],[-6.555,4.533],[-6.486,4.581],[-6.38,4.6055],[-6.3285,4.6325],[-6.1225,4.7],[-5.9785,4.775],[-5.798,4.842],[-5.5555,4.882],[-5.1835,4.924],[-4.745,4.955],[-4.524,4.988],[-4.232,5.0175],[-4.0265,5.0435],[-3.9295,5.041],[-3.8485,5.013],[-3.758,4.994],[-3.541,4.9675],[-3.358,4.937],[-3.1485,4.896],[-3.1035,5.086],[-3.1105,5.1185],[-3.0685,5.1345],[-2.97,5.1035],[-2.9485,5.127],[-2.8075,5.1065],[-2.7275,5.1325],[-2.7745,5.261],[-2.766,5.317],[-2.724,5.343],[-2.724,5.388],[-2.749,5.4375],[-2.768,5.5555],[-2.7615,5.6],[-2.864,5.6525],[-2.9365,5.6215],[-2.9665,5.6415],[-2.954,5.716],[-3.0265,5.709],[-3.02,5.857],[-3.074,5.985],[-3.1025,6.153],[-3.17,6.283],[-3.236,6.5415],[-3.2335,6.6015],[-3.259,6.6165],[-3.216,6.7255],[-3.2305,6.8235],[-3.0875,7.0525],[-3.032,7.072],[-3.024,7.142],[-2.953,7.2375],[-2.9755,7.27],[-2.97,7.3315],[-2.931,7.4945],[-2.922,7.612],[-2.831,7.82],[-2.791,7.8625],[-2.7815,7.952],[-2.7375,7.9475],[-2.6795,8.0205],[-2.6045,8.0305],[-2.626,8.102],[-2.6085,8.152],[-2.5515,8.1455],[-2.493,8.2065],[-2.4955,8.3025],[-2.5845,8.7865],[-2.623,8.8785],[-2.618,8.9215],[-2.652,8.9445],[-2.664,9.019],[-2.762,9.0375],[-2.773,9.1435],[-2.7255,9.17],[-2.7175,9.2015],[-2.664,9.24],[-2.7205,9.333],[-2.68,9.3625],[-2.687,9.4915],[-2.758,9.4115],[-2.824,9.451],[-2.9265,9.5685],[-3.0055,9.743],[-3.0565,9.7255],[-3.09,9.765],[-3.092,9.81],[-3.193,9.885],[-3.2335,9.8975],[-3.272,9.8465],[-3.345,9.9015],[-3.4065,9.93],[-3.5975,9.937],[-3.638,9.956],[-3.6675,9.9285],[-3.7755,9.927],[-3.81,9.9015],[-3.896,9.9055],[-3.9125,9.883],[-3.9675,9.875],[-3.995,9.831],[-4.051,9.803],[-4.127,9.847],[-4.189,9.774],[-4.2475,9.7645],[-4.287,9.6695],[-4.2935,9.6185],[-4.3405,9.625],[-4.4,9.6035],[-4.4305,9.664],[-4.505,9.647],[-4.521,9.6765],[-4.5025,9.7265],[-4.5835,9.695],[-4.651,9.7095],[-4.6875,9.6775],[-4.7455,9.7535],[-4.791,9.757],[-4.786,9.8355],[-4.9125,9.891],[-4.97,9.8985],[-4.964,9.9465],[-4.987,9.9885],[-4.96,10.0095],[-4.9735,10.0485],[-5.0185,10.101],[-5.0735,10.112],[-5.0615,10.181],[-5.1075,10.241],[-5.127,10.309],[-5.156,10.2865],[-5.2215,10.314],[-5.3685,10.2845],[-5.404,10.2965],[-5.462,10.3475],[-5.513,10.431],[-5.5625,10.464],[-5.621,10.452],[-5.6495,10.468],[-5.704,10.431],[-5.7655,10.439],[-5.8075,10.419],[-5.874,10.3555],[-5.874,10.3145],[-5.9015,10.263],[-5.9495,10.274],[-5.9845,10.1985],[-6.108,10.205],[-6.178,10.227],[-6.218,10.264],[-6.2305,10.305],[-6.1635,10.3665],[-6.181,10.4115],[-6.193,10.4975],[-6.242,10.5445],[-6.2105,10.569],[-6.246,10.74],[-6.3165,10.6855],[-6.395,10.709],[-6.415,10.698],[-6.3975,10.5985],[-6.4215,10.574],[-6.505,10.5795],[-6.59,10.623],[-6.6335,10.6735],[-6.669,10.6255],[-6.661,10.5885],[-6.682,10.528],[-6.6625,10.457],[-6.626,10.439],[-6.648,10.371],[-6.6785,10.3385],[-6.789,10.371],[-6.8675,10.344],[-6.945,10.3505],[-7.013,10.2425],[-6.977,10.2165],[-6.995,10.1725],[-7.049,10.148],[-7.0755,10.1965],[-7.1415,10.2495],[-7.2705,10.2595],[-7.3415,10.236],[-7.363,10.2745],[-7.3585,10.3345],[-7.421,10.342],[-7.4525,10.4015],[-7.4495,10.4445],[-7.514,10.4605],[-7.55,10.4125],[-7.626,10.462],[-7.718,10.3955],[-7.7215,10.3485],[-7.754,10.3415],[-7.8005,10.2445],[-7.8545,10.2005],[-7.8925,10.2],[-7.932,10.1545],[-7.9795,10.1705],[-8.032,10.091],[-8.121,10.05],[-8.1605,9.9315],[-8.095,9.8635],[-8.0955,9.8085],[-8.1255,9.6785],[-8.1155,9.651],[-8.144,9.5925],[-8.1295,9.5105],[-8.0845,9.44],[-8.036,9.3965],[-7.975,9.3985],[-7.9165,9.422],[-7.8495,9.4215],[-7.882,9.3395],[-7.875,9.298],[-7.9195,9.221],[-7.8825,9.1615],[-7.85,9.179],[-7.7825,9.106],[-7.8405,9.066],[-7.8565,9.0325],[-7.9345,8.982],[-7.9165,8.9355],[-7.9395,8.9025],[-7.9375,8.849],[-7.9745,8.831],[-7.9485,8.7885],[-7.911,8.7745],[-7.809,8.7685],[-7.7615,8.731],[-7.7325,8.659],[-7.692,8.6495],[-7.6715,8.618],[-7.6955,8.557],[-7.659,8.4985],[-7.6765,8.4415],[-7.65,8.378],[-7.742,8.3785],[-7.798,8.4855],[-7.8285,8.44],[-7.865,8.424],[-7.9075,8.4405],[-7.908,8.4905],[-7.9755,8.4895],[-8.063,8.511],[-8.111,8.4835],[-8.189,8.5065],[-8.238,8.4155],[-8.215,8.358],[-8.2175,8.2965],[-8.24,8.2345],[-8.1735,8.185],[-8.1005,8.182],[-8.062,8.157],[-7.9975,8.198],[-7.995,8.124],[-7.95,8.0325],[-7.9945,8.047],[-8.0625,8.0365],[-8.0685,8.002],[-8.135,7.883],[-8.1245,7.835],[-8.0955,7.7975],[-8.143,7.662],[-8.185,7.608],[-8.1705,7.5845],[-8.2185,7.5385],[-8.308,7.612],[-8.3545,7.5875],[-8.407,7.6235],[-8.471,7.557],[-8.423,7.528],[-8.3975,7.397],[-8.396,7.3285],[-8.36,7.278],[-8.365,7.2405],[-8.3315,7.203],[-8.288,7.1875],[-8.303,7.1355],[-8.2935,7.0255],[-8.326,6.972],[-8.3335,6.8915],[-8.3105,6.855],[-8.419,6.676],[-8.534,6.6],[-8.5695,6.5575],[-8.5715,6.523],[-8.482,6.434],[-8.4565,6.471],[-8.4055,6.447],[-8.385,6.386],[-8.334,6.373],[-8.1665,6.2795],[-8.0925,6.307],[-8.006,6.3225],[-7.9035,6.278],[-7.836,6.2045],[-7.852,6.1025],[-7.803,6.0935],[-7.7845,5.979],[-7.688,5.898],[-7.5865,5.8995],[-7.476,5.812],[-7.4305,5.8395],[-7.431,5.696],[-7.3755,5.613],[-7.3975,5.546],[-7.384,5.522],[-7.4195,5.4835],[-7.424,5.385],[-7.368,5.33],[-7.468,5.281],[-7.4825,5.2125],[-7.4705,5.1645],[-7.4835,5.1265],[-7.557,5.0755],[-7.5355,4.9585],[-7.55,4.924],[-7.592,4.908],[-7.5835,4.8335],[-7.5605,4.783],[-7.5625,4.649],[-7.548,4.612],[-7.5725,4.5815],[-7.5525,4.4885],[-7.567,4.4565],[-7.5655,4.3905],[-7.5295,4.3625],[-7.527,4.162]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fe/Flag_of_C%C3%B4te_d%27Ivoire.svg","name:en":"Côte d'Ivoire","wikidata":"Q1008","ISO3166-1:alpha2":"CI","ISO3166-1:alpha3":"CIV","ISO3166-1:numeric":"384"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[0.912,10.996],[1.013,11.0875],[1.0275,11.0525],[1.101,11.068],[1.0745,11.132],[1.1925,11.2635],[1.284,11.266],[1.294,11.2985],[1.347,11.3065],[1.326,11.3525],[1.392,11.4115],[1.3955,11.453],[1.4745,11.471],[1.5835,11.447],[1.6015,11.3975],[1.7735,11.4195],[1.8725,11.446],[1.982,11.42],[2.0195,11.4275],[2.184,11.594],[2.3145,11.6795],[2.2985,11.716],[2.372,11.798],[2.3805,11.8575],[2.409,11.9025],[2.3975,11.91],[2.2055,12.1675],[2.0655,12.3475],[2.078,12.386],[2.118,12.387],[2.1615,12.4155],[2.2615,12.4145],[2.264,12.4735],[2.2255,12.5165],[2.221,12.5935],[2.201,12.631],[2.1545,12.6515],[2.146,12.691],[2.096,12.7255],[2.0385,12.7165],[1.989,12.731],[1.928,12.7005],[1.8685,12.6055],[1.5665,12.6115],[1.33,12.8315],[1.1595,13.0075],[0.974,13.0075],[0.992,13.1035],[0.992,13.3745],[1.115,13.3315],[1.1855,13.321],[1.283,13.3545],[1.2445,13.393],[1.2035,13.387],[1.124,13.4135],[1.0315,13.4625],[1.019,13.523],[0.977,13.57],[0.902,13.623],[0.833,13.625],[0.7745,13.644],[0.773,13.6885],[0.625,13.684],[0.598,13.731],[0.618,13.767],[0.526,13.8435],[0.465,13.9135],[0.473,13.951],[0.424,13.9835],[0.417,14.023],[0.381,14.051],[0.213,14.418],[0.169,14.522],[0.236,14.7355],[0.1965,14.8265],[0.2445,14.914],[0.232,15.0],[0.1295,14.978],[0.0615,14.9745],[-0.2455,15.079],[-0.39,15.004],[-0.4315,15.0835],[-0.724,15.084],[-1.069,14.785],[-1.093,14.785],[-1.3185,14.73],[-1.678,14.5015],[-1.7775,14.4825],[-1.907,14.4895],[-1.9805,14.4755],[-1.998,14.1915],[-2.1035,14.147],[-2.178,14.192],[-2.2975,14.249],[-2.4735,14.2995],[-2.589,14.216],[-2.6765,14.135],[-2.806,14.077],[-2.839,14.0545],[-2.836,13.993],[-2.8805,13.867],[-2.9065,13.821],[-2.9065,13.7335],[-2.874,13.654],[-2.9525,13.6245],[-3.0075,13.6545],[-3.0295,13.6145],[-3.108,13.689],[-3.247,13.682],[-3.2505,13.5855],[-3.278,13.5575],[-3.255,13.3655],[-3.234,13.289],[-3.3385,13.288],[-3.4375,13.2765],[-3.4225,13.1785],[-3.4635,13.156],[-3.5235,13.166],[-3.5875,13.201],[-3.6725,13.275],[-3.7225,13.2925],[-3.798,13.372],[-3.8195,13.3605],[-3.9135,13.3825],[-3.9595,13.473],[-4.053,13.407],[-4.1005,13.387],[-4.1075,13.3345],[-4.1405,13.2935],[-4.2075,13.2695],[-4.259,13.2335],[-4.34,13.143],[-4.346,13.114],[-4.275,13.0395],[-4.2645,12.99],[-4.2255,12.974],[-4.2155,12.92],[-4.226,12.866],[-4.209,12.8165],[-4.233,12.734],[-4.2675,12.7045],[-4.3245,12.7115],[-4.347,12.744],[-4.3995,12.7185],[-4.453,12.7165],[-4.4805,12.6585],[-4.4115,12.61],[-4.4025,12.5605],[-4.3645,12.533],[-4.419,12.487],[-4.439,12.4045],[-4.3965,12.3105],[-4.472,12.332],[-4.4715,12.286],[-4.569,12.2085],[-4.544,12.1415],[-4.6075,12.139],[-4.6285,12.1135],[-4.63,12.0595],[-4.7005,12.066],[-4.7405,11.999],[-4.843,12.0145],[-4.9275,12.005],[-5.127,11.957],[-5.1725,11.927],[-5.245,11.84],[-5.319,11.8415],[-5.3555,11.825],[-5.285,11.7765],[-5.2595,11.736],[-5.268,11.6765],[-5.294,11.617],[-5.24,11.6045],[-5.2,11.528],[-5.21,11.4575],[-5.1985,11.429],[-5.2425,11.3965],[-5.255,11.359],[-5.256,11.244],[-5.3265,11.201],[-5.343,11.1295],[-5.392,11.1025],[-5.4895,11.0815],[-5.452,10.976],[-5.4455,10.8945],[-5.4025,10.8545],[-5.431,10.778],[-5.463,10.728],[-5.4575,10.556],[-5.508,10.461],[-5.513,10.431],[-5.462,10.3475],[-5.404,10.2965],[-5.3685,10.2845],[-5.2215,10.314],[-5.156,10.2865],[-5.127,10.309],[-5.1075,10.241],[-5.0615,10.181],[-5.0735,10.112],[-5.0185,10.101],[-4.9735,10.0485],[-4.96,10.0095],[-4.987,9.9885],[-4.964,9.9465],[-4.97,9.8985],[-4.9125,9.891],[-4.786,9.8355],[-4.791,9.757],[-4.7455,9.7535],[-4.6875,9.6775],[-4.651,9.7095],[-4.5835,9.695],[-4.5025,9.7265],[-4.521,9.6765],[-4.505,9.647],[-4.4305,9.664],[-4.4,9.6035],[-4.3405,9.625],[-4.2935,9.6185],[-4.287,9.6695],[-4.2475,9.7645],[-4.189,9.774],[-4.127,9.847],[-4.051,9.803],[-3.995,9.831],[-3.9675,9.875],[-3.9125,9.883],[-3.896,9.9055],[-3.81,9.9015],[-3.7755,9.927],[-3.6675,9.9285],[-3.638,9.956],[-3.5975,9.937],[-3.4065,9.93],[-3.345,9.9015],[-3.272,9.8465],[-3.2335,9.8975],[-3.193,9.885],[-3.092,9.81],[-3.09,9.765],[-3.0565,9.7255],[-3.0055,9.743],[-2.9265,9.5685],[-2.824,9.451],[-2.758,9.4115],[-2.687,9.4915],[-2.7605,9.555],[-2.772,9.6],[-2.7475,9.634],[-2.7835,9.6895],[-2.7905,9.7435],[-2.73,9.8385],[-2.7625,9.8885],[-2.743,9.9565],[-2.775,10.01],[-2.7905,10.0695],[-2.7965,10.1985],[-2.761,10.2265],[-2.766,10.2655],[-2.832,10.295],[-2.8505,10.319],[-2.8315,10.388],[-2.7775,10.4255],[-2.863,10.452],[-2.941,10.6365],[-2.91,10.6985],[-2.9405,10.7105],[-2.8955,10.764],[-2.8635,10.8645],[-2.8175,10.9255],[-2.841,10.971],[-2.8335,11.0065],[-2.6895,11.0045],[-2.5005,10.99],[-2.2245,10.994],[-2.1315,10.986],[-1.6165,10.985],[-1.6155,11.021],[-1.425,11.021],[-1.3825,10.997],[-1.115,10.9855],[-1.115,11.004],[-1.0005,11.008],[-0.9165,11.002],[-0.87,10.9665],[-0.855,10.998],[-0.684,10.9975],[-0.6715,10.961],[-0.5935,10.9235],[-0.5685,10.9925],[-0.5105,10.993],[-0.5005,11.0185],[-0.4335,11.037],[-0.4275,11.1145],[-0.381,11.126],[-0.336,11.107],[-0.2735,11.1235],[-0.2755,11.175],[-0.136,11.1395],[0.039,11.101],[0.5005,11.009],[0.4945,10.931],[0.6565,10.9975],[0.912,10.996]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/31/Flag_of_Burkina_Faso.svg","name:en":"Burkina Faso","wikidata":"Q965","ISO3166-1:alpha2":"BF","ISO3166-1:alpha3":"BFA","ISO3166-1:numeric":"854"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-25.573,17.0455],[-25.5595,16.971],[-25.5145,16.856],[-25.4575,16.771],[-25.3865,16.7235],[-24.6905,16.428],[-24.575,16.3755],[-24.8835,15.1815],[-24.9525,14.913],[-24.962,14.7985],[-24.9345,14.728],[-24.8535,14.6515],[-24.779,14.617],[-24.705,14.6065],[-24.358,14.615],[-23.4955,14.6975],[-23.3795,14.7355],[-22.9935,14.983],[-22.9465,15.024],[-22.908,15.086],[-22.5955,15.7635],[-22.4845,16.004],[-22.455,16.157],[-22.487,16.264],[-22.699,16.896],[-22.7485,16.9745],[-22.803,17.017],[-22.8745,17.05],[-22.954,17.055],[-23.245,17.019],[-24.2865,16.8905],[-24.902,17.3385],[-25.0525,17.4025],[-25.123,17.406],[-25.191,17.387],[-25.4335,17.2725],[-25.4955,17.2275],[-25.561,17.1245],[-25.573,17.0455]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/38/Flag_of_Cape_Verde.svg","name:en":"Cape Verde","wikidata":"Q1011","ISO3166-1:alpha2":"CV","ISO3166-1:alpha3":"CPV","ISO3166-1:numeric":"132"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-2.099,49.4595],[-2.139,49.54],[-2.066,49.681],[-2.0505,49.8085],[-2.0545,49.92],[-2.1185,49.933],[-2.2805,49.939],[-2.4675,49.9145],[-2.5935,49.868],[-2.6675,49.801],[-2.692,49.703],[-2.807,49.6705],[-2.893,49.6195],[-2.988,49.533],[-3.0205,49.4315],[-3.0,49.3685],[-2.933,49.3035],[-2.7785,49.279],[-2.5605,49.22],[-2.48,49.2645],[-2.295,49.326],[-2.1335,49.4075],[-2.099,49.4595]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fa/Flag_of_Guernsey.svg","name:en":"Guernsey","wikidata":"Q25230","ISO3166-1:alpha2":"GG","ISO3166-1:alpha3":"GGY","ISO3166-1:numeric":"831"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-61.6655,12.5965],[-61.7025,12.494],[-61.794,12.389],[-61.867,12.327],[-61.909,12.269],[-62.0045,12.03],[-61.9995,11.95],[-61.972,11.892],[-61.9275,11.84],[-61.87,11.803],[-61.8085,11.787],[-61.703,11.792],[-61.6175,11.815],[-61.518,11.876],[-61.4585,11.944],[-61.3655,12.12],[-61.3475,12.178],[-61.3495,12.25],[-61.215,12.393],[-61.1895,12.436],[-61.173,12.5165],[-61.3825,12.53],[-61.6655,12.5965]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/bc/Flag_of_Grenada.svg","name:en":"Grenada","wikidata":"Q769","ISO3166-1:alpha2":"GD","ISO3166-1:alpha3":"GRD","ISO3166-1:numeric":"308"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-180.0,-19.8005],[-179.094,-20.819],[-179.058,-21.0565],[-179.0355,-21.122],[-178.9905,-21.1765],[-178.9285,-21.2135],[-178.857,-21.2285],[-178.7845,-21.22],[-178.679,-21.19],[-178.6085,-21.155],[-178.556,-21.098],[-178.53,-21.027],[-178.484,-20.725],[-178.0815,-19.981],[-178.0185,-19.8515],[-178.0135,-19.771],[-178.0995,-19.212],[-178.1185,-18.945],[-178.133,-18.8795],[-178.0085,-18.3525],[-178.0035,-18.298],[-178.024,-17.915],[-178.045,-17.837],[-178.4305,-17.079],[-178.5055,-16.966],[-178.743,-16.655],[-178.945,-16.032],[-178.98,-15.9675],[-179.037,-15.919],[-179.3105,-15.7635],[-179.8935,-15.5235],[-180.0,-15.509],[-180.0,-15.563],[-180.0,-15.6155],[-180.0,-15.668],[-180.0,-15.7205],[-180.0,-15.773],[-180.0,-15.8775],[-180.0,-16.1415],[-180.0,-16.155],[-180.0,-16.1685],[-180.0,-16.3255],[-180.0,-16.3775],[-180.0,-16.43],[-180.0,-16.4825],[-180.0,-16.505],[-180.0,-16.5275],[-180.0,-16.572],[-180.0,-16.6165],[-180.0,-16.6605],[-180.0,-16.705],[-180.0,-16.749],[-180.0,-16.7935],[-180.0,-16.795],[-180.0,-16.7965],[-180.0,-16.818],[-180.0,-16.839],[-180.0,-16.86],[-180.0,-16.8815],[-180.0,-16.9025],[-180.0,-16.9235],[-180.0,-16.945],[-180.0,-16.9665],[-180.0,-17.105],[-180.0,-17.1515],[-180.0,-17.1975],[-180.0,-17.783],[-180.0,-18.3665],[-180.0,-18.419],[-180.0,-18.4715],[-180.0,-18.524],[-180.0,-18.5765],[-180.0,-18.629],[-180.0,-18.6815],[-180.0,-18.734],[-180.0,-18.7865],[-180.0,-18.839],[-180.0,-18.8915],[-180.0,-18.944],[-180.0,-18.9965],[-180.0,-19.0485],[-180.0,-19.101],[-180.0,-19.1535],[-180.0,-19.206],[-180.0,-19.489],[-180.0,-19.8005]]],[[[174.4215,-21.733],[174.4325,-21.8025],[174.4605,-21.8555],[174.5335,-21.9175],[174.625,-21.943],[174.702,-21.9355],[174.8015,-21.8775],[174.842,-21.818],[174.8585,-21.74],[174.845,-21.6685],[174.8105,-21.6095],[174.725,-21.5485],[174.652,-21.533],[174.5325,-21.5595],[174.4555,-21.6275],[174.4215,-21.733]]],[[[176.67,-17.2115],[176.676,-17.2475],[176.794,-17.6975],[176.8235,-17.761],[177.0305,-18.052],[177.142,-18.228],[177.479,-18.7095],[177.777,-19.253],[177.799,-19.28],[177.901,-19.3555],[177.9705,-19.377],[178.0975,-19.3925],[179.648,-19.4055],[180.0,-19.8005],[180.0,-19.489],[180.0,-19.206],[180.0,-19.1535],[180.0,-19.101],[180.0,-19.0485],[180.0,-18.9965],[180.0,-18.944],[180.0,-18.8915],[180.0,-18.839],[180.0,-18.7865],[180.0,-18.734],[180.0,-18.6815],[180.0,-18.629],[180.0,-18.5765],[180.0,-18.524],[180.0,-18.4715],[180.0,-18.419],[180.0,-18.3665],[180.0,-17.783],[180.0,-17.1975],[180.0,-17.145],[180.0,-17.0925],[180.0,-17.04],[180.0,-16.9875],[180.0,-16.9665],[180.0,-16.9455],[180.0,-16.924],[180.0,-16.903],[180.0,-16.8815],[180.0,-16.8605],[180.0,-16.839],[180.0,-16.818],[180.0,-16.7965],[180.0,-16.795],[180.0,-16.7935],[180.0,-16.7775],[180.0,-16.7245],[180.0,-16.672],[180.0,-16.6195],[180.0,-16.567],[180.0,-16.5275],[180.0,-16.505],[180.0,-16.4825],[180.0,-16.43],[180.0,-16.3775],[180.0,-16.3255],[180.0,-16.273],[180.0,-16.221],[180.0,-16.1685],[180.0,-16.155],[180.0,-16.1415],[180.0,-16.141],[180.0,-16.1405],[180.0,-16.14],[180.0,-16.1395],[180.0,-16.087],[180.0,-16.035],[180.0,-15.9825],[180.0,-15.93],[180.0,-15.8775],[180.0,-15.8255],[180.0,-15.773],[180.0,-15.7205],[180.0,-15.668],[180.0,-15.6155],[180.0,-15.563],[180.0,-15.509],[179.932,-15.529],[178.994,-15.9855],[178.4035,-16.1285],[178.0305,-16.2495],[177.4935,-16.4665],[177.195,-16.6015],[177.142,-16.636],[176.769,-16.9675],[176.7395,-16.9995],[176.6925,-17.0975],[176.67,-17.2115]]],[[[176.7295,-12.515],[176.7475,-12.582],[176.7885,-12.64],[176.8505,-12.6825],[176.984,-12.7175],[177.1435,-12.7325],[177.2055,-12.7225],[177.2735,-12.687],[177.325,-12.6275],[177.3515,-12.537],[177.3485,-12.4835],[177.3215,-12.403],[177.2815,-12.349],[177.222,-12.3015],[177.149,-12.2785],[177.0165,-12.263],[176.954,-12.2645],[176.886,-12.2865],[176.7875,-12.357],[176.7475,-12.414],[176.7295,-12.515]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/ba/Flag_of_Fiji.svg","name:en":"Fiji","wikidata":"Q712","ISO3166-1:alpha2":"FJ","ISO3166-1:alpha3":"FJI","ISO3166-1:numeric":"242"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[71.0365,-6.39],[71.041,-6.43],[71.098,-6.542],[71.123,-6.723],[71.1785,-6.8085],[71.2885,-6.8755],[71.3525,-6.885],[71.471,-6.878],[71.5195,-6.8505],[71.576,-6.7915],[71.598,-6.7215],[71.5945,-6.6555],[71.5675,-6.58],[71.506,-6.553],[71.434,-6.4575],[71.4425,-6.3845],[71.4645,-6.364],[71.5335,-6.3725],[71.5985,-6.362],[71.6615,-6.324],[71.7405,-6.2325],[71.746,-6.179],[71.729,-6.1],[71.686,-6.034],[71.561,-5.9545],[71.505,-5.9365],[71.4295,-5.9505],[71.238,-6.0035],[71.1615,-6.0735],[71.1085,-6.1595],[71.093,-6.2275],[71.05,-6.3215],[71.0365,-6.39]]],[[[71.5315,-5.2865],[71.5525,-5.459],[71.5965,-5.5675],[71.6515,-5.6225],[71.7165,-5.655],[71.8335,-5.6645],[71.906,-5.64],[72.0665,-5.5475],[72.107,-5.5355],[72.1715,-5.5605],[72.2185,-5.563],[72.3255,-5.5385],[72.379,-5.51],[72.452,-5.431],[72.473,-5.3865],[72.4675,-5.272],[72.4295,-5.1955],[72.388,-5.146],[72.312,-5.11],[72.2495,-5.1],[72.1565,-5.1125],[71.961,-5.0415],[71.7735,-5.0415],[71.681,-5.069],[71.583,-5.156],[71.5475,-5.2235],[71.5315,-5.2865]]],[[[72.151,-7.2845],[72.155,-7.3215],[72.2395,-7.492],[72.2595,-7.5595],[72.3415,-7.621],[72.4485,-7.6455],[72.5185,-7.6235],[72.5865,-7.5795],[72.636,-7.4815],[72.697,-7.3915],[72.6985,-7.321],[72.682,-7.248],[72.6455,-7.1695],[72.563,-7.0785],[72.487,-7.0385],[72.4185,-7.0275],[72.32,-7.055],[72.2025,-7.1395],[72.161,-7.209],[72.151,-7.2845]]],[[[72.221,-5.6735],[72.226,-5.7115],[72.2765,-5.7675],[72.3515,-5.7715],[72.408,-5.7215],[72.412,-5.6465],[72.362,-5.591],[72.3245,-5.581],[72.2535,-5.606],[72.221,-5.6735]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/6e/Flag_of_the_British_Indian_Ocean_Territory.svg","name:en":"British Indian Ocean Territory","wikidata":"Q43448","ISO3166-1:alpha2":"IO","ISO3166-1:alpha3":"IOT","ISO3166-1:numeric":"086"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-7.7985,62.0955],[-7.7815,62.069],[-7.3935,61.7775],[-6.958,61.447],[-6.7545,61.3095],[-6.6825,61.2885],[-6.5565,61.3185],[-6.5475,61.3505],[-6.3165,61.9145],[-6.1435,62.331],[-6.1675,62.375],[-6.2635,62.402],[-6.5685,62.4475],[-7.225,62.4245],[-7.3235,62.3835],[-7.3255,62.3275],[-7.7615,62.1345],[-7.7985,62.0955]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/3c/Flag_of_the_Faroe_Islands.svg","name:en":"Faroe Islands","wikidata":"Q4628","ISO3166-1:alpha2":"FO","ISO3166-1:alpha3":"FRO","ISO3166-1:numeric":"234"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-59.856,13.308],[-59.843,13.124],[-59.832,13.056],[-59.807,12.997],[-59.745,12.926],[-59.7045,12.9],[-59.5945,12.856],[-59.5235,12.845],[-59.4595,12.855],[-59.381,12.892],[-59.293,12.968],[-59.236,13.056],[-59.218,13.114],[-59.2175,13.182],[-59.247,13.26],[-59.2805,13.302],[-59.38,13.368],[-59.408,13.422],[-59.504,13.505],[-59.592,13.534],[-59.6705,13.528],[-59.755,13.49],[-59.7915,13.458],[-59.841,13.382],[-59.856,13.308]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/ef/Flag_of_Barbados.svg","name:en":"Barbados","wikidata":"Q244","ISO3166-1:alpha2":"BB","ISO3166-1:alpha3":"BRB","ISO3166-1:numeric":"052"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-68.647,80.2245],[-68.518,80.099],[-68.258,80.023],[-69.1075,79.319],[-69.259,79.1885],[-69.8085,79.0965],[-70.551,78.9475],[-71.098,78.8745],[-71.8705,78.8065],[-73.1305,78.666],[-73.5015,78.5555],[-73.7165,78.4765],[-73.8875,78.3835],[-73.9965,78.153],[-73.674,77.4265],[-73.4905,77.3185],[-72.1115,76.9005],[-72.722,76.942],[-73.1905,76.951],[-73.567,76.932],[-73.9485,76.864],[-74.06,76.8175],[-74.1175,76.708],[-74.001,76.628],[-73.7035,76.542],[-73.506,76.5075],[-73.164,76.4805],[-72.662,76.4845],[-72.1815,76.517],[-71.9435,76.5505],[-71.762,76.601],[-71.641,76.676],[-70.928,76.3285],[-70.6455,76.2535],[-69.2715,75.932],[-68.9675,75.8875],[-66.7635,75.7175],[-66.5525,75.7065],[-65.126,75.7495],[-64.385,75.679],[-60.734,75.3275],[-58.699,74.595],[-58.569,74.024],[-58.5375,73.9735],[-57.59,72.984],[-57.274,72.712],[-56.6775,72.099],[-56.4895,71.6635],[-56.433,71.6015],[-56.114,71.4085],[-55.6135,70.324],[-55.507,69.839],[-55.548,69.737],[-55.536,69.661],[-55.4875,69.6095],[-55.2325,69.469],[-54.763,69.282],[-54.305,68.2805],[-54.4925,67.817],[-54.576,67.3635],[-54.6385,67.007],[-54.6335,66.9615],[-54.4125,66.4],[-54.278,66.1765],[-53.713,65.4395],[-52.79,64.37],[-52.6405,63.9695],[-52.574,63.8905],[-51.7765,63.3055],[-51.58,63.067],[-51.54,63.0305],[-51.359,62.9215],[-50.947,62.615],[-50.7565,62.397],[-50.65,62.3],[-50.195,61.8475],[-49.9935,61.658],[-49.7145,61.4055],[-49.311,61.1415],[-48.8475,60.6545],[-48.7855,60.612],[-48.6485,60.5515],[-48.537,60.5255],[-47.7415,60.3935],[-45.5725,59.807],[-45.2165,59.681],[-45.0905,59.6505],[-44.1235,59.5225],[-44.009,59.5155],[-43.8955,59.5255],[-43.594,59.5795],[-42.99,59.7445],[-42.848,59.8085],[-42.718,59.9045],[-42.653,59.978],[-42.2095,60.8365],[-41.82,61.4875],[-41.6545,61.77],[-41.2535,62.5795],[-41.1485,62.7875],[-40.8055,63.0095],[-40.234,63.4655],[-40.0715,63.6485],[-39.755,64.284],[-39.339,64.849],[-36.8785,65.3075],[-36.486,65.3695],[-36.3335,65.4075],[-35.7985,65.6075],[-34.3175,66.233],[-33.829,66.4525],[-33.425,66.6185],[-33.33,66.6725],[-33.152,66.8215],[-32.1145,67.4585],[-30.2835,67.901],[-29.97,67.9395],[-29.215,68.046],[-27.4295,68.313],[-26.207,68.5175],[-26.1285,68.5335],[-25.361,68.721],[-23.698,69.262],[-22.853,69.517],[-21.896,69.858],[-21.5805,69.9965],[-21.5095,70.0565],[-20.98,70.423],[-20.902,70.4875],[-20.801,70.6145],[-20.785,70.6945],[-21.0095,71.3445],[-21.0375,71.568],[-21.3375,72.2555],[-21.028,72.5915],[-20.572,73.0375],[-19.8285,73.375],[-19.7575,73.4155],[-19.394,73.691],[-18.7125,74.1745],[-18.5915,74.224],[-17.754,74.5005],[-16.718,74.9045],[-16.588,74.9885],[-16.528,75.1175],[-16.575,75.209],[-16.6835,75.261],[-17.254,75.486],[-17.642,76.0235],[-17.631,76.0995],[-17.716,76.1955],[-17.735,76.295],[-17.8425,76.3945],[-17.8755,76.4775],[-17.4515,76.758],[-17.3335,76.8475],[-17.2845,77.0235],[-17.305,77.2145],[-16.844,77.535],[-16.761,77.5685],[-16.545,77.728],[-16.512,77.805],[-16.6695,77.927],[-16.907,78.7435],[-16.6065,78.981],[-16.5145,79.1225],[-16.3945,79.1675],[-16.0485,79.134],[-15.812,79.1285],[-15.4245,79.1435],[-15.062,79.196],[-14.864,79.266],[-14.81,79.3265],[-14.9225,79.432],[-15.191,79.4985],[-15.379,79.5215],[-15.772,79.5395],[-16.1725,79.5235],[-16.4725,79.4825],[-16.7035,79.4175],[-16.835,79.638],[-16.2575,79.8605],[-15.2955,80.1345],[-15.0155,80.2245],[-14.7765,80.2775],[-13.0985,80.449],[-12.719,80.5],[-12.477,80.557],[-12.3815,80.607],[-12.048,80.915],[-11.623,80.987],[-11.3485,81.0605],[-10.8935,81.1185],[-10.4145,81.2045],[-10.1445,81.289],[-10.029,81.369],[-10.0825,81.427],[-10.349,81.4995],[-10.64,81.63],[-11.5805,81.786],[-12.2735,81.867],[-16.7905,82.284],[-18.3475,82.6695],[-18.5455,82.725],[-19.1105,82.8345],[-19.851,82.948],[-20.4645,83.009],[-23.1415,83.3635],[-24.2025,83.5415],[-24.5665,83.5855],[-25.7185,83.6855],[-26.3025,83.7235],[-27.201,83.7565],[-30.116,83.866],[-30.5455,83.875],[-34.078,83.857],[-37.278,83.7985],[-39.2155,83.735],[-39.7555,83.7055],[-40.736,83.613],[-44.433,83.42],[-45.7815,83.3525],[-47.124,83.2585],[-49.8315,83.0135],[-51.3735,82.9415],[-51.7815,82.912],[-55.9525,82.5155],[-58.403,82.357],[-59.6265,82.2995],[-59.995,82.2175],[-62.1625,81.8675],[-64.1855,81.2995],[-66.2685,80.834],[-67.1205,80.749],[-68.335,80.416],[-68.647,80.2245]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/09/Flag_of_Greenland.svg","name:en":"Greenland","wikidata":"Q223","ISO3166-1:alpha2":"GL","ISO3166-1:alpha3":"GRL","ISO3166-1:numeric":"304"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[24.0,-10.89],[23.9615,-10.935],[23.8975,-10.9765],[23.863,-11.0355],[23.778,-10.994],[23.6685,-11.003],[23.597,-10.9865],[23.5315,-10.9455],[23.484,-10.9385],[23.3975,-10.9605],[23.299,-11.0215],[23.2555,-11.0715],[23.196,-11.108],[23.1515,-11.0815],[23.08,-11.113],[23.041,-11.0905],[22.9065,-11.0865],[22.8515,-11.065],[22.7735,-11.1185],[22.71,-11.1135],[22.647,-11.079],[22.6275,-11.047],[22.5775,-11.028],[22.489,-11.0645],[22.5025,-11.112],[22.4385,-11.177],[22.346,-11.1825],[22.344,-11.2025],[22.271,-11.263],[22.254,-11.213],[22.2685,-11.1805],[22.2175,-11.1125],[22.203,-10.9305],[22.1675,-10.8635],[22.209,-10.819],[22.2675,-10.785],[22.3075,-10.7795],[22.323,-10.719],[22.307,-10.531],[22.2715,-10.5075],[22.324,-10.383],[22.275,-10.2575],[22.235,-10.2165],[22.215,-10.1585],[22.2155,-10.0535],[22.193,-9.993],[22.1915,-9.933],[22.1455,-9.886],[22.0695,-9.875],[22.013,-9.82],[22.0015,-9.762],[21.935,-9.707],[21.86,-9.5945],[21.8645,-9.5595],[21.8335,-9.51],[21.8315,-9.464],[21.791,-9.4075],[21.81,-9.3295],[21.862,-9.1945],[21.8395,-9.1305],[21.8345,-9.0605],[21.8525,-8.949],[21.8965,-8.7835],[21.892,-8.7],[21.9245,-8.567],[21.9525,-8.504],[21.9485,-8.442],[21.9225,-8.397],[21.925,-8.307],[21.8925,-8.293],[21.8915,-8.253],[21.8235,-8.0705],[21.7745,-8.025],[21.7525,-7.945],[21.776,-7.9085],[21.7835,-7.829],[21.7755,-7.7735],[21.8525,-7.6045],[21.842,-7.5305],[21.8605,-7.4475],[21.8345,-7.4085],[21.8435,-7.374],[21.789,-7.2835],[21.242,-7.2835],[20.553,-7.2835],[20.548,-7.229],[20.5615,-7.1175],[20.5955,-7.0605],[20.627,-6.917],[20.322,-6.9165],[20.303,-7.0],[19.548,-7.0],[19.562,-7.0555],[19.4985,-7.168],[19.505,-7.249],[19.488,-7.3105],[19.4945,-7.3635],[19.5225,-7.3935],[19.53,-7.4625],[19.486,-7.5045],[19.4735,-7.572],[19.383,-7.572],[19.3695,-7.597],[19.406,-7.6705],[19.3835,-7.789],[19.3635,-7.8075],[19.3585,-7.9495],[19.3815,-8.0],[18.7965,-8.0],[18.762,-7.9165],[18.5305,-7.9165],[18.532,-8.0],[18.4075,-8.0],[18.3715,-8.0245],[18.3115,-8.0155],[18.2835,-7.987],[18.1965,-7.986],[18.1425,-8.0355],[18.093,-8.056],[18.1035,-8.0965],[17.976,-8.096],[17.874,-8.0805],[17.7965,-8.1135],[17.7625,-8.1005],[17.6795,-8.101],[17.614,-8.131],[17.539,-8.1115],[17.5035,-8.066],[17.514,-8.0165],[17.4805,-8.0015],[17.484,-7.9615],[17.45,-7.942],[17.42,-7.8555],[17.377,-7.7765],[17.335,-7.782],[17.316,-7.7155],[17.2905,-7.687],[17.302,-7.6335],[17.2125,-7.572],[17.195,-7.496],[17.15,-7.471],[17.1735,-7.424],[17.135,-7.4055],[17.1085,-7.362],[17.0365,-7.317],[17.0205,-7.2925],[16.941,-7.236],[16.955,-7.2115],[16.925,-7.141],[16.9555,-7.1045],[16.9665,-7.0525],[16.9,-6.983],[16.9105,-6.912],[16.8735,-6.8755],[16.846,-6.884],[16.8365,-6.8145],[16.802,-6.7935],[16.7975,-6.7265],[16.7655,-6.713],[16.754,-6.6315],[16.7325,-6.586],[16.7445,-6.511],[16.7055,-6.4765],[16.718,-6.451],[16.678,-6.382],[16.7195,-6.331],[16.7035,-6.2655],[16.704,-6.203],[16.7305,-6.167],[16.7135,-6.1295],[16.663,-6.091],[16.624,-6.0395],[16.596,-6.033],[16.6005,-5.9785],[16.577,-5.937],[16.5835,-5.878],[16.492,-5.844],[16.4345,-5.866],[16.1745,-5.86],[16.153,-5.864],[15.8145,-5.878],[15.68,-5.862],[15.4195,-5.87],[14.9445,-5.8655],[14.6995,-5.878],[14.596,-5.9045],[14.5055,-5.8875],[14.4125,-5.897],[14.3105,-5.883],[14.2775,-5.8605],[14.239,-5.881],[14.1795,-5.8395],[14.146,-5.8495],[14.013,-5.839],[13.774,-5.8695],[13.617,-5.873],[13.572,-5.883],[13.5145,-5.8625],[13.431,-5.86],[13.3775,-5.8725],[13.365,-5.902],[13.2225,-5.862],[13.1135,-5.9035],[13.084,-5.871],[13.0425,-5.8635],[12.951,-5.9125],[12.856,-5.9245],[12.785,-5.986],[12.625,-6.0195],[12.4445,-6.078],[12.3895,-6.068],[12.3125,-6.006],[12.1145,-5.99],[12.078,-6.0725],[12.0805,-6.1955],[12.1135,-6.268],[12.219,-6.42],[12.3345,-6.6615],[12.4175,-6.8055],[12.573,-6.973],[12.6295,-7.06],[12.6425,-7.1275],[12.653,-7.2905],[12.6675,-7.3445],[12.7265,-7.428],[12.8095,-7.669],[12.86,-7.7775],[12.916,-7.928],[12.956,-7.998],[13.0905,-8.2745],[13.1635,-8.3835],[13.145,-8.4865],[13.158,-8.5445],[13.1065,-8.628],[12.991,-8.7315],[12.9005,-8.8245],[12.838,-8.9075],[12.806,-8.9655],[12.7905,-9.051],[12.8065,-9.1685],[12.957,-9.4125],[13.0005,-9.6205],[12.9995,-9.733],[13.0265,-9.799],[13.111,-9.9175],[13.109,-9.9965],[13.135,-10.078],[13.2195,-10.189],[13.257,-10.2235],[13.304,-10.328],[13.3295,-10.358],[13.3445,-10.4695],[13.397,-10.5435],[13.465,-10.6095],[13.5285,-10.689],[13.517,-10.753],[13.5245,-10.8115],[13.5535,-10.873],[13.6545,-11.009],[13.622,-11.233],[13.595,-11.311],[13.589,-11.436],[13.5665,-11.5615],[13.5915,-11.761],[13.5725,-11.859],[13.538,-11.9125],[13.5105,-12.025],[13.4575,-12.1565],[13.3555,-12.2495],[13.2805,-12.3825],[13.136,-12.406],[13.036,-12.4685],[12.969,-12.5445],[12.8115,-12.6785],[12.745,-12.7735],[12.733,-12.8635],[12.744,-12.926],[12.6455,-13.022],[12.5695,-13.066],[12.511,-13.123],[12.442,-13.234],[12.38,-13.283],[12.338,-13.351],[12.3165,-13.4345],[12.334,-13.5485],[12.324,-13.7025],[12.272,-13.741],[12.2215,-13.829],[12.2,-13.9345],[12.169,-13.988],[12.1265,-14.204],[12.15,-14.3035],[12.133,-14.4225],[12.112,-14.485],[12.066,-14.7055],[12.0335,-14.749],[12.006,-14.84],[11.977,-14.8925],[11.918,-15.061],[11.862,-15.1635],[11.8445,-15.296],[11.8245,-15.375],[11.815,-15.509],[11.7415,-15.587],[11.703,-15.598],[11.623,-15.649],[11.586,-15.695],[11.5385,-15.802],[11.5235,-15.874],[11.5405,-15.9735],[11.587,-16.0525],[11.589,-16.2045],[11.498,-16.424],[11.4615,-16.531],[11.4685,-16.6155],[11.5215,-16.753],[11.561,-16.811],[11.57,-16.8675],[11.5585,-16.909],[11.5415,-17.093],[11.542,-17.25],[11.7495,-17.25],[11.7985,-17.268],[11.9325,-17.19],[12.079,-17.142],[12.102,-17.161],[12.162,-17.1625],[12.184,-17.1965],[12.25,-17.2405],[12.2835,-17.242],[12.4235,-17.2075],[12.4505,-17.2545],[12.5945,-17.235],[12.6175,-17.207],[12.6965,-17.169],[12.8725,-17.0645],[12.9155,-17.021],[12.9865,-16.982],[13.0905,-16.98],[13.17,-16.964],[13.2725,-16.9985],[13.352,-16.974],[13.4645,-17.013],[13.5225,-17.0905],[13.5415,-17.147],[13.68,-17.247],[13.8685,-17.343],[13.9165,-17.378],[13.9555,-17.433],[14.017,-17.415],[14.101,-17.439],[14.2125,-17.41],[14.2225,-17.3915],[14.637,-17.3915],[15.2015,-17.391],[15.578,-17.3905],[16.107,-17.3905],[16.8945,-17.39],[17.6265,-17.3895],[18.4215,-17.3895],[18.5335,-17.5205],[18.5685,-17.5845],[18.598,-17.5835],[18.624,-17.636],[18.7905,-17.7665],[18.834,-17.772],[18.8945,-17.816],[19.0345,-17.835],[19.118,-17.8365],[19.138,-17.8075],[19.231,-17.828],[19.2715,-17.8205],[19.331,-17.852],[19.4085,-17.8675],[19.508,-17.8605],[19.5665,-17.874],[19.6735,-17.8495],[19.7425,-17.9075],[19.8065,-17.8685],[19.863,-17.8835],[19.912,-17.862],[20.028,-17.9055],[20.057,-17.8855],[20.116,-17.9095],[20.1745,-17.877],[20.233,-17.8845],[20.2755,-17.865],[20.3325,-17.8665],[20.368,-17.8965],[20.4385,-17.9065],[20.4765,-17.952],[20.56,-17.9845],[20.6455,-17.977],[20.719,-18.013],[20.749,-18.0035],[20.8025,-18.0375],[20.8665,-18.0295],[20.9055,-17.995],[20.977,-17.958],[21.095,-17.9495],[21.1145,-17.9355],[21.21,-17.929],[21.268,-17.967],[21.423,-18.026],[22.5,-17.821],[22.9965,-17.724],[23.434,-17.638],[23.391,-17.5765],[23.3415,-17.554],[23.2215,-17.5325],[23.1775,-17.475],[23.136,-17.4665],[23.087,-17.4135],[22.9955,-17.274],[22.9095,-17.2225],[22.8965,-17.201],[22.802,-17.1655],[22.7915,-17.12],[22.729,-17.075],[22.716,-17.0425],[22.6285,-16.961],[22.599,-16.9185],[22.492,-16.835],[22.42,-16.7455],[22.264,-16.602],[22.185,-16.546],[22.1355,-16.49],[22.1395,-16.459],[22.1205,-16.348],[22.03,-16.2185],[22.0,-16.2075],[22.0005,-15.8865],[22.0005,-15.4065],[22.0005,-14.7385],[22.0005,-14.146],[22.0005,-13.73],[22.001,-13.3095],[22.0035,-12.9995],[22.6525,-12.9995],[23.229,-13.0005],[23.6725,-13.001],[24.058,-13.001],[24.025,-12.9705],[23.9835,-12.8985],[23.945,-12.8955],[23.8935,-12.8415],[23.898,-12.7695],[23.933,-12.698],[23.9205,-12.6815],[23.96,-12.592],[23.947,-12.5595],[24.0405,-12.4345],[24.064,-12.426],[24.078,-12.362],[24.0595,-12.2635],[24.0,-12.208],[23.9825,-12.1715],[23.9805,-12.0775],[23.998,-11.856],[24.023,-11.83],[23.993,-11.743],[23.9755,-11.6505],[24.038,-11.531],[24.024,-11.446],[24.0875,-11.4035],[24.056,-11.3085],[24.0315,-11.2745],[24.032,-11.134],[24.0105,-11.0185],[24.018,-10.942],[24.0,-10.89]]],[[[11.8475,-5.149],[11.934,-5.2685],[12.005,-5.456],[11.9525,-5.583],[11.9475,-5.683],[11.9685,-5.766],[11.9955,-5.8275],[12.039,-5.8895],[12.202,-5.774],[12.219,-5.7525],[12.2785,-5.742],[12.5285,-5.742],[12.528,-5.167],[12.494,-5.14],[12.4695,-5.0825],[12.518,-5.0465],[12.6055,-5.0265],[12.626,-4.9505],[12.72,-4.95],[12.7285,-4.911],[12.8155,-4.839],[12.828,-4.782],[12.8965,-4.735],[12.923,-4.7475],[12.9805,-4.7055],[13.03,-4.7155],[13.0515,-4.675],[13.1025,-4.684],[13.0075,-4.59],[12.9385,-4.5015],[12.853,-4.43],[12.816,-4.4375],[12.7585,-4.4175],[12.781,-4.4875],[12.765,-4.507],[12.71,-4.4775],[12.638,-4.5635],[12.5635,-4.5635],[12.536,-4.588],[12.487,-4.565],[12.4465,-4.602],[12.391,-4.611],[12.399,-4.666],[12.3715,-4.7335],[12.312,-4.8035],[12.2085,-4.8035],[12.166,-4.896],[12.0885,-4.9595],[12.016,-5.041],[11.8475,-5.149]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9d/Flag_of_Angola.svg","name:en":"Angola","wikidata":"Q916","ISO3166-1:alpha2":"AO","ISO3166-1:alpha3":"AGO","ISO3166-1:numeric":"024"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[9.1495,0.7535],[9.1085,0.597],[9.103,0.367],[8.8095,-0.0945],[8.551,-0.502],[8.5155,-0.5655],[8.501,-0.6845],[8.511,-0.7355],[8.575,-0.852],[8.6435,-1.0025],[8.682,-1.113],[8.7815,-1.248],[8.791,-1.325],[8.844,-1.4435],[8.872,-1.4855],[9.0295,-1.6835],[9.0475,-1.7195],[9.0725,-1.917],[9.135,-2.0405],[9.2465,-2.173],[9.3875,-2.318],[9.392,-2.3815],[9.412,-2.438],[9.4845,-2.547],[9.5735,-2.6275],[9.6945,-2.7525],[9.7305,-2.8185],[9.765,-2.854],[10.0005,-3.0255],[10.274,-3.2345],[10.358,-3.327],[10.385,-3.377],[10.442,-3.429],[10.4695,-3.5395],[10.5035,-3.5825],[10.615,-3.6645],[10.743,-3.7725],[10.808,-3.841],[10.842,-3.948],[10.881,-4.001],[11.005,-4.101],[11.1395,-3.9235],[11.1845,-3.8165],[11.223,-3.698],[11.348,-3.604],[11.431,-3.591],[11.483,-3.514],[11.5515,-3.5125],[11.5805,-3.5315],[11.6525,-3.636],[11.6835,-3.6945],[11.74,-3.6865],[11.8685,-3.71],[11.926,-3.637],[11.8525,-3.5895],[11.8395,-3.556],[11.9155,-3.386],[11.9635,-3.3515],[11.954,-3.2885],[11.871,-3.276],[11.801,-3.2145],[11.699,-3.173],[11.701,-3.089],[11.732,-3.038],[11.766,-3.047],[11.8055,-3.018],[11.7485,-2.946],[11.7135,-2.9185],[11.6545,-2.827],[11.628,-2.8205],[11.549,-2.8655],[11.546,-2.794],[11.612,-2.716],[11.647,-2.627],[11.6295,-2.5505],[11.5935,-2.504],[11.602,-2.4455],[11.593,-2.3745],[11.618,-2.3325],[11.677,-2.354],[11.6885,-2.394],[11.7635,-2.4025],[11.8015,-2.372],[11.9415,-2.3355],[11.98,-2.344],[12.0345,-2.4065],[12.091,-2.407],[12.478,-2.327],[12.4635,-2.3135],[12.496,-2.221],[12.484,-2.164],[12.517,-2.095],[12.479,-2.082],[12.448,-2.0245],[12.45,-1.9195],[12.507,-1.9215],[12.5185,-1.877],[12.585,-1.827],[12.65,-1.8225],[12.7165,-1.852],[12.7475,-1.8895],[12.8185,-1.9085],[12.8455,-1.9545],[12.8715,-2.0485],[12.9005,-2.0745],[12.901,-2.143],[12.944,-2.195],[13.0065,-2.2405],[13.011,-2.2955],[13.0415,-2.328],[13.153,-2.3745],[13.186,-2.359],[13.3875,-2.4285],[13.4085,-2.4175],[13.487,-2.4355],[13.501,-2.401],[13.552,-2.3695],[13.5855,-2.328],[13.727,-2.184],[13.727,-2.151],[13.758,-2.0915],[13.8095,-2.1445],[13.8465,-2.256],[13.891,-2.3305],[13.928,-2.363],[13.869,-2.414],[13.866,-2.469],[13.9315,-2.49],[13.968,-2.471],[14.001,-2.4985],[14.063,-2.475],[14.114,-2.4885],[14.1495,-2.4345],[14.157,-2.3915],[14.253,-2.3375],[14.194,-2.286],[14.1585,-2.2285],[14.2255,-2.1905],[14.2535,-2.1265],[14.2695,-2.033],[14.2645,-1.966],[14.317,-1.938],[14.3815,-1.9255],[14.4295,-1.8915],[14.4145,-1.857],[14.429,-1.793],[14.416,-1.7505],[14.424,-1.6715],[14.389,-1.6025],[14.4805,-1.5455],[14.4545,-1.497],[14.49,-1.3745],[14.451,-1.3525],[14.4475,-1.3145],[14.4765,-1.289],[14.482,-1.213],[14.4455,-1.1605],[14.4545,-1.127],[14.4175,-1.034],[14.433,-0.9595],[14.43,-0.9065],[14.488,-0.8475],[14.466,-0.785],[14.4745,-0.72],[14.534,-0.6395],[14.5155,-0.572],[14.47,-0.4935],[14.418,-0.4445],[14.347,-0.405],[14.232,-0.3975],[14.1845,-0.414],[14.19,-0.34],[14.1465,-0.253],[14.089,-0.221],[14.019,-0.2085],[13.9845,-0.18],[13.9465,-0.1815],[13.9065,-0.222],[13.876,-0.102],[13.952,-0.018],[13.9355,0.048],[13.904,0.1115],[13.9065,0.1745],[13.8805,0.2505],[13.9355,0.326],[13.973,0.4065],[14.0555,0.4605],[14.0835,0.508],[14.064,0.537],[14.107,0.583],[14.2385,0.567],[14.2965,0.6185],[14.3785,0.6705],[14.3765,0.742],[14.413,0.7735],[14.4275,0.819],[14.4605,0.8265],[14.484,0.8725],[14.4695,0.905],[14.4955,0.963],[14.4405,1.011],[14.399,1.0935],[14.392,1.143],[14.347,1.142],[14.3035,1.2705],[14.3055,1.323],[14.287,1.359],[14.2355,1.397],[14.147,1.3965],[14.074,1.3755],[14.0065,1.4345],[13.9815,1.414],[13.811,1.4295],[13.7905,1.374],[13.7605,1.3495],[13.661,1.3515],[13.6055,1.3285],[13.56,1.277],[13.4765,1.269],[13.437,1.296],[13.372,1.287],[13.31,1.223],[13.1835,1.225],[13.1865,1.286],[13.2275,1.286],[13.2575,1.3165],[13.233,1.4385],[13.1325,1.5605],[13.144,1.626],[13.14,1.707],[13.1815,1.755],[13.1995,1.8065],[13.1865,1.928],[13.208,1.9835],[13.264,2.0215],[13.2965,2.0635],[13.294,2.1705],[13.3055,2.207],[13.248,2.2735],[13.2195,2.261],[13.157,2.286],[13.0535,2.2395],[12.9975,2.252],[12.8615,2.2275],[12.8185,2.2525],[12.7515,2.2245],[12.6835,2.244],[12.6415,2.24],[12.5745,2.2705],[12.528,2.253],[12.4545,2.252],[12.3665,2.2675],[12.304,2.2905],[12.249,2.2565],[12.2075,2.282],[12.156,2.2765],[12.015,2.2905],[11.8695,2.2845],[11.754,2.271],[11.7015,2.319],[11.61,2.2955],[11.3965,2.3025],[11.352,2.2225],[11.36,2.172],[11.355,2.1595],[11.3465,1.8315],[11.3495,1.4715],[11.3535,1.002],[10.7185,1.0025],[9.992,1.0025],[9.9935,0.9385],[9.958,0.929],[9.895,0.953],[9.8795,0.9885],[9.798,1.0],[9.749,1.063],[9.668,1.062],[9.62,1.0295],[9.5475,1.0225],[9.519,0.963],[9.355,0.8465],[9.1495,0.7535]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/04/Flag_of_Gabon.svg","name:en":"Gabon","wikidata":"Q1000","ISO3166-1:alpha2":"GA","ISO3166-1:alpha3":"GAB","ISO3166-1:numeric":"266"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[13.1025,-4.684],[13.105,-4.6025],[13.1405,-4.594],[13.1855,-4.6665],[13.2815,-4.7655],[13.3735,-4.8],[13.373,-4.8585],[13.4285,-4.913],[13.441,-4.863],[13.495,-4.802],[13.515,-4.7475],[13.5775,-4.803],[13.6995,-4.745],[13.7255,-4.6895],[13.7175,-4.6375],[13.734,-4.506],[13.7235,-4.4555],[13.7795,-4.424],[13.8445,-4.4435],[13.88,-4.509],[13.9445,-4.5155],[14.07,-4.4045],[14.099,-4.415],[14.176,-4.398],[14.2525,-4.3575],[14.2885,-4.3195],[14.4015,-4.2785],[14.393,-4.3555],[14.4255,-4.3975],[14.4805,-4.432],[14.4625,-4.465],[14.3595,-4.561],[14.3905,-4.6335],[14.4195,-4.6515],[14.407,-4.7035],[14.3975,-4.8515],[14.415,-4.896],[14.472,-4.8505],[14.5615,-4.865],[14.591,-4.9035],[14.651,-4.922],[14.7005,-4.9065],[14.7535,-4.862],[14.848,-4.807],[14.8985,-4.688],[14.9865,-4.603],[15.0315,-4.539],[15.1,-4.489],[15.144,-4.4185],[15.1755,-4.391],[15.1965,-4.325],[15.2565,-4.316],[15.3285,-4.269],[15.404,-4.2855],[15.4795,-4.229],[15.517,-4.078],[15.5645,-4.0355],[15.7195,-3.989],[15.803,-3.9815],[15.8865,-3.9545],[15.906,-3.9335],[15.927,-3.8595],[15.9675,-3.7685],[15.9995,-3.725],[16.039,-3.627],[16.0915,-3.5565],[16.1245,-3.472],[16.164,-3.423],[16.2015,-3.352],[16.2135,-3.287],[16.1815,-3.2565],[16.187,-3.192],[16.1715,-3.118],[16.192,-3.0],[16.179,-2.904],[16.208,-2.7235],[16.2355,-2.6545],[16.236,-2.602],[16.1745,-2.3445],[16.1935,-2.1805],[16.2655,-2.0935],[16.3665,-2.0365],[16.4065,-1.98],[16.441,-1.96],[16.508,-1.8905],[16.576,-1.7865],[16.663,-1.62],[16.681,-1.562],[16.74,-1.428],[16.807,-1.3165],[16.9535,-1.167],[17.0455,-1.109],[17.1495,-1.0565],[17.2485,-1.036],[17.344,-0.99],[17.5295,-0.7445],[17.582,-0.6945],[17.6355,-0.6695],[17.6755,-0.6105],[17.694,-0.5575],[17.7165,-0.4145],[17.7135,-0.356],[17.677,-0.3005],[17.6635,-0.231],[17.6765,-0.168],[17.734,-0.064],[17.774,0.1225],[17.8215,0.2335],[17.92,0.3145],[17.954,0.3615],[17.9685,0.453],[17.932,0.534],[17.8855,0.571],[17.868,0.614],[17.869,0.702],[17.889,0.776],[17.8775,0.8525],[17.8405,0.9245],[17.845,1.021],[17.926,1.1455],[17.962,1.3195],[18.0705,1.5285],[18.078,1.729],[18.0655,1.875],[18.084,1.938],[18.072,2.0425],[18.0945,2.139],[18.091,2.218],[18.1105,2.2805],[18.1685,2.3315],[18.2245,2.4185],[18.2265,2.4845],[18.243,2.5295],[18.3205,2.581],[18.402,2.73],[18.4445,2.8665],[18.491,2.9495],[18.539,3.0755],[18.62,3.144],[18.6415,3.2045],[18.633,3.2745],[18.6435,3.3115],[18.626,3.471],[18.594,3.485],[18.5335,3.599],[18.4715,3.641],[18.4425,3.5955],[18.265,3.577],[18.222,3.4905],[18.178,3.4835],[18.16,3.533],[18.1195,3.5605],[18.0435,3.566],[17.995,3.5395],[17.9475,3.573],[17.854,3.5375],[17.834,3.6105],[17.723,3.6265],[17.6155,3.6295],[17.564,3.6535],[17.5025,3.706],[17.4595,3.7085],[17.414,3.6795],[17.3605,3.618],[17.3035,3.6265],[17.2565,3.617],[17.234,3.5845],[17.0715,3.5755],[17.0335,3.5405],[16.988,3.5355],[16.9605,3.556],[16.8765,3.566],[16.862,3.5245],[16.7975,3.526],[16.767,3.55],[16.6595,3.5335],[16.589,3.482],[16.572,3.444],[16.561,3.339],[16.529,3.2755],[16.5355,3.2485],[16.484,3.162],[16.4825,3.132],[16.512,3.0605],[16.469,2.981],[16.466,2.9225],[16.496,2.8845],[16.499,2.843],[16.207,2.221],[16.184,2.2055],[16.0815,2.1905],[16.0835,2.0475],[16.0515,2.0045],[16.0835,1.8995],[16.134,1.8045],[16.145,1.6905],[16.0825,1.6905],[16.0575,1.652],[16.019,1.6785],[16.021,1.766],[15.96,1.7665],[15.8855,1.7905],[15.8685,1.8285],[15.8055,1.856],[15.7475,1.92],[15.616,1.9385],[15.4855,1.9835],[15.347,1.912],[15.3035,1.9425],[15.305,1.969],[15.2495,2.028],[15.2015,2.0385],[15.122,2.014],[15.105,1.983],[15.045,1.9795],[14.979,2.0355],[14.9685,2.008],[14.8935,2.0155],[14.911,2.064],[14.8565,2.1015],[14.808,2.0595],[14.718,2.1275],[14.644,2.1325],[14.595,2.202],[14.572,2.179],[14.443,2.1575],[14.413,2.1815],[14.3555,2.1855],[14.292,2.172],[13.8195,2.172],[13.294,2.1705],[13.2965,2.0635],[13.264,2.0215],[13.208,1.9835],[13.1865,1.928],[13.1995,1.8065],[13.1815,1.755],[13.14,1.707],[13.144,1.626],[13.1325,1.5605],[13.233,1.4385],[13.2575,1.3165],[13.2275,1.286],[13.1865,1.286],[13.1835,1.225],[13.31,1.223],[13.372,1.287],[13.437,1.296],[13.4765,1.269],[13.56,1.277],[13.6055,1.3285],[13.661,1.3515],[13.7605,1.3495],[13.7905,1.374],[13.811,1.4295],[13.9815,1.414],[14.0065,1.4345],[14.074,1.3755],[14.147,1.3965],[14.2355,1.397],[14.287,1.359],[14.3055,1.323],[14.3035,1.2705],[14.347,1.142],[14.392,1.143],[14.399,1.0935],[14.4405,1.011],[14.4955,0.963],[14.4695,0.905],[14.484,0.8725],[14.4605,0.8265],[14.4275,0.819],[14.413,0.7735],[14.3765,0.742],[14.3785,0.6705],[14.2965,0.6185],[14.2385,0.567],[14.107,0.583],[14.064,0.537],[14.0835,0.508],[14.0555,0.4605],[13.973,0.4065],[13.9355,0.326],[13.8805,0.2505],[13.9065,0.1745],[13.904,0.1115],[13.9355,0.048],[13.952,-0.018],[13.876,-0.102],[13.9065,-0.222],[13.9465,-0.1815],[13.9845,-0.18],[14.019,-0.2085],[14.089,-0.221],[14.1465,-0.253],[14.19,-0.34],[14.1845,-0.414],[14.232,-0.3975],[14.347,-0.405],[14.418,-0.4445],[14.47,-0.4935],[14.5155,-0.572],[14.534,-0.6395],[14.4745,-0.72],[14.466,-0.785],[14.488,-0.8475],[14.43,-0.9065],[14.433,-0.9595],[14.4175,-1.034],[14.4545,-1.127],[14.4455,-1.1605],[14.482,-1.213],[14.4765,-1.289],[14.4475,-1.3145],[14.451,-1.3525],[14.49,-1.3745],[14.4545,-1.497],[14.4805,-1.5455],[14.389,-1.6025],[14.424,-1.6715],[14.416,-1.7505],[14.429,-1.793],[14.4145,-1.857],[14.4295,-1.8915],[14.3815,-1.9255],[14.317,-1.938],[14.2645,-1.966],[14.2695,-2.033],[14.2535,-2.1265],[14.2255,-2.1905],[14.1585,-2.2285],[14.194,-2.286],[14.253,-2.3375],[14.157,-2.3915],[14.1495,-2.4345],[14.114,-2.4885],[14.063,-2.475],[14.001,-2.4985],[13.968,-2.471],[13.9315,-2.49],[13.866,-2.469],[13.869,-2.414],[13.928,-2.363],[13.891,-2.3305],[13.8465,-2.256],[13.8095,-2.1445],[13.758,-2.0915],[13.727,-2.151],[13.727,-2.184],[13.5855,-2.328],[13.552,-2.3695],[13.501,-2.401],[13.487,-2.4355],[13.4085,-2.4175],[13.3875,-2.4285],[13.186,-2.359],[13.153,-2.3745],[13.0415,-2.328],[13.011,-2.2955],[13.0065,-2.2405],[12.944,-2.195],[12.901,-2.143],[12.9005,-2.0745],[12.8715,-2.0485],[12.8455,-1.9545],[12.8185,-1.9085],[12.7475,-1.8895],[12.7165,-1.852],[12.65,-1.8225],[12.585,-1.827],[12.5185,-1.877],[12.507,-1.9215],[12.45,-1.9195],[12.448,-2.0245],[12.479,-2.082],[12.517,-2.095],[12.484,-2.164],[12.496,-2.221],[12.4635,-2.3135],[12.478,-2.327],[12.091,-2.407],[12.0345,-2.4065],[11.98,-2.344],[11.9415,-2.3355],[11.8015,-2.372],[11.7635,-2.4025],[11.6885,-2.394],[11.677,-2.354],[11.618,-2.3325],[11.593,-2.3745],[11.602,-2.4455],[11.5935,-2.504],[11.6295,-2.5505],[11.647,-2.627],[11.612,-2.716],[11.546,-2.794],[11.549,-2.8655],[11.628,-2.8205],[11.6545,-2.827],[11.7135,-2.9185],[11.7485,-2.946],[11.8055,-3.018],[11.766,-3.047],[11.732,-3.038],[11.701,-3.089],[11.699,-3.173],[11.801,-3.2145],[11.871,-3.276],[11.954,-3.2885],[11.9635,-3.3515],[11.9155,-3.386],[11.8395,-3.556],[11.8525,-3.5895],[11.926,-3.637],[11.8685,-3.71],[11.74,-3.6865],[11.6835,-3.6945],[11.6525,-3.636],[11.5805,-3.5315],[11.5515,-3.5125],[11.483,-3.514],[11.431,-3.591],[11.348,-3.604],[11.223,-3.698],[11.1845,-3.8165],[11.1395,-3.9235],[11.005,-4.101],[11.0905,-4.158],[11.18,-4.2295],[11.219,-4.3125],[11.254,-4.3545],[11.3905,-4.465],[11.584,-4.634],[11.5845,-4.707],[11.6545,-4.894],[11.715,-4.966],[11.8475,-5.149],[12.016,-5.041],[12.0885,-4.9595],[12.166,-4.896],[12.2085,-4.8035],[12.312,-4.8035],[12.3715,-4.7335],[12.399,-4.666],[12.391,-4.611],[12.4465,-4.602],[12.487,-4.565],[12.536,-4.588],[12.5635,-4.5635],[12.638,-4.5635],[12.71,-4.4775],[12.765,-4.507],[12.781,-4.4875],[12.7585,-4.4175],[12.816,-4.4375],[12.853,-4.43],[12.9385,-4.5015],[13.0075,-4.59],[13.1025,-4.684]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/92/Flag_of_the_Republic_of_the_Congo.svg","name:en":"Congo-Brazzaville","wikidata":"Q971","ISO3166-1:alpha2":"CG","ISO3166-1:alpha3":"COG","ISO3166-1:numeric":"178"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[43.0255,-11.737],[43.059,-11.872],[43.1275,-11.971],[43.2045,-12.029],[43.283,-12.056],[43.3915,-12.126],[43.4665,-12.138],[43.422,-12.24],[43.4205,-12.316],[43.4405,-12.452],[43.4955,-12.545],[43.599,-12.608],[43.669,-12.621],[43.745,-12.618],[43.8325,-12.589],[43.913,-12.586],[43.975,-12.563],[44.0335,-12.513],[44.0785,-12.414],[44.077,-12.33],[44.1615,-12.385],[44.2665,-12.432],[44.37,-12.537],[44.45,-12.575],[44.563,-12.579],[44.6215,-12.558],[44.68,-12.514],[44.7205,-12.448],[44.738,-12.388],[44.7435,-12.234],[44.6905,-12.048],[44.6415,-11.955],[44.578,-11.899],[44.5305,-11.878],[44.4435,-11.869],[44.368,-11.89],[44.26,-11.969],[44.1465,-11.969],[44.0595,-12.017],[44.0115,-12.083],[43.9955,-12.139],[43.997,-12.199],[43.9335,-12.155],[43.8015,-12.082],[43.661,-12.049],[43.706,-11.972],[43.721,-11.872],[43.7025,-11.806],[43.659,-11.74],[43.645,-11.674],[43.607,-11.589],[43.6205,-11.435],[43.606,-11.341],[43.55,-11.246],[43.4905,-11.199],[43.4395,-11.179],[43.353,-11.165],[43.227,-11.194],[43.1455,-11.245],[43.1035,-11.295],[43.0755,-11.358],[43.06,-11.456],[43.066,-11.498],[43.056,-11.626],[43.0255,-11.737]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/94/Flag_of_the_Comoros.svg","name:en":"Comoros","wikidata":"Q970","ISO3166-1:alpha2":"KM","ISO3166-1:alpha3":"COM","ISO3166-1:numeric":"174"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[30.5435,-2.413],[30.4715,-2.367],[30.459,-2.3185],[30.4085,-2.31],[30.3735,-2.3525],[30.2925,-2.3735],[30.232,-2.3505],[30.185,-2.427],[30.126,-2.437],[30.061,-2.39],[30.0465,-2.361],[29.9595,-2.3275],[29.944,-2.373],[29.9675,-2.446],[29.9205,-2.5565],[29.9355,-2.642],[29.918,-2.7025],[29.887,-2.7495],[29.8115,-2.771],[29.7695,-2.767],[29.7455,-2.807],[29.708,-2.8195],[29.6585,-2.7875],[29.543,-2.8295],[29.487,-2.8025],[29.4385,-2.7975],[29.3845,-2.821],[29.334,-2.747],[29.3505,-2.7005],[29.328,-2.653],[29.264,-2.6185],[29.2225,-2.632],[29.1495,-2.5915],[29.0625,-2.6015],[29.055,-2.7125],[29.0405,-2.7435],[29.001,-2.7855],[29.006,-2.8165],[29.0515,-2.821],[29.094,-2.882],[29.0965,-2.9205],[29.158,-2.9505],[29.203,-3.0225],[29.2545,-3.0525],[29.2475,-3.1255],[29.2225,-3.198],[29.238,-3.279],[29.2135,-3.295],[29.245,-3.5155],[29.2495,-3.7165],[29.2255,-3.843],[29.244,-3.928],[29.313,-3.9855],[29.3825,-4.117],[29.419,-4.2495],[29.436,-4.449],[29.6595,-4.448],[29.7075,-4.462],[29.7685,-4.4565],[29.765,-4.4275],[29.8155,-4.364],[29.8665,-4.381],[29.8935,-4.3535],[29.9615,-4.3255],[30.006,-4.28],[30.04,-4.2765],[30.078,-4.168],[30.1575,-4.115],[30.215,-4.0465],[30.2135,-3.9905],[30.256,-3.884],[30.308,-3.8455],[30.331,-3.7845],[30.41,-3.7665],[30.388,-3.713],[30.4215,-3.6415],[30.4555,-3.607],[30.451,-3.5605],[30.501,-3.517],[30.618,-3.4665],[30.6775,-3.398],[30.635,-3.3595],[30.6665,-3.3205],[30.826,-3.264],[30.8135,-3.2235],[30.846,-3.206],[30.836,-3.094],[30.816,-3.0395],[30.8375,-2.976],[30.7475,-2.9975],[30.704,-2.9715],[30.668,-2.9935],[30.647,-2.954],[30.5765,-2.8945],[30.5065,-2.9535],[30.4495,-2.913],[30.415,-2.843],[30.439,-2.802],[30.4405,-2.745],[30.4595,-2.691],[30.42,-2.6625],[30.485,-2.556],[30.5435,-2.413]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/5/50/Flag_of_Burundi.svg","name:en":"Burundi","wikidata":"Q967","ISO3166-1:alpha2":"BI","ISO3166-1:alpha3":"BDI","ISO3166-1:numeric":"108"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[72.246,-53.0205],[72.2675,-53.104],[72.3295,-53.173],[72.4245,-53.2215],[72.5885,-53.25],[72.7395,-53.2325],[72.8695,-53.176],[72.9315,-53.1205],[72.982,-53.1195],[73.034,-53.1585],[73.048,-53.2435],[73.0795,-53.2845],[73.194,-53.3525],[73.2925,-53.376],[73.4945,-53.3945],[73.61,-53.391],[73.728,-53.3725],[73.8805,-53.3135],[73.97,-53.3005],[74.0895,-53.26],[74.149,-53.219],[74.202,-53.1325],[74.197,-53.065],[74.1615,-53.009],[74.062,-52.943],[73.918,-52.909],[73.873,-52.8175],[73.8225,-52.7755],[73.7265,-52.7275],[73.5775,-52.703],[73.451,-52.716],[73.334,-52.762],[73.247,-52.764],[73.147,-52.785],[73.041,-52.8315],[72.9485,-52.9075],[72.894,-52.9185],[72.8185,-52.8745],[72.637,-52.824],[72.5125,-52.825],[72.373,-52.864],[72.263,-52.958],[72.246,-53.0205]]],[[[96.6115,-11.8345],[96.6225,-11.903],[96.658,-11.9665],[96.6305,-12.0345],[96.6125,-12.1285],[96.612,-12.1895],[96.635,-12.2735],[96.724,-12.3725],[96.7695,-12.396],[96.908,-12.4115],[97.0065,-12.384],[97.086,-12.319],[97.1205,-12.257],[97.1315,-12.145],[97.1015,-12.0505],[97.0515,-11.9705],[97.0115,-11.93],[97.035,-11.8315],[97.0165,-11.7445],[96.97,-11.6795],[96.8805,-11.629],[96.7655,-11.6295],[96.6705,-11.6885],[96.6325,-11.7465],[96.6115,-11.8345]]],[[[105.33,-10.5125],[105.3445,-10.586],[105.368,-10.6295],[105.433,-10.688],[105.5035,-10.7135],[105.5765,-10.758],[105.635,-10.771],[105.715,-10.7635],[105.761,-10.746],[105.819,-10.701],[105.8525,-10.649],[105.9095,-10.516],[105.9155,-10.4535],[105.9,-10.3665],[105.869,-10.299],[105.8015,-10.238],[105.7405,-10.2155],[105.6295,-10.218],[105.5705,-10.2415],[105.48,-10.2535],[105.407,-10.3005],[105.357,-10.3805],[105.33,-10.5125]]],[[[112.7,-25.5885],[112.7035,-25.638],[112.7455,-25.7625],[112.754,-25.8365],[112.8725,-26.052],[112.9365,-26.119],[112.9465,-26.2195],[113.0015,-26.303],[113.0695,-26.3735],[113.083,-26.458],[113.1105,-26.5105],[113.163,-26.5685],[113.3715,-26.7575],[113.419,-26.811],[113.588,-27.0305],[113.7645,-27.3215],[113.8885,-27.564],[113.926,-27.6775],[113.8805,-27.8275],[113.8845,-27.8945],[113.904,-27.948],[113.965,-28.186],[114.027,-28.2795],[114.0875,-28.3435],[114.152,-28.3785],[114.1955,-28.428],[114.253,-28.519],[114.3185,-28.58],[114.377,-28.6765],[114.3435,-28.789],[114.371,-28.894],[114.4245,-28.9745],[114.5175,-29.0685],[114.6525,-29.192],[114.676,-29.251],[114.6225,-29.3105],[114.597,-29.4135],[114.637,-29.5165],[114.721,-29.5855],[114.716,-29.6235],[114.6675,-29.6665],[114.632,-29.7305],[114.6255,-29.8085],[114.6455,-29.87],[114.706,-29.944],[114.6905,-30.028],[114.7135,-30.107],[114.712,-30.16],[114.7425,-30.24],[114.742,-30.3175],[114.768,-30.48],[114.7905,-30.561],[114.8515,-30.688],[114.997,-30.945],[115.0565,-30.9955],[115.13,-31.147],[115.1215,-31.1985],[115.1475,-31.3125],[115.2075,-31.38],[115.262,-31.41],[115.2935,-31.5115],[115.369,-31.583],[115.416,-31.667],[115.469,-31.7895],[115.4,-31.8085],[115.3135,-31.854],[115.2435,-31.921],[115.217,-31.9785],[115.2125,-32.056],[115.241,-32.126],[115.3305,-32.2015],[115.4275,-32.227],[115.4025,-32.303],[115.4055,-32.379],[115.425,-32.425],[115.428,-32.4945],[115.378,-32.6155],[115.37,-32.6805],[115.3965,-32.8545],[115.4205,-32.938],[115.449,-33.1125],[115.442,-33.192],[115.409,-33.246],[115.376,-33.337],[115.287,-33.434],[115.1975,-33.3805],[115.11,-33.343],[115.0025,-33.3255],[114.9445,-33.3325],[114.8305,-33.39],[114.78,-33.4545],[114.762,-33.531],[114.7665,-33.596],[114.741,-33.6495],[114.74,-33.749],[114.723,-33.7955],[114.734,-33.9065],[114.7315,-34.0025],[114.7485,-34.0445],[114.7635,-34.168],[114.761,-34.289],[114.806,-34.392],[114.902,-34.488],[115.011,-34.567],[115.0895,-34.6105],[115.2315,-34.625],[115.325,-34.6005],[115.375,-34.572],[115.49,-34.622],[115.6245,-34.725],[115.7265,-34.8185],[115.7885,-34.96],[115.8195,-34.992],[115.926,-35.0505],[115.983,-35.066],[116.027,-35.14],[116.083,-35.183],[116.17,-35.213],[116.237,-35.2165],[116.3345,-35.1935],[116.4465,-35.2335],[116.701,-35.2755],[116.821,-35.2575],[116.927,-35.2645],[117.035,-35.244],[117.1445,-35.2685],[117.278,-35.241],[117.331,-35.2445],[117.5275,-35.3215],[117.586,-35.333],[117.6935,-35.33],[117.7575,-35.3695],[117.831,-35.3895],[117.953,-35.384],[118.0035,-35.369],[118.1345,-35.305],[118.19,-35.2595],[118.3405,-35.1845],[118.392,-35.1485],[118.5165,-35.1435],[118.6105,-35.1155],[118.7,-35.0345],[118.7285,-34.9475],[118.73,-34.897],[118.819,-34.8565],[118.946,-34.757],[119.0035,-34.6895],[119.198,-34.7325],[119.333,-34.7245],[119.523,-34.6485],[119.6425,-34.5815],[119.7255,-34.5625],[119.8155,-34.5005],[119.854,-34.434],[119.862,-34.375],[119.8435,-34.3035],[119.8095,-34.256],[119.959,-34.1785],[120.0535,-34.157],[120.121,-34.164],[120.213,-34.1495],[120.262,-34.2275],[120.3685,-34.2875],[120.488,-34.295],[120.6225,-34.25],[120.6775,-34.2045],[120.7745,-34.2625],[120.86,-34.278],[120.962,-34.264],[121.025,-34.2335],[121.085,-34.173],[121.115,-34.072],[121.187,-34.0785],[121.2535,-34.068],[121.416,-34.175],[121.4455,-34.2735],[121.5065,-34.334],[121.623,-34.3755],[121.677,-34.4475],[121.7445,-34.488],[121.765,-34.554],[121.8475,-34.6365],[121.9105,-34.663],[122.0105,-34.674],[122.115,-34.648],[122.207,-34.5725],[122.306,-34.558],[122.3825,-34.5175],[122.437,-34.453],[122.4545,-34.4025],[122.5515,-34.393],[122.8575,-34.334],[123.132,-34.3675],[123.243,-34.351],[123.286,-34.363],[123.3185,-34.4745],[123.388,-34.539],[123.4775,-34.5735],[123.593,-34.5735],[123.7115,-34.519],[123.777,-34.4485],[123.804,-34.3515],[123.854,-34.3075],[123.9895,-34.2905],[124.0545,-34.2535],[124.117,-34.1875],[124.139,-34.1395],[124.144,-34.0655],[124.232,-34.0495],[124.3295,-33.9875],[124.3925,-33.901],[124.4155,-33.826],[124.404,-33.6975],[124.3585,-33.601],[124.312,-33.561],[124.225,-33.525],[124.2445,-33.4395],[124.3015,-33.325],[124.3245,-33.2495],[124.392,-33.1735],[124.5065,-33.1325],[124.7265,-33.108],[124.834,-33.0865],[125.0105,-33.01],[125.094,-32.9455],[125.2165,-32.892],[125.4025,-32.7965],[125.5275,-32.7695],[125.693,-32.701],[125.8325,-32.6],[125.93,-32.5375],[126.0485,-32.4765],[126.118,-32.474],[126.2435,-32.453],[126.3035,-32.471],[126.535,-32.5045],[126.6765,-32.5135],[126.8095,-32.499],[127.0395,-32.499],[127.1255,-32.4825],[127.3085,-32.47],[127.4315,-32.438],[127.5645,-32.4165],[127.6815,-32.364],[127.794,-32.327],[128.0715,-32.273],[128.428,-32.158],[128.7685,-32.0245],[128.854,-31.973],[129.0985,-31.873],[129.4675,-31.836],[129.5545,-31.823],[129.8735,-31.8065],[129.95,-31.791],[130.1315,-31.787],[130.2665,-31.776],[130.5015,-31.7935],[130.5715,-31.789],[130.781,-31.808],[130.911,-31.7905],[131.0705,-31.7395],[131.184,-31.6905],[131.3955,-31.776],[131.5615,-31.854],[131.6955,-31.933],[131.6985,-32.017],[131.7485,-32.1075],[131.82,-32.1575],[131.8895,-32.1785],[131.9165,-32.2425],[131.964,-32.2925],[132.015,-32.3225],[132.0955,-32.343],[132.153,-32.3425],[132.232,-32.321],[132.291,-32.2885],[132.354,-32.2295],[132.4005,-32.2225],[132.8525,-32.3075],[132.9165,-32.35],[132.984,-32.4885],[132.916,-32.5695],[132.899,-32.6395],[132.9065,-32.697],[132.9395,-32.7585],[133.0285,-32.825],[133.1225,-32.8465],[133.2565,-32.848],[133.372,-32.813],[133.7945,-32.8975],[133.856,-33.026],[133.9305,-33.1325],[134.0265,-33.2255],[134.0885,-33.308],[134.1775,-33.3585],[134.3435,-33.513],[134.307,-33.5345],[134.2405,-33.5375],[134.1545,-33.565],[134.0675,-33.642],[134.042,-33.7035],[134.045,-33.7845],[134.063,-33.826],[134.03,-33.882],[134.002,-33.9735],[134.002,-34.0365],[134.0305,-34.108],[134.0685,-34.1505],[134.1645,-34.2005],[134.218,-34.2095],[134.3055,-34.203],[134.419,-34.1495],[134.481,-34.075],[134.5175,-33.966],[134.612,-33.9255],[134.6875,-33.919],[134.8035,-33.8585],[134.8735,-33.923],[134.8775,-33.983],[134.91,-34.0505],[135.0,-34.1235],[134.964,-34.354],[134.9155,-34.402],[134.8885,-34.4535],[134.81,-34.439],[134.7045,-34.4455],[134.5925,-34.505],[134.544,-34.5705],[134.532,-34.6745],[134.487,-34.735],[134.469,-34.8285],[134.489,-34.894],[134.5245,-34.942],[134.609,-34.996],[134.6555,-35.01],[134.764,-35.0115],[134.827,-34.9935],[134.895,-34.951],[134.98,-34.9815],[135.0865,-34.983],[135.209,-34.929],[135.2895,-34.911],[135.3065,-34.987],[135.435,-35.1435],[135.555,-35.2025],[136.0245,-35.6875],[136.3005,-35.9695],[136.3935,-36.0825],[136.449,-36.1225],[136.4715,-36.1745],[136.5305,-36.237],[136.6365,-36.2815],[136.693,-36.3245],[136.7675,-36.352],[136.864,-36.357],[137.0195,-36.306],[136.9955,-36.3625],[136.997,-36.4395],[137.03,-36.506],[137.139,-36.5815],[137.23,-36.5985],[137.3655,-36.571],[137.4435,-36.52],[137.486,-36.4765],[137.522,-36.3835],[137.5175,-36.329],[137.5905,-36.327],[137.696,-36.2895],[137.763,-36.2275],[137.7945,-36.138],[137.847,-36.0705],[138.032,-36.1055],[138.127,-36.093],[138.2455,-36.024],[138.3995,-35.964],[138.4905,-35.899],[138.6855,-35.7955],[138.883,-35.7955],[139.054,-35.9085],[139.2195,-36.0515],[139.3195,-36.157],[139.4325,-36.3095],[139.4825,-36.394],[139.573,-36.588],[139.4915,-36.765],[139.437,-36.792],[139.3755,-36.851],[139.3505,-36.903],[139.3465,-36.9815],[139.3625,-37.0325],[139.4225,-37.104],[139.4755,-37.1345],[139.4795,-37.2005],[139.5065,-37.2695],[139.6755,-37.5075],[139.7985,-37.6145],[139.8685,-37.6895],[140.0165,-37.777],[140.1005,-37.9085],[140.1355,-37.987],[140.22,-38.0705],[140.3255,-38.126],[140.3955,-38.178],[140.572,-38.2505],[140.6725,-38.2635],[140.9155,-38.2535],[141.0065,-38.2935],[141.1105,-38.3595],[141.116,-38.4215],[141.152,-38.488],[141.2315,-38.5525],[141.312,-38.5825],[141.5065,-38.631],[141.574,-38.6315],[141.7295,-38.6055],[141.8235,-38.571],[141.95,-38.622],[142.068,-38.618],[142.1385,-38.593],[142.247,-38.5985],[142.3495,-38.5815],[142.444,-38.605],[142.605,-38.713],[142.721,-38.775],[142.796,-38.8035],[142.956,-38.8325],[143.007,-38.883],[143.0865,-38.9335],[143.231,-38.972],[143.294,-38.9715],[143.3615,-39.0215],[143.454,-39.0535],[143.5135,-39.0585],[143.635,-39.047],[143.699,-39.022],[143.888,-38.889],[144.005,-38.8455],[144.059,-38.804],[144.2075,-38.6535],[144.417,-38.55],[144.499,-38.492],[144.536,-38.49],[144.6445,-38.564],[144.7245,-38.6565],[144.781,-38.6845],[144.882,-38.703],[144.964,-38.699],[145.05,-38.7255],[145.208,-38.7335],[145.35,-38.768],[145.461,-38.846],[145.569,-38.8795],[145.641,-38.8805],[145.666,-38.957],[145.705,-39.0195],[145.786,-39.081],[145.8645,-39.106],[145.968,-39.108],[145.9935,-39.1865],[146.0395,-39.2465],[146.1535,-39.327],[146.254,-39.4085],[146.3315,-39.5075],[146.3925,-39.5385],[146.434,-39.629],[146.5125,-39.69],[146.6325,-39.7215],[146.7465,-39.709],[146.834,-39.666],[146.889,-39.606],[146.956,-39.6755],[147.025,-39.7095],[146.996,-39.7535],[146.9815,-39.8425],[147.0095,-39.9145],[147.0735,-39.976],[147.202,-40.02],[147.319,-40.0125],[147.423,-39.964],[147.47,-40.014],[147.46,-40.146],[147.539,-40.3725],[147.6005,-40.444],[147.773,-40.584],[147.58,-40.5845],[147.4725,-40.625],[147.423,-40.667],[147.2905,-40.6325],[147.186,-40.641],[147.107,-40.6715],[147.0275,-40.744],[146.9515,-40.743],[146.8625,-40.764],[146.7595,-40.8365],[146.624,-40.867],[146.5195,-40.918],[146.334,-40.954],[146.27,-40.9505],[146.1,-40.889],[145.96,-40.846],[145.896,-40.7895],[145.758,-40.745],[145.6795,-40.6775],[145.6185,-40.6525],[145.531,-40.64],[145.4805,-40.5865],[145.21,-40.3345],[145.1795,-40.287],[145.071,-40.2115],[144.982,-40.192],[144.8905,-40.188],[144.786,-40.197],[144.714,-40.1755],[144.6035,-40.1745],[144.5185,-40.2],[144.43,-40.2465],[144.407,-40.163],[144.3545,-40.1045],[144.386,-40.027],[144.398,-39.917],[144.423,-39.855],[144.4215,-39.768],[144.369,-39.6865],[144.354,-39.603],[144.324,-39.557],[144.148,-39.412],[144.0175,-39.361],[143.8565,-39.362],[143.7705,-39.3725],[143.665,-39.4255],[143.63,-39.462],[143.567,-39.6095],[143.5545,-39.6715],[143.5925,-39.817],[143.57,-39.9175],[143.6015,-40.0225],[143.599,-40.0965],[143.6255,-40.1765],[143.677,-40.25],[143.775,-40.33],[143.858,-40.359],[143.9305,-40.3655],[144.005,-40.433],[144.0775,-40.462],[144.1035,-40.5605],[144.197,-40.641],[144.2805,-40.669],[144.3685,-40.6745],[144.4105,-40.7135],[144.423,-40.7775],[144.3585,-40.8725],[144.3395,-41.003],[144.353,-41.074],[144.3995,-41.146],[144.4075,-41.2365],[144.4795,-41.393],[144.4795,-41.4395],[144.5125,-41.52],[144.6305,-41.6535],[144.6465,-41.74],[144.6835,-41.821],[144.777,-41.895],[144.8705,-41.9875],[144.9635,-42.062],[144.909,-42.132],[144.896,-42.1825],[144.9025,-42.3105],[144.927,-42.4025],[144.9545,-42.446],[144.9635,-42.56],[145.002,-42.679],[145.0435,-42.744],[145.105,-42.7915],[145.141,-42.8785],[145.153,-42.943],[145.2205,-43.026],[145.25,-43.088],[145.3025,-43.1375],[145.4195,-43.1855],[145.501,-43.265],[145.578,-43.417],[145.6575,-43.4765],[145.676,-43.511],[145.754,-43.5785],[145.7725,-43.6435],[145.8095,-43.692],[145.8965,-43.747],[146.136,-43.847],[146.2075,-43.903],[146.314,-43.938],[146.387,-43.9425],[146.4885,-43.9245],[146.605,-43.852],[146.6975,-43.848],[146.704,-43.906],[146.7445,-43.9735],[146.8545,-44.0415],[146.979,-44.061],[147.0805,-44.048],[147.203,-44.002],[147.2585,-43.9565],[147.2985,-43.8815],[147.301,-43.83],[147.2685,-43.752],[147.423,-43.712],[148.136,-43.422],[148.219,-43.371],[148.2645,-43.313],[148.3265,-43.155],[148.431,-42.7065],[148.597,-42.415],[148.6155,-42.3465],[148.6245,-42.084],[148.574,-41.996],[148.5895,-41.943],[148.5645,-41.796],[148.5665,-41.7025],[148.5875,-41.5975],[148.554,-41.48],[148.6035,-41.411],[148.6335,-41.269],[148.6025,-41.1925],[148.556,-41.1325],[148.601,-41.0775],[148.621,-40.985],[148.5805,-40.8555],[148.4755,-40.723],[148.518,-40.675],[148.585,-40.6305],[148.689,-40.582],[148.742,-40.5235],[148.7655,-40.4365],[148.7495,-40.3665],[148.7045,-40.2945],[148.6575,-40.2465],[148.657,-40.1915],[148.6305,-40.1275],[148.5785,-40.0745],[148.6245,-39.979],[148.613,-39.889],[148.571,-39.8255],[148.472,-39.762],[148.381,-39.7355],[148.2905,-39.7335],[148.26,-39.7065],[148.2695,-39.6315],[148.2345,-39.5475],[148.186,-39.499],[148.0835,-39.4505],[147.993,-39.4365],[147.9205,-39.442],[147.832,-39.4735],[147.746,-39.4305],[147.6355,-39.403],[147.6145,-39.3555],[147.5505,-39.291],[147.436,-39.246],[147.282,-39.235],[147.274,-39.128],[147.221,-39.058],[147.162,-39.02],[147.0705,-38.994],[146.955,-38.999],[146.9625,-38.95],[146.94,-38.867],[147.014,-38.827],[147.2455,-38.6195],[147.653,-38.2965],[147.852,-38.1625],[148.027,-38.087],[148.2605,-38.022],[148.4605,-38.0025],[148.657,-38.008],[148.792,-38.0195],[148.9595,-37.9815],[149.149,-37.98],[149.257,-38.006],[149.323,-38.006],[149.42,-37.979],[149.546,-37.971],[149.615,-37.9485],[149.813,-37.8545],[149.906,-37.7705],[150.0005,-37.7595],[150.11,-37.7005],[150.209,-37.583],[150.2295,-37.5085],[150.2225,-37.41],[150.2895,-37.327],[150.2985,-37.2255],[150.2475,-37.0885],[150.1985,-37.0135],[150.191,-36.9065],[150.2425,-36.727],[150.308,-36.6085],[150.313,-36.5175],[150.3325,-36.441],[150.404,-36.4005],[150.467,-36.314],[150.471,-36.197],[150.434,-36.1295],[150.3915,-36.0915],[150.41,-35.977],[150.4585,-35.92],[150.482,-35.84],[150.5665,-35.699],[150.643,-35.618],[150.6655,-35.513],[150.71,-35.462],[150.735,-35.398],[150.8185,-35.374],[150.9015,-35.3315],[150.9625,-35.2775],[150.9905,-35.228],[151.051,-35.1655],[151.0915,-35.037],[151.0845,-34.9595],[151.036,-34.882],[151.103,-34.7165],[151.146,-34.633],[151.188,-34.471],[151.174,-34.3975],[151.195,-34.349],[151.2975,-34.28],[151.4195,-34.153],[151.4645,-34.093],[151.498,-34.022],[151.546,-33.835],[151.564,-33.706],[151.6595,-33.578],[151.721,-33.448],[151.792,-33.3745],[151.8545,-33.2615],[151.876,-33.195],[151.9535,-33.0765],[152.017,-33.0075],[152.065,-32.9915],[152.173,-32.9875],[152.337,-32.9165],[152.385,-32.881],[152.4485,-32.8015],[152.508,-32.763],[152.566,-32.674],[152.6415,-32.658],[152.736,-32.5945],[152.777,-32.527],[152.789,-32.475],[152.7765,-32.3965],[152.785,-32.305],[152.8065,-32.2115],[152.778,-32.1195],[152.8175,-32.0515],[152.909,-31.996],[152.9735,-31.917],[152.989,-31.855],[153.0765,-31.692],[153.084,-31.633],[153.15,-31.561],[153.1675,-31.5155],[153.17,-31.4315],[153.2065,-31.351],[153.213,-31.227],[153.251,-31.1885],[153.2935,-31.0975],[153.335,-30.931],[153.2955,-30.8105],[153.2325,-30.7295],[153.2655,-30.551],[153.374,-30.3855],[153.447,-30.3345],[153.4865,-30.2745],[153.499,-30.183],[153.478,-30.119],[153.549,-30.0805],[153.6095,-30.002],[153.6235,-29.932],[153.6075,-29.8565],[153.5415,-29.7645],[153.5615,-29.703],[153.5725,-29.607],[153.5965,-29.5095],[153.6015,-29.333],[153.66,-29.2785],[153.6945,-29.1875],[153.68,-29.094],[153.7895,-28.9765],[153.828,-28.8995],[153.8335,-28.791],[153.8615,-28.677],[153.8655,-28.6165],[153.8355,-28.5275],[153.7855,-28.466],[153.807,-28.3735],[153.8125,-28.247],[153.8015,-28.154],[153.7665,-28.0805],[153.7,-28.0115],[153.6575,-27.985],[153.653,-27.8875],[153.7,-27.6305],[153.7635,-27.4945],[153.78,-27.4115],[153.772,-27.346],[153.701,-27.044],[153.714,-26.9785],[153.696,-26.899],[153.6555,-26.8415],[153.564,-26.7875],[153.475,-26.7765],[153.3625,-26.696],[153.36,-26.6455],[153.321,-26.5035],[153.341,-26.415],[153.3305,-26.3135],[153.2975,-26.258],[153.3405,-26.127],[153.404,-25.9955],[153.408,-25.887],[153.383,-25.83],[153.3105,-25.76],[153.3,-25.687],[153.3545,-25.5365],[153.4135,-25.404],[153.567,-25.0775],[153.579,-25.0275],[153.567,-24.923],[153.544,-24.8675],[153.4815,-24.8015],[153.4915,-24.6905],[153.4615,-24.5715],[153.3685,-24.484],[153.3205,-24.464],[153.2385,-24.456],[153.167,-24.4725],[153.1095,-24.5065],[152.4755,-24.5475],[152.3805,-24.5275],[152.286,-24.4615],[152.2185,-24.3725],[152.15,-24.1735],[151.9675,-23.8795],[151.9175,-23.8235],[151.8435,-23.785],[151.7615,-23.761],[151.862,-23.727],[151.9585,-23.7925],[152.02,-23.8625],[152.0875,-23.896],[152.125,-23.9475],[152.183,-23.9895],[152.213,-24.037],[152.2625,-24.08],[152.3565,-24.115],[152.4385,-24.1165],[152.4915,-24.104],[152.4965,-24.159],[152.531,-24.2315],[152.607,-24.2935],[152.7005,-24.3185],[152.7825,-24.3095],[152.8505,-24.2785],[152.903,-24.229],[152.9415,-24.1295],[152.9315,-24.05],[152.8635,-23.956],[152.7855,-23.916],[152.7275,-23.9065],[152.6495,-23.916],[152.6335,-23.8175],[152.572,-23.737],[152.4885,-23.674],[152.4405,-23.607],[152.398,-23.5755],[152.373,-23.5165],[152.308,-23.45],[152.2425,-23.3175],[152.186,-23.2565],[152.116,-23.101],[152.053,-23.0245],[151.9795,-22.9865],[151.925,-22.977],[151.8475,-22.984],[151.741,-23.043],[151.6985,-23.055],[151.5475,-23.146],[151.4855,-23.2445],[151.479,-23.3255],[151.5105,-23.4075],[151.4755,-23.446],[151.3815,-23.321],[151.2965,-23.102],[151.212,-22.7025],[151.1515,-22.5575],[151.121,-22.5185],[150.965,-22.3715],[150.9315,-22.2865],[150.8955,-22.0905],[150.916,-21.9815],[150.916,-21.9245],[150.882,-21.8405],[150.8205,-21.7765],[150.6045,-21.626],[150.555,-21.58],[150.5095,-21.3935],[150.471,-21.335],[150.395,-21.2555],[150.426,-21.2165],[150.471,-21.1045],[150.4725,-21.0355],[150.4555,-20.983],[150.4085,-20.92],[150.3675,-20.8905],[150.283,-20.864],[150.1795,-20.754],[150.069,-20.7025],[149.959,-20.7105],[149.8725,-20.763],[149.7975,-20.645],[149.7535,-20.602],[149.6705,-20.565],[149.5375,-20.5365],[149.3575,-20.455],[149.395,-20.2895],[149.3895,-20.196],[149.322,-20.096],[149.109,-19.9115],[149.0395,-19.8715],[148.912,-19.837],[148.6765,-19.8165],[148.571,-19.787],[148.5695,-19.673],[148.523,-19.595],[148.4815,-19.56],[148.3995,-19.526],[148.3215,-19.5245],[148.245,-19.5515],[148.1775,-19.6145],[148.1525,-19.667],[148.046,-19.6595],[148.0085,-19.5985],[147.9575,-19.5535],[147.895,-19.5205],[147.819,-19.508],[147.761,-19.472],[147.672,-19.3415],[147.6435,-19.268],[147.604,-19.2105],[147.5475,-19.154],[147.4975,-19.125],[147.2105,-19.033],[147.163,-19.0005],[147.045,-18.9665],[146.982,-18.837],[147.084,-18.845],[147.145,-18.828],[147.2505,-18.7525],[147.3245,-18.717],[147.3865,-18.6485],[147.413,-18.568],[147.41,-18.5055],[147.3435,-18.339],[147.315,-18.2905],[147.26,-18.24],[147.1705,-18.207],[147.1855,-18.122],[147.1785,-18.077],[147.1325,-17.995],[147.082,-17.9545],[146.998,-17.8625],[146.963,-17.802],[146.8755,-17.7025],[146.8325,-17.6355],[146.756,-17.585],[146.6705,-17.4765],[146.5845,-17.426],[146.512,-17.4165],[146.4415,-17.4315],[146.3155,-17.5035],[146.2945,-17.471],[146.282,-17.401],[146.3785,-17.383],[146.463,-17.3205],[146.4955,-17.2675],[146.5135,-17.173],[146.485,-17.0725],[146.4995,-17.001],[146.541,-16.948],[146.5635,-16.875],[146.546,-16.771],[146.4955,-16.703],[146.4115,-16.6245],[146.307,-16.58],[146.2125,-16.4155],[146.198,-16.35],[146.1505,-16.2785],[146.0885,-16.2365],[145.964,-16.1995],[145.941,-16.0635],[145.914,-16.008],[145.869,-15.9595],[145.8475,-15.894],[145.8125,-15.846],[145.8255,-15.7515],[145.846,-15.719],[145.867,-15.635],[145.855,-15.562],[145.807,-15.4855],[145.7515,-15.438],[145.7565,-15.359],[145.8,-15.314],[145.8265,-15.2605],[145.8455,-15.168],[145.891,-15.116],[145.92,-15.049],[145.9415,-14.895],[145.9405,-14.8275],[145.921,-14.7215],[145.8925,-14.6295],[145.773,-14.4315],[145.669,-14.3185],[145.511,-14.2105],[145.2985,-14.1095],[145.2065,-14.083],[145.1465,-14.0525],[145.054,-14.0305],[144.993,-14.0015],[144.979,-13.944],[144.9115,-13.856],[144.828,-13.814],[144.789,-13.773],[144.713,-13.732],[144.581,-13.7165],[144.5045,-13.741],[144.4205,-13.6145],[144.346,-13.553],[144.3105,-13.4695],[144.2365,-13.3355],[144.199,-13.298],[144.1775,-13.1765],[144.184,-13.071],[144.1555,-12.9825],[144.1165,-12.9195],[144.0595,-12.875],[144.06,-12.8185],[144.0305,-12.695],[143.994,-12.623],[144.005,-12.5425],[144.0685,-12.42],[144.1315,-12.329],[144.153,-12.1925],[144.187,-12.063],[144.216,-11.9895],[144.2735,-11.9205],[144.3135,-11.7655],[144.3015,-11.6665],[144.2725,-11.611],[144.297,-11.5405],[144.298,-11.4845],[144.261,-11.354],[144.282,-11.2595],[144.2775,-11.187],[144.2575,-11.12],[144.191,-11.039],[144.127,-11.008],[144.0595,-10.9965],[143.9705,-11.008],[143.909,-11.044],[143.853,-11.1195],[143.7905,-11.116],[143.707,-11.1425],[143.639,-11.203],[143.5885,-11.3275],[143.51,-11.375],[143.4625,-11.2815],[143.4725,-11.0935],[143.604,-11.096],[143.654,-11.078],[143.708,-11.0375],[143.7445,-10.9855],[143.8025,-10.9415],[143.8475,-10.8665],[143.8575,-10.793],[143.849,-10.742],[143.8745,-10.656],[143.869,-10.592],[143.8365,-10.523],[143.7665,-10.4615],[143.699,-10.439],[143.66,-10.38],[143.698,-10.286],[143.736,-10.269],[143.779,-10.2985],[143.882,-10.321],[143.956,-10.35],[144.028,-10.352],[144.083,-10.3405],[144.1815,-10.2735],[144.2245,-10.1865],[144.3265,-10.0775],[144.35,-10.0265],[144.404,-9.984],[144.45,-9.902],[144.4545,-9.8085],[144.4005,-9.6755],[144.3025,-9.5765],[144.256,-9.5495],[144.251,-9.4985],[143.801,-9.365],[143.501,-9.3985],[143.3345,-9.5485],[143.346,-9.501],[143.3025,-9.46],[143.238,-9.495],[143.2485,-9.5485],[143.0845,-9.5485],[143.001,-9.665],[142.3845,-9.6985],[142.001,-9.765],[141.9455,-9.7995],[141.9075,-9.8475],[141.8795,-9.923],[141.858,-10.0225],[141.849,-10.174],[141.834,-10.255],[141.837,-10.311],[141.7245,-10.5175],[141.7065,-10.5765],[141.707,-10.635],[141.7305,-10.7035],[141.812,-10.8815],[141.8,-10.942],[141.819,-11.0665],[141.861,-11.1465],[141.9155,-11.1905],[141.9095,-11.345],[141.861,-11.463],[141.8465,-11.53],[141.807,-11.6155],[141.719,-11.66],[141.672,-11.7185],[141.6555,-11.761],[141.6505,-11.8355],[141.664,-11.8865],[141.627,-11.954],[141.569,-12.0885],[141.521,-12.1585],[141.464,-12.3435],[141.391,-12.492],[141.379,-12.538],[141.39,-12.923],[141.375,-13.004],[141.442,-13.2445],[141.4135,-13.344],[141.3595,-13.426],[141.3075,-13.588],[141.287,-13.7335],[141.2625,-13.8455],[141.2625,-13.9345],[141.2925,-14.034],[141.378,-14.1495],[141.3865,-14.2345],[141.3535,-14.316],[141.3205,-14.4595],[141.321,-14.514],[141.3475,-14.621],[141.3595,-14.7535],[141.393,-14.918],[141.432,-15.012],[141.3685,-15.141],[141.3675,-15.2255],[141.342,-15.2715],[141.3285,-15.3405],[141.2795,-15.444],[141.227,-15.6335],[141.2115,-15.7915],[141.173,-15.865],[141.166,-15.9265],[141.2,-16.059],[141.15,-16.133],[141.124,-16.2325],[141.068,-16.325],[141.0595,-16.4335],[141.015,-16.5645],[140.9925,-16.6055],[140.904,-16.6795],[140.867,-16.783],[140.8015,-16.836],[140.7215,-16.9865],[140.708,-17.0505],[140.7145,-17.117],[140.6885,-17.2385],[140.6095,-17.2775],[140.5,-17.15],[140.4835,-17.1665],[140.5915,-17.293],[140.525,-17.369],[140.4555,-17.3905],[140.397,-17.4335],[140.3135,-17.473],[140.194,-17.463],[140.09,-17.479],[140.0055,-17.4325],[139.917,-17.369],[139.7665,-17.322],[139.807,-17.282],[139.841,-17.216],[139.8575,-17.1085],[140.012,-16.829],[140.068,-16.771],[140.094,-16.719],[140.108,-16.643],[140.0885,-16.5585],[139.998,-16.3945],[139.93,-16.308],[139.893,-16.276],[139.809,-16.2425],[139.765,-16.239],[139.7,-16.2115],[139.631,-16.202],[139.358,-16.0555],[139.283,-16.03],[139.17,-16.0455],[139.0835,-16.111],[139.0515,-16.1725],[139.0425,-16.228],[139.035,-16.438],[138.982,-16.504],[138.913,-16.543],[138.8505,-16.626],[138.6915,-16.5675],[138.634,-16.5585],[138.5485,-16.5715],[138.4905,-16.543],[138.397,-16.521],[138.321,-16.482],[138.2415,-16.4755],[138.092,-16.3275],[138.0335,-16.2885],[137.9555,-16.19],[137.8975,-16.0895],[137.8275,-16.04],[137.7285,-16.0185],[137.649,-15.9845],[137.525,-15.9545],[137.461,-15.9305],[137.419,-15.875],[137.3245,-15.8205],[137.302,-15.584],[137.282,-15.526],[137.2415,-15.473],[137.085,-15.3405],[136.9735,-15.2885],[136.887,-15.286],[136.836,-15.3015],[136.613,-15.2935],[136.3495,-15.218],[136.32,-15.1865],[136.2865,-15.0625],[136.246,-15.012],[136.173,-14.97],[136.091,-14.907],[135.9775,-14.8835],[135.9755,-14.8345],[135.9465,-14.757],[135.972,-14.7125],[135.9885,-14.6105],[136.073,-14.5575],[136.115,-14.499],[136.136,-14.4265],[136.345,-14.4465],[136.4095,-14.43],[136.477,-14.448],[136.5545,-14.4955],[136.696,-14.525],[136.728,-14.5855],[136.7785,-14.6325],[136.8425,-14.66],[136.924,-14.664],[136.9935,-14.6405],[137.053,-14.592],[137.096,-14.4995],[137.144,-14.4315],[137.189,-14.2945],[137.191,-14.157],[137.138,-13.8315],[137.1385,-13.791],[137.177,-13.6265],[137.1665,-13.556],[137.1175,-13.478],[136.897,-13.01],[136.931,-12.933],[136.929,-12.7765],[136.949,-12.701],[137.0085,-12.6695],[137.065,-12.603],[137.085,-12.5445],[137.161,-12.4655],[137.1845,-12.382],[137.1785,-12.2965],[137.085,-11.9895],[137.0355,-11.583],[136.9715,-11.032],[136.96,-10.952],[136.9305,-10.8885],[136.855,-10.822],[136.7335,-10.796],[136.6655,-10.808],[136.5945,-10.852],[136.555,-10.903],[136.498,-11.049],[136.4615,-11.087],[136.3845,-11.2235],[136.3205,-11.2915],[136.049,-11.3505],[135.9975,-11.3755],[135.912,-11.441],[135.8615,-11.458],[135.793,-11.5125],[135.7585,-11.5735],[135.6735,-11.666],[135.649,-11.7305],[135.564,-11.7435],[135.5135,-11.7385],[135.4885,-11.6325],[135.4145,-11.533],[135.293,-11.433],[135.237,-11.412],[135.1775,-11.408],[135.1055,-11.4265],[135.031,-11.485],[134.955,-11.5955],[134.9385,-11.644],[134.9375,-11.7165],[134.7815,-11.7395],[134.6795,-11.7095],[134.6115,-11.7185],[134.536,-11.76],[134.459,-11.8485],[134.364,-11.7465],[134.3045,-11.7015],[134.12,-11.656],[134.022,-11.562],[133.8885,-11.5195],[133.818,-11.5155],[133.6965,-11.544],[133.6975,-11.484],[133.649,-11.3665],[133.6,-11.311],[133.517,-11.2685],[133.4055,-11.272],[133.311,-11.303],[133.2665,-11.3275],[133.2195,-11.3765],[133.1255,-11.269],[133.206,-11.157],[133.223,-11.075],[133.217,-11.015],[133.246,-10.9405],[133.233,-10.828],[133.187,-10.76],[133.1245,-10.719],[133.0235,-10.702],[132.9595,-10.7145],[132.8775,-10.769],[132.8425,-10.764],[132.576,-10.763],[132.4825,-10.7885],[132.434,-10.8195],[132.382,-10.8325],[132.233,-10.9025],[132.066,-10.9215],[131.9435,-10.9205],[131.8245,-10.957],[131.2885,-10.9855],[131.2165,-10.992],[131.159,-11.0115],[131.1055,-11.0505],[131.0095,-11.0805],[130.9465,-11.0645],[130.882,-11.069],[130.8285,-11.0895],[130.77,-11.138],[130.696,-11.0855],[130.609,-11.0595],[130.4415,-10.915],[130.368,-10.8905],[130.3075,-10.8885],[130.2355,-10.91],[130.144,-10.991],[130.119,-11.049],[130.067,-11.2275],[130.0435,-11.2725],[129.9685,-11.3755],[129.9415,-11.4635],[129.8715,-11.6285],[129.827,-11.697],[129.816,-11.81],[129.836,-11.8815],[129.8705,-11.935],[130.0925,-12.5855],[130.108,-12.6805],[130.0575,-12.7035],[130.0045,-12.751],[129.9645,-12.8085],[129.93,-12.886],[129.8875,-12.909],[129.8155,-12.993],[129.785,-13.104],[129.7915,-13.187],[129.738,-13.2905],[129.6615,-13.3665],[129.626,-13.4175],[129.6005,-13.501],[129.603,-13.553],[129.569,-13.6005],[129.5495,-13.661],[129.4725,-13.6365],[129.401,-13.6405],[129.3075,-13.6895],[129.2545,-13.7655],[129.1945,-13.8205],[129.1625,-13.8855],[129.1585,-13.972],[129.204,-14.0675],[129.244,-14.1035],[129.2045,-14.176],[129.1625,-14.2805],[129.147,-14.36],[129.0815,-14.449],[128.993,-14.4285],[128.938,-14.435],[128.9025,-14.406],[128.8225,-14.374],[128.7745,-14.3715],[128.6765,-14.401],[128.6205,-14.45],[128.5885,-14.5075],[128.5335,-14.4195],[128.4845,-14.386],[128.374,-14.363],[128.2915,-14.3845],[128.2355,-14.3425],[128.1375,-14.316],[128.05,-14.333],[128.0035,-14.2465],[127.9415,-14.1865],[127.878,-14.102],[127.7045,-13.9405],[127.6555,-13.912],[127.6095,-13.834],[127.556,-13.7785],[127.48,-13.742],[127.41,-13.6515],[127.3295,-13.6095],[127.2495,-13.603],[127.1565,-13.632],[127.074,-13.544],[127.005,-13.496],[126.935,-13.479],[126.826,-13.4975],[126.765,-13.5295],[126.399,-13.548],[126.3165,-13.542],[126.2785,-13.4495],[126.188,-13.3805],[126.115,-13.366],[126.092,-13.3175],[126.0395,-13.2625],[125.973,-13.2315],[125.858,-13.2335],[125.778,-13.279],[125.746,-13.317],[125.718,-13.3835],[125.657,-13.407],[125.5945,-13.4635],[125.558,-13.5545],[125.56,-13.619],[125.5935,-13.6955],[125.467,-13.789],[125.425,-13.863],[125.311,-13.858],[125.2345,-13.893],[125.103,-13.997],[125.065,-14.0755],[125.0305,-14.0985],[124.976,-14.1645],[124.8525,-14.2205],[124.7955,-14.2315],[124.7065,-14.2905],[124.651,-14.3775],[124.632,-14.4805],[124.651,-14.558],[124.551,-14.7235],[124.441,-14.803],[124.23,-14.867],[124.165,-14.9185],[124.1405,-14.956],[124.078,-14.991],[124.0125,-15.0875],[123.969,-15.112],[123.916,-15.1665],[123.8865,-15.232],[123.8895,-15.3425],[123.811,-15.421],[123.8,-15.404],[123.819,-15.3275],[123.811,-15.264],[123.752,-15.172],[123.6865,-15.1325],[123.5475,-15.096],[123.487,-15.096],[123.4085,-15.12],[123.3605,-15.1545],[123.312,-15.2275],[123.2165,-15.23],[123.1325,-15.208],[123.081,-15.207],[123.002,-15.2305],[122.919,-15.3065],[122.8895,-15.3745],[122.884,-15.4805],[122.8985,-15.566],[122.936,-15.648],[123.018,-15.7355],[123.1235,-15.792],[123.2335,-15.8025],[123.18,-15.8425],[123.056,-15.912],[123.0015,-15.966],[122.966,-16.0725],[122.9145,-16.129],[122.765,-16.251],[122.6785,-16.388],[122.5755,-16.483],[122.5305,-16.565],[122.4545,-16.6],[122.408,-16.643],[122.356,-16.7275],[122.2985,-16.6775],[122.2265,-16.6455],[122.138,-16.6315],[122.004,-16.6535],[121.952,-16.681],[121.892,-16.749],[121.8665,-16.8345],[121.872,-16.8935],[121.9065,-16.966],[122.018,-17.062],[122.006,-17.1325],[121.955,-17.239],[121.934,-17.327],[121.929,-17.472],[121.9355,-17.582],[121.976,-17.6885],[121.977,-17.824],[121.99,-17.8685],[121.974,-17.91],[121.942,-18.161],[121.875,-18.232],[121.7795,-18.2445],[121.6825,-18.2845],[121.542,-18.431],[121.4965,-18.524],[121.428,-18.5745],[121.382,-18.6635],[121.3815,-18.7455],[121.4005,-18.7975],[121.3545,-18.8685],[121.3045,-19.0045],[121.117,-19.229],[121.076,-19.271],[120.9335,-19.3885],[120.8315,-19.451],[120.598,-19.5525],[120.4185,-19.6135],[120.3205,-19.6255],[120.211,-19.686],[120.058,-19.687],[119.9085,-19.7145],[119.7975,-19.755],[119.71,-19.7605],[119.5855,-19.8185],[119.402,-19.7785],[119.338,-19.7395],[119.27,-19.7235],[119.2985,-19.6795],[119.32,-19.592],[119.309,-19.517],[119.2725,-19.4545],[119.179,-19.389],[119.0585,-19.3765],[118.9755,-19.4045],[118.9145,-19.4575],[118.885,-19.509],[118.872,-19.576],[118.8915,-19.667],[118.791,-19.693],[118.701,-19.7715],[118.6665,-19.8535],[118.611,-19.9155],[118.584,-20.002],[118.5905,-20.0675],[118.4665,-20.084],[118.308,-20.114],[118.2495,-20.135],[118.1455,-20.129],[118.0515,-20.1685],[118.0135,-20.2045],[117.919,-20.252],[117.8535,-20.271],[117.777,-20.3175],[117.6895,-20.3915],[117.5195,-20.479],[117.4025,-20.501],[117.252,-20.292],[117.2,-20.2555],[117.1015,-20.228],[117.0265,-20.225],[116.9225,-20.167],[116.865,-20.152],[116.771,-20.16],[116.495,-20.272],[115.6895,-20.2095],[115.5675,-20.151],[115.474,-20.081],[115.4025,-20.066],[115.298,-20.086],[115.247,-20.1185],[115.1875,-20.1855],[115.1645,-20.2435],[115.1625,-20.3135],[115.1865,-20.382],[115.2445,-20.4455],[115.2295,-20.578],[115.151,-20.661],[115.107,-20.765],[115.091,-20.885],[115.067,-20.932],[114.868,-21.124],[114.64,-21.3495],[114.462,-21.4005],[114.315,-21.4335],[114.236,-21.4745],[114.1465,-21.561],[114.0815,-21.579],[114.0285,-21.612],[113.891,-21.6755],[113.844,-21.7045],[113.764,-21.781],[113.718,-21.863],[113.681,-22.012],[113.6165,-22.1885],[113.5675,-22.26],[113.5035,-22.3845],[113.474,-22.4255],[113.408,-22.5965],[113.406,-22.688],[113.437,-22.8085],[113.4655,-22.869],[113.5545,-22.9575],[113.515,-23.0825],[113.558,-23.336],[113.5515,-23.3975],[113.5045,-23.4265],[113.4445,-23.4905],[113.379,-23.6185],[113.317,-23.712],[113.2645,-23.8305],[113.2455,-23.924],[113.204,-24.0325],[113.203,-24.12],[113.1745,-24.1955],[113.185,-24.3155],[113.181,-24.422],[112.9915,-24.6205],[112.9285,-24.737],[112.909,-24.825],[112.902,-24.9475],[112.867,-25.0605],[112.871,-25.1385],[112.8485,-25.2405],[112.7875,-25.361],[112.715,-25.4705],[112.7,-25.5885]]],[[[113.347,-28.2875],[113.3635,-28.3765],[113.441,-28.536],[113.488,-28.8025],[113.531,-28.886],[113.6865,-29.072],[113.812,-29.1615],[113.891,-29.188],[114.003,-29.1895],[114.0695,-29.167],[114.1585,-29.0905],[114.2245,-28.9685],[114.264,-28.85],[114.265,-28.755],[114.234,-28.6905],[114.087,-28.527],[114.024,-28.38],[113.988,-28.33],[113.8855,-28.2645],[113.7035,-28.096],[113.6495,-28.0745],[113.5575,-28.0675],[113.494,-28.0825],[113.416,-28.1305],[113.3775,-28.18],[113.347,-28.2875]]],[[[118.6745,-17.605],[118.6855,-17.675],[118.721,-17.7425],[118.763,-17.7905],[118.838,-17.8405],[118.95,-17.8615],[119.0285,-17.842],[119.1295,-17.7635],[119.172,-17.6835],[119.1865,-17.63],[119.183,-17.523],[119.291,-17.5865],[119.3775,-17.594],[119.439,-17.5795],[119.4925,-17.55],[119.5635,-17.4645],[119.585,-17.4105],[119.5915,-17.305],[119.558,-17.1805],[119.5115,-17.115],[119.455,-17.0725],[119.3585,-17.044],[119.2845,-17.051],[119.2385,-17.07],[119.1745,-17.12],[119.128,-17.196],[119.105,-17.281],[119.105,-17.3545],[119.0515,-17.3175],[118.949,-17.2945],[118.829,-17.3305],[118.76,-17.3965],[118.6885,-17.531],[118.6745,-17.605]]],[[[121.5105,-14.146],[121.5295,-14.2375],[121.559,-14.2855],[121.639,-14.3555],[121.739,-14.4],[121.811,-14.413],[121.897,-14.403],[122.0415,-14.332],[122.125,-14.2625],[122.1795,-14.155],[122.183,-14.0595],[122.153,-13.972],[122.134,-13.8785],[122.2085,-13.824],[122.244,-13.7675],[122.26,-13.7055],[122.256,-13.641],[122.2275,-13.5725],[122.181,-13.5065],[122.1295,-13.4625],[122.0325,-13.426],[121.9565,-13.428],[121.8965,-13.449],[121.8195,-13.5165],[121.783,-13.581],[121.7705,-13.694],[121.7035,-13.7345],[121.651,-13.808],[121.617,-13.9],[121.537,-14.012],[121.5105,-14.146]]],[[[122.723,-12.2385],[122.7365,-12.314],[122.7705,-12.372],[122.8445,-12.432],[122.924,-12.475],[122.9845,-12.494],[123.1825,-12.494],[123.243,-12.47],[123.3155,-12.4005],[123.3365,-12.357],[123.361,-12.254],[123.3365,-12.137],[123.304,-12.079],[123.2325,-12.0165],[123.092,-11.9755],[123.0155,-11.9785],[122.9175,-12.028],[122.845,-12.053],[122.7865,-12.0935],[122.7335,-12.1765],[122.723,-12.2385]]],[[[123.33,-12.538],[123.3435,-12.611],[123.391,-12.6845],[123.4315,-12.7165],[123.5045,-12.744],[123.571,-12.747],[123.6535,-12.7255],[123.7075,-12.693],[123.769,-12.6115],[123.786,-12.5375],[123.7825,-12.4915],[123.7445,-12.408],[123.653,-12.341],[123.5535,-12.323],[123.462,-12.34],[123.4025,-12.3765],[123.352,-12.442],[123.33,-12.538]]],[[[123.3315,-14.109],[123.355,-14.2045],[123.427,-14.284],[123.4915,-14.3135],[123.608,-14.314],[123.678,-14.2795],[123.748,-14.188],[123.7625,-14.116],[123.733,-14.006],[123.681,-13.946],[123.576,-13.9015],[123.494,-13.906],[123.428,-13.934],[123.366,-13.9945],[123.3315,-14.109]]],[[[141.362,-9.8285],[141.3735,-9.8615],[141.4325,-9.8795],[141.4875,-9.8275],[141.4585,-9.7745],[141.3955,-9.7775],[141.362,-9.8285]]],[[[141.481,-9.533],[141.515,-9.587],[141.5065,-9.6215],[141.5475,-9.6685],[141.5995,-9.6575],[141.6235,-9.6275],[141.6175,-9.5745],[141.656,-9.521],[141.6445,-9.467],[141.5575,-9.448],[141.5025,-9.4885],[141.481,-9.533]]],[[[142.0595,-9.261],[142.091,-9.315],[142.163,-9.34],[142.2705,-9.3475],[142.333,-9.3165],[142.346,-9.266],[142.275,-9.23],[142.2365,-9.1915],[142.1435,-9.196],[142.0595,-9.261]]],[[[142.127,-9.5455],[142.1555,-9.5915],[142.2035,-9.592],[142.2515,-9.615],[142.3475,-9.6265],[142.3895,-9.568],[142.356,-9.5195],[142.259,-9.492],[142.1875,-9.485],[142.152,-9.498],[142.127,-9.5455]]],[[[142.4955,-9.366],[142.472,-9.4055],[142.483,-9.4535],[142.517,-9.485],[142.5855,-9.46],[142.647,-9.4785],[142.773,-9.4855],[142.8185,-9.4585],[142.851,-9.3915],[142.8435,-9.349],[142.8065,-9.3225],[142.5925,-9.3555],[142.559,-9.3745],[142.4955,-9.366]]],[[[143.7835,-9.174],[143.801,-9.211],[143.846,-9.222],[143.931,-9.1745],[143.934,-9.12],[143.9005,-9.091],[143.8615,-9.091],[143.7965,-9.139],[143.7835,-9.174]]],[[[144.074,-9.358],[144.103,-9.406],[144.1635,-9.4135],[144.202,-9.457],[144.274,-9.4535],[144.296,-9.4155],[144.2775,-9.3605],[144.236,-9.3335],[144.1895,-9.34],[144.132,-9.3055],[144.084,-9.3265],[144.074,-9.358]]],[[[147.593,-16.5235],[147.608,-16.603],[147.644,-16.6635],[147.702,-16.7155],[147.799,-16.7495],[147.8725,-16.7455],[147.9325,-16.725],[147.9985,-16.73],[148.086,-16.708],[148.191,-16.649],[148.2575,-16.5795],[148.2925,-16.509],[148.302,-16.455],[148.294,-16.3845],[148.246,-16.298],[148.204,-16.2585],[148.1475,-16.23],[148.065,-16.209],[147.9805,-16.209],[147.8615,-16.1815],[147.804,-16.188],[147.7005,-16.2455],[147.6375,-16.3335],[147.6235,-16.423],[147.593,-16.5235]]],[[[148.121,-17.574],[148.139,-17.6575],[148.1345,-17.7115],[148.159,-17.801],[148.1985,-17.853],[148.234,-17.9315],[148.33,-18.0255],[148.3745,-18.0545],[148.455,-18.0805],[148.5325,-18.0845],[148.5905,-18.0705],[148.6935,-18.0005],[148.7415,-17.894],[148.7395,-17.8165],[148.7835,-17.754],[148.802,-17.693],[148.8045,-17.607],[148.789,-17.5385],[148.753,-17.46],[148.6815,-17.3775],[148.6205,-17.3465],[148.5665,-17.336],[148.4675,-17.3525],[148.4115,-17.3875],[148.3295,-17.372],[148.2395,-17.3925],[148.183,-17.431],[148.1335,-17.5055],[148.121,-17.574]]],[[[148.917,-16.9755],[148.945,-17.0995],[148.9915,-17.1635],[149.0405,-17.1975],[149.1265,-17.2245],[149.2235,-17.2165],[149.3185,-17.158],[149.4035,-17.0275],[149.4215,-16.919],[149.4065,-16.855],[149.3645,-16.7895],[149.2835,-16.7345],[149.174,-16.7205],[149.0835,-16.753],[148.9765,-16.8335],[148.928,-16.9095],[148.917,-16.9755]]],[[[149.1,-19.7385],[149.111,-19.8045],[149.162,-19.884],[149.3025,-19.977],[149.4225,-20.032],[149.5105,-20.0865],[149.576,-20.098],[149.6675,-20.082],[149.7285,-20.047],[149.7665,-20.008],[149.8685,-19.9325],[149.912,-19.8785],[149.9445,-19.8085],[149.9725,-19.6695],[149.9645,-19.5885],[149.905,-19.489],[149.858,-19.4455],[149.757,-19.387],[149.698,-19.3395],[149.598,-19.3125],[149.515,-19.3255],[149.4615,-19.353],[149.386,-19.356],[149.337,-19.3725],[149.2685,-19.4245],[149.226,-19.505],[149.1625,-19.5645],[149.105,-19.679],[149.1,-19.7385]]],[[[149.4085,-15.722],[149.4195,-15.79],[149.4795,-15.8775],[149.547,-15.917],[149.662,-15.9255],[149.725,-15.903],[149.797,-15.837],[149.829,-15.7565],[149.816,-15.65],[149.773,-15.5845],[149.6695,-15.526],[149.5515,-15.5305],[149.4745,-15.575],[149.4295,-15.634],[149.4085,-15.722]]],[[[149.6945,-16.9695],[149.6985,-17.0055],[149.7405,-17.0965],[149.7885,-17.14],[149.871,-17.1725],[149.935,-17.1735],[150.0875,-17.1275],[150.146,-17.093],[150.2085,-17.006],[150.221,-16.95],[150.2165,-16.8795],[150.1835,-16.8085],[150.1035,-16.7425],[150.016,-16.721],[149.94,-16.732],[149.7975,-16.7905],[149.7285,-16.855],[149.6945,-16.9695]]],[[[149.752,-16.294],[149.773,-16.3885],[149.8225,-16.456],[149.873,-16.489],[149.988,-16.5085],[150.0665,-16.4845],[150.0635,-16.576],[150.0985,-16.6565],[150.2005,-16.764],[150.2965,-16.8085],[150.346,-16.8125],[150.421,-16.797],[150.516,-16.724],[150.5545,-16.622],[150.553,-16.5695],[150.5315,-16.5055],[150.4675,-16.3975],[150.4315,-16.3595],[150.361,-16.322],[150.308,-16.3125],[150.2245,-16.324],[150.1795,-16.3135],[150.231,-16.216],[150.2445,-16.135],[150.2265,-16.056],[150.165,-15.972],[150.1085,-15.9335],[150.017,-15.908],[149.9595,-15.9105],[149.8625,-15.944],[149.813,-15.985],[149.767,-16.0715],[149.761,-16.1345],[149.776,-16.1955],[149.752,-16.294]]],[[[150.5925,-17.4295],[150.6115,-17.5135],[150.642,-17.5605],[150.619,-17.622],[150.616,-17.6865],[150.648,-17.7725],[150.6845,-17.8135],[150.753,-17.853],[150.868,-17.8615],[150.967,-17.816],[151.0145,-17.762],[151.0435,-17.6515],[151.143,-17.647],[151.167,-17.748],[151.22,-17.8105],[151.2625,-17.8365],[151.3335,-17.856],[151.458,-17.845],[151.575,-17.79],[151.6645,-17.7715],[151.7595,-17.729],[151.902,-17.623],[151.9825,-17.593],[152.0495,-17.5865],[152.174,-17.53],[152.2495,-17.4395],[152.276,-17.367],[152.319,-17.311],[152.358,-17.209],[152.365,-17.1315],[152.3505,-17.0695],[152.2745,-16.9665],[152.2265,-16.9355],[152.1385,-16.91],[152.0585,-16.9145],[151.9245,-16.9035],[151.7855,-16.9195],[151.699,-16.9655],[151.612,-17.0355],[151.5605,-17.0965],[151.4905,-17.15],[151.3825,-17.208],[151.321,-17.264],[151.287,-17.3375],[151.1995,-17.248],[151.1395,-17.221],[151.0505,-17.2135],[150.953,-17.249],[150.9195,-17.2505],[150.8265,-17.224],[150.711,-17.2465],[150.647,-17.293],[150.6015,-17.3705],[150.5925,-17.4295]]],[[[151.0225,-21.8115],[151.0275,-21.854],[151.0615,-21.9285],[151.0975,-21.9665],[151.184,-22.012],[151.2585,-22.0235],[151.356,-22.003],[151.405,-21.973],[151.463,-21.8985],[151.4775,-21.782],[151.432,-21.686],[151.377,-21.6385],[151.2945,-21.6085],[151.1775,-21.6165],[151.099,-21.657],[151.046,-21.72],[151.0225,-21.8115]]],[[[151.4175,-21.332],[151.4275,-21.3875],[151.4765,-21.4865],[151.569,-21.5685],[151.6525,-21.5915],[151.7185,-21.6245],[151.7885,-21.635],[151.851,-21.6265],[151.9695,-21.594],[151.987,-21.68],[151.97,-21.7915],[151.9985,-21.892],[152.054,-21.971],[152.046,-22.026],[152.072,-22.1205],[152.1195,-22.1765],[152.2085,-22.2595],[152.3305,-22.348],[152.3415,-22.449],[152.3755,-22.505],[152.4235,-22.5455],[152.53,-22.593],[152.6445,-22.5965],[152.8035,-22.548],[152.866,-22.5115],[152.932,-22.429],[152.969,-22.3185],[152.9795,-22.2575],[152.9835,-22.1335],[152.967,-22.066],[152.9215,-22.0005],[152.9075,-21.889],[152.854,-21.794],[152.8405,-21.745],[152.763,-21.6155],[152.7935,-21.47],[152.7795,-21.4],[152.7465,-21.346],[152.8025,-21.291],[152.84,-21.227],[152.8645,-21.1095],[152.8425,-20.9815],[152.8215,-20.9375],[152.771,-20.882],[152.7215,-20.8515],[152.6175,-20.8205],[152.3905,-20.7775],[152.222,-20.7265],[152.1595,-20.723],[152.034,-20.738],[151.935,-20.78],[151.8735,-20.847],[151.849,-20.9185],[151.7655,-20.8725],[151.687,-20.861],[151.6315,-20.868],[151.5635,-20.898],[151.484,-20.989],[151.457,-21.0875],[151.4675,-21.161],[151.4265,-21.269],[151.4175,-21.332]]],[[[151.9485,-19.222],[151.9765,-19.321],[152.0635,-19.43],[152.1035,-19.4595],[152.1905,-19.497],[152.2815,-19.5015],[152.357,-19.4835],[152.4965,-19.4065],[152.553,-19.3405],[152.5955,-19.2675],[152.619,-19.1535],[152.61,-19.074],[152.584,-18.955],[152.5505,-18.894],[152.454,-18.7865],[152.3735,-18.741],[152.2945,-18.7275],[152.174,-18.742],[152.097,-18.784],[152.0475,-18.8445],[152.029,-18.894],[152.03,-18.984],[151.987,-19.0595],[151.9485,-19.222]]],[[[153.298,-21.8555],[153.3315,-21.977],[153.3735,-22.037],[153.437,-22.088],[153.502,-22.1155],[153.5885,-22.1255],[153.711,-22.101],[153.798,-22.0485],[153.9105,-21.926],[153.957,-21.861],[153.9975,-21.753],[153.999,-21.667],[153.9815,-21.6045],[153.926,-21.5185],[153.8205,-21.4605],[153.731,-21.457],[153.6215,-21.507],[153.5755,-21.563],[153.55,-21.649],[153.4835,-21.65],[153.4055,-21.677],[153.328,-21.7505],[153.298,-21.8555]]],[[[154.133,-21.012],[154.139,-21.0625],[154.1745,-21.1355],[154.2415,-21.1945],[154.3255,-21.2245],[154.3915,-21.227],[154.4945,-21.1965],[154.5545,-21.1465],[154.5895,-21.097],[154.6125,-21.0195],[154.617,-20.9345],[154.6055,-20.8665],[154.5805,-20.8165],[154.4985,-20.745],[154.4275,-20.7235],[154.3695,-20.723],[154.2905,-20.748],[154.2285,-20.798],[154.147,-20.9405],[154.133,-21.012]]],[[[154.94,-22.179],[154.9595,-22.263],[155.0205,-22.336],[155.09,-22.3785],[155.182,-22.4085],[155.2785,-22.417],[155.3585,-22.402],[155.4695,-22.3945],[155.5545,-22.3685],[155.6495,-22.305],[155.6905,-22.2305],[155.692,-22.1285],[155.639,-22.0375],[155.5735,-21.992],[155.4855,-21.965],[155.429,-21.965],[155.278,-21.997],[155.181,-21.975],[155.099,-21.984],[155.0035,-22.036],[154.959,-22.096],[154.94,-22.179]]],[[[155.3145,-23.2525],[155.338,-23.3445],[155.4055,-23.418],[155.4745,-23.4495],[155.5665,-23.458],[155.6425,-23.442],[155.726,-23.3865],[155.7655,-23.3265],[155.7815,-23.2525],[155.7635,-23.169],[155.717,-23.1035],[155.6745,-23.0715],[155.603,-23.045],[155.4835,-23.052],[155.4255,-23.0755],[155.369,-23.1185],[155.3345,-23.1685],[155.3145,-23.2525]]],[[[155.492,-21.278],[155.5115,-21.3605],[155.557,-21.422],[155.595,-21.4495],[155.6935,-21.48],[155.824,-21.4595],[155.91,-21.421],[155.979,-21.3555],[156.007,-21.2915],[156.0075,-21.2035],[155.9525,-21.0375],[155.909,-20.969],[155.8125,-20.909],[155.7205,-20.898],[155.6445,-20.9165],[155.5875,-20.9525],[155.5395,-21.0155],[155.524,-21.0605],[155.527,-21.165],[155.492,-21.278]]],[[[155.627,-17.3505],[155.655,-17.4995],[155.6875,-17.5605],[155.76,-17.619],[155.81,-17.6365],[155.9005,-17.638],[156.005,-17.5885],[156.055,-17.526],[156.0815,-17.4635],[156.086,-17.3435],[156.059,-17.279],[156.02,-17.226],[155.9515,-17.168],[155.8875,-17.1465],[155.8105,-17.1465],[155.741,-17.169],[155.694,-17.2015],[155.647,-17.2645],[155.627,-17.3505]]],[[[158.3265,-55.124],[158.3425,-55.182],[158.407,-55.2505],[158.489,-55.292],[158.615,-55.3195],[158.7635,-55.3165],[158.8595,-55.293],[158.964,-55.2405],[159.0155,-55.1885],[159.04,-55.111],[159.0145,-55.0395],[158.9465,-54.9775],[159.102,-54.923],[159.1845,-54.8385],[159.1965,-54.7565],[159.2515,-54.6485],[159.2915,-54.5245],[159.293,-54.4645],[159.3405,-54.3575],[159.3095,-54.2745],[159.25,-54.2215],[159.1215,-54.17],[158.9975,-54.156],[158.835,-54.1795],[158.7155,-54.24],[158.6505,-54.3445],[158.557,-54.41],[158.51,-54.4835],[158.4305,-54.7465],[158.4385,-54.8295],[158.5325,-54.9315],[158.3875,-55.0075],[158.3455,-55.0575],[158.3265,-55.124]]],[[[158.7875,-29.9425],[158.8175,-30.0455],[158.872,-30.105],[158.933,-30.1395],[159.0305,-30.1735],[159.139,-30.1815],[159.22,-30.159],[159.3135,-30.086],[159.3495,-30.0135],[159.3505,-29.9085],[159.31,-29.831],[159.194,-29.7385],[159.121,-29.7135],[159.0475,-29.7105],[158.9165,-29.7535],[158.8445,-29.8065],[158.8085,-29.858],[158.7875,-29.9425]]],[[[158.801,-31.513],[158.814,-31.583],[158.8665,-31.698],[158.9605,-31.7765],[159.004,-31.792],[159.032,-31.8585],[159.0685,-31.9],[159.1445,-31.9515],[159.229,-31.9835],[159.293,-31.988],[159.4085,-31.9555],[159.4615,-31.9155],[159.505,-31.846],[159.515,-31.7825],[159.482,-31.6855],[159.4115,-31.6065],[159.359,-31.5755],[159.3555,-31.5195],[159.3255,-31.453],[159.2535,-31.3605],[159.2155,-31.329],[159.131,-31.2925],[159.0665,-31.2855],[158.9655,-31.307],[158.851,-31.3915],[158.8085,-31.4685],[158.801,-31.513]]],[[[158.818,-29.46],[158.828,-29.5225],[158.8665,-29.5905],[158.9425,-29.65],[159.058,-29.684],[159.187,-29.6695],[159.2755,-29.625],[159.3465,-29.5435],[159.37,-29.451],[159.3615,-29.3915],[159.309,-29.3045],[159.262,-29.2665],[159.186,-29.233],[159.1155,-29.2235],[159.001,-29.246],[158.9115,-29.2925],[158.8465,-29.361],[158.818,-29.46]]],[[[167.6855,-29.009],[167.688,-29.07],[167.728,-29.195],[167.77,-29.2635],[167.8705,-29.3255],[167.9625,-29.3365],[168.069,-29.3065],[168.1285,-29.2615],[168.1685,-29.2115],[168.2195,-29.0845],[168.22,-28.9825],[168.1575,-28.884],[168.0745,-28.832],[167.9325,-28.794],[167.84,-28.807],[167.7715,-28.843],[167.7145,-28.9075],[167.6855,-29.009]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/b9/Flag_of_Australia.svg","name:en":"Australia","wikidata":"Q408","ISO3166-1:alpha2":"AU","ISO3166-1:alpha3":"AUS","ISO3166-1:numeric":"036"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-71.18,20.9795],[-72.0295,20.0],[-72.0575,19.953],[-72.0315,19.9245],[-71.914,19.7965],[-71.804,19.733],[-71.7735,19.7315],[-71.7455,19.634],[-71.743,19.5845],[-71.7135,19.552],[-71.6795,19.434],[-71.712,19.3715],[-71.778,19.3345],[-71.7405,19.277],[-71.691,19.239],[-71.639,19.2315],[-71.631,19.1835],[-71.6525,19.139],[-71.723,19.0535],[-71.771,19.0315],[-71.8355,18.97],[-71.783,18.9485],[-71.768,18.907],[-71.725,18.8755],[-71.722,18.804],[-71.738,18.722],[-71.804,18.6855],[-71.807,18.6355],[-71.8445,18.629],[-71.9665,18.6565],[-71.9815,18.612],[-71.881,18.504],[-71.9045,18.457],[-71.844,18.429],[-71.83,18.399],[-71.696,18.3405],[-71.768,18.2205],[-71.7835,18.17],[-71.745,18.137],[-71.744,18.053],[-71.8295,17.954],[-71.9395,17.841],[-71.897,17.5945],[-71.8375,17.3895],[-71.7935,17.33],[-71.7125,17.281],[-71.658,17.2705],[-71.5375,17.2955],[-70.1125,18.023],[-68.7725,17.9375],[-68.683,17.9185],[-68.55,17.9135],[-68.4905,17.926],[-68.405,17.9875],[-68.382,18.0235],[-68.133,18.51],[-68.118,18.549],[-68.11,18.644],[-68.12,18.6995],[-68.5255,19.9025],[-68.589,20.009],[-68.597,20.0515],[-69.341,20.7815],[-69.3875,20.835],[-69.556,20.9345],[-69.814,21.0675],[-70.4545,21.2855],[-70.5565,21.2925],[-70.635,21.26],[-70.695,21.2015],[-70.8275,21.185],[-70.979,21.149],[-71.0835,21.038],[-71.18,20.9795]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9f/Flag_of_the_Dominican_Republic.svg","name:en":"Dominican Republic","wikidata":"Q786","ISO3166-1:alpha2":"DO","ISO3166-1:alpha3":"DOM","ISO3166-1:numeric":"214"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-61.68,15.6445],[-61.687,15.526],[-61.672,15.449],[-61.635,15.3525],[-61.598,15.283],[-61.574,15.1625],[-61.539,15.093],[-61.45,15.027],[-61.349,15.0075],[-61.159,15.079],[-61.084,15.1445],[-61.044,15.2375],[-61.0345,15.315],[-61.0365,15.495],[-61.0435,15.541],[-61.0835,15.63],[-61.1385,15.7015],[-61.3235,15.734],[-61.4425,15.787],[-61.68,15.6445]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/c4/Flag_of_Dominica.svg","name:en":"Dominica","wikidata":"Q784","ISO3166-1:alpha2":"DM","ISO3166-1:alpha3":"DMA","ISO3166-1:numeric":"212"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-65.123,32.275],[-65.099,32.1895],[-65.062,32.1345],[-64.9665,32.0685],[-64.8865,32.0485],[-64.8195,32.047],[-64.7235,32.0685],[-64.517,32.1825],[-64.458,32.2325],[-64.418,32.3055],[-64.4135,32.396],[-64.4655,32.498],[-64.5575,32.5655],[-64.6305,32.588],[-64.7295,32.586],[-64.8515,32.5295],[-64.8995,32.5225],[-65.029,32.4635],[-65.0835,32.4125],[-65.113,32.3515],[-65.123,32.275]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/bf/Flag_of_Bermuda.svg","name:en":"Bermuda","wikidata":"Q23635","ISO3166-1:alpha2":"BM","ISO3166-1:alpha3":"BMU","ISO3166-1:numeric":"060"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[5.4175,-1.416],[5.4285,-1.506],[5.4665,-1.585],[5.516,-1.6315],[5.57,-1.66],[5.634,-1.673],[5.6995,-1.665],[5.789,-1.6095],[5.8395,-1.529],[5.8575,-1.4075],[5.826,-1.2975],[5.7735,-1.2425],[5.6945,-1.2085],[5.635,-1.202],[5.5655,-1.213],[5.47,-1.275],[5.4345,-1.33],[5.4175,-1.416]]],[[[8.8315,3.9605],[8.7495,3.986],[8.692,3.988],[8.6225,3.969],[8.528,3.894],[8.434,3.764],[8.407,3.662],[8.3245,3.614],[8.2545,3.517],[8.2245,3.433],[8.215,3.373],[8.225,3.291],[8.255,3.203],[8.29,3.148],[8.382,3.083],[8.6415,3.013],[8.698,3.011],[8.759,3.027],[8.841,3.091],[8.9195,3.191],[8.9685,3.304],[9.04,3.448],[9.099,3.525],[9.1275,3.593],[9.1385,3.705],[9.1255,3.771],[9.047,3.856],[8.9835,3.906],[8.8315,3.9605]]],[[[11.36,2.172],[11.0,2.171],[10.656,2.174],[10.19,2.175],[10.167,2.152],[10.035,2.163],[9.906,2.2075],[9.848,2.2455],[9.8375,2.325],[9.762,2.3905],[9.621,2.45],[9.592,2.4005],[9.576,2.3015],[9.5935,2.207],[9.5645,2.0895],[9.5935,1.9775],[9.547,1.893],[9.4375,1.7695],[9.4145,1.712],[9.405,1.6315],[9.3325,1.554],[9.2845,1.4885],[9.262,1.422],[9.22,1.368],[9.1645,1.2685],[9.142,1.1585],[9.1675,1.0755],[9.12,1.0045],[9.103,0.9175],[9.116,0.814],[9.1495,0.7535],[9.355,0.8465],[9.519,0.963],[9.5475,1.0225],[9.62,1.0295],[9.668,1.062],[9.749,1.063],[9.798,1.0],[9.8795,0.9885],[9.895,0.953],[9.958,0.929],[9.9935,0.9385],[9.992,1.0025],[10.7185,1.0025],[11.3535,1.002],[11.3495,1.4715],[11.3465,1.8315],[11.355,2.1595],[11.36,2.172]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/31/Flag_of_Equatorial_Guinea.svg","name:en":"Equatorial Guinea","wikidata":"Q983","ISO3166-1:alpha2":"GQ","ISO3166-1:alpha3":"GNQ","ISO3166-1:numeric":"226"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1.673,6.04],[1.8815,6.084],[2.13,6.127],[2.353,6.149],[2.426,6.142],[2.5125,6.159],[2.629,6.164],[2.738,6.178],[2.7065,6.3775],[2.706,6.52],[2.748,6.568],[2.7295,6.637],[2.784,6.699],[2.7845,6.764],[2.7345,6.785],[2.7415,6.9275],[2.7125,6.9515],[2.729,7.017],[2.7615,7.0435],[2.7405,7.105],[2.773,7.133],[2.7575,7.2515],[2.745,7.2815],[2.745,7.4245],[2.794,7.4295],[2.7925,7.4975],[2.735,7.55],[2.7165,7.6365],[2.729,7.7555],[2.7255,7.8025],[2.6785,7.8745],[2.6995,7.9325],[2.733,8.0815],[2.754,8.2125],[2.7205,8.25],[2.696,8.3545],[2.7195,8.3875],[2.7085,8.4225],[2.7495,8.4485],[2.7555,8.654],[2.739,8.683],[2.749,8.7405],[2.731,8.787],[2.7595,8.8445],[2.7905,8.9895],[2.783,9.0695],[2.8795,9.0645],[2.953,9.0925],[2.9725,9.075],[3.0885,9.102],[3.122,9.1925],[3.123,9.2325],[3.158,9.284],[3.1555,9.3605],[3.133,9.441],[3.1615,9.4975],[3.1885,9.5115],[3.252,9.6065],[3.267,9.659],[3.314,9.6595],[3.361,9.708],[3.325,9.761],[3.332,9.8055],[3.3625,9.835],[3.4625,9.8705],[3.515,9.8605],[3.5635,9.901],[3.598,9.957],[3.617,10.084],[3.659,10.109],[3.6795,10.177],[3.608,10.2115],[3.5765,10.2725],[3.604,10.344],[3.602,10.4125],[3.6835,10.4615],[3.785,10.4075],[3.8055,10.4505],[3.8055,10.514],[3.845,10.5945],[3.835,10.696],[3.78,10.736],[3.7455,10.817],[3.7695,10.924],[3.751,11.013],[3.725,11.0275],[3.7225,11.1285],[3.6935,11.129],[3.4765,11.438],[3.525,11.575],[3.5765,11.6685],[3.6095,11.6935],[3.5515,11.7285],[3.5615,11.7785],[3.482,11.86],[3.448,11.8585],[3.3735,11.889],[3.323,11.8845],[3.2795,11.942],[3.2205,12.0615],[3.126,12.156],[3.0745,12.1815],[3.0145,12.266],[2.9665,12.2865],[2.8695,12.3895],[2.8415,12.4055],[2.7225,12.3525],[2.6945,12.283],[2.662,12.3065],[2.5925,12.2995],[2.5475,12.274],[2.471,12.273],[2.4015,12.258],[2.3875,12.2165],[2.4035,12.1035],[2.464,11.9855],[2.4065,11.9525],[2.409,11.9025],[2.3805,11.8575],[2.372,11.798],[2.2985,11.716],[2.3145,11.6795],[2.184,11.594],[2.0195,11.4275],[1.982,11.42],[1.8725,11.446],[1.7735,11.4195],[1.6015,11.3975],[1.5835,11.447],[1.4745,11.471],[1.3955,11.453],[1.392,11.4115],[1.326,11.3525],[1.347,11.3065],[1.294,11.2985],[1.284,11.266],[1.1925,11.2635],[1.0745,11.132],[1.101,11.068],[1.0275,11.0525],[1.013,11.0875],[0.912,10.996],[0.8885,10.92],[0.8795,10.7995],[0.8095,10.728],[0.8005,10.681],[0.8075,10.607],[0.7855,10.5245],[0.7765,10.3765],[1.098,10.15],[1.153,10.121],[1.355,9.9955],[1.3635,9.826],[1.3685,9.5965],[1.3395,9.546],[1.3705,9.4815],[1.393,9.479],[1.407,9.3445],[1.4315,9.3015],[1.5105,9.211],[1.57,9.166],[1.6045,9.1045],[1.6265,9.0075],[1.617,8.9595],[1.628,8.881],[1.6245,8.548],[1.661,8.497],[1.631,8.452],[1.613,8.369],[1.6345,8.358],[1.638,7.8465],[1.6425,7.62],[1.656,7.5325],[1.6445,7.443],[1.6425,6.9955],[1.559,6.997],[1.584,6.91],[1.6065,6.9045],[1.5945,6.7995],[1.6235,6.7375],[1.58,6.6915],[1.6075,6.665],[1.61,6.6135],[1.703,6.532],[1.745,6.4735],[1.7965,6.3205],[1.7765,6.2855],[1.63,6.2355],[1.673,6.04]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/0a/Flag_of_Benin.svg","name:en":"Benin","wikidata":"Q962","ISO3166-1:alpha2":"BJ","ISO3166-1:alpha3":"BEN","ISO3166-1:numeric":"204"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-64.64,18.1065],[-64.534,18.1215],[-64.458,18.1555],[-64.324,18.24],[-64.2595,18.3065],[-64.2095,18.33],[-64.144,18.3935],[-64.11,18.4875],[-64.1165,18.5555],[-64.0735,18.6225],[-64.0595,18.6905],[-64.0815,18.789],[-64.1355,18.862],[-64.177,18.8985],[-64.289,18.9455],[-64.389,18.95],[-64.4655,18.9405],[-64.537,18.908],[-64.587,18.86],[-64.6145,18.81],[-64.6275,18.7475],[-64.6195,18.6875],[-64.6945,18.6585],[-64.847,18.6525],[-64.956,18.6115],[-64.897,18.4895],[-64.872,18.4085],[-64.799,18.408],[-64.756,18.377],[-64.6395,18.364],[-64.6605,18.29],[-64.64,18.1065]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/42/Flag_of_the_British_Virgin_Islands.svg","name:en":"British Virgin Islands","wikidata":"Q25305","ISO3166-1:alpha2":"VG","ISO3166-1:alpha3":"VGB","ISO3166-1:numeric":"092"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[24.0,19.5],[23.4995,19.772],[22.831,20.129],[22.268,20.4255],[21.6615,20.741],[21.2735,20.941],[20.7355,21.216],[20.2605,21.4555],[19.6715,21.7505],[19.186,21.99],[18.63,22.262],[18.1465,22.494],[17.819,22.6505],[17.7885,22.661],[16.0,23.4515],[14.9985,23.001],[15.074,22.6135],[15.1945,21.999],[15.2025,21.496],[15.2845,21.4455],[15.627,20.9555],[15.5685,20.9105],[15.554,20.8525],[15.576,20.7665],[15.6755,20.6965],[15.9965,20.353],[15.754,19.9325],[15.6665,19.264],[15.6025,18.782],[15.573,18.2625],[15.5435,17.729],[15.522,17.333],[15.5055,16.898],[15.2585,16.6395],[14.9565,16.3255],[14.9135,16.2765],[14.6105,15.956],[14.385,15.7315],[14.276,15.588],[13.97,15.1565],[13.8695,15.043],[13.7895,14.863],[13.81,14.7215],[13.7455,14.6985],[13.6775,14.6365],[13.696,14.551],[13.641,14.5135],[13.572,14.5055],[13.4935,14.4745],[13.4735,14.443],[13.4855,14.3585],[13.6335,13.708],[14.0835,13.0835],[14.4665,13.0835],[14.535,12.946],[14.5605,12.9205],[14.565,12.8365],[14.547,12.7925],[14.5945,12.746],[14.669,12.7195],[14.7095,12.7265],[14.7615,12.6775],[14.772,12.635],[14.823,12.64],[14.8545,12.576],[14.848,12.518],[14.9085,12.3805],[14.8935,12.3075],[14.9095,12.1915],[14.8855,12.165],[14.956,12.0995],[14.9935,12.088],[15.038,12.104],[15.0605,12.06],[15.051,12.0145],[15.085,12.002],[15.0395,11.9105],[15.0495,11.8585],[15.089,11.855],[15.122,11.7885],[15.0635,11.712],[15.102,11.5845],[15.1445,11.5625],[15.058,11.4145],[15.0735,11.3265],[15.0485,11.2805],[15.068,11.2015],[15.095,11.168],[15.092,11.0335],[15.046,11.004],[15.079,10.958],[15.095,10.876],[15.0675,10.8235],[15.096,10.748],[15.138,10.6865],[15.161,10.626],[15.1425,10.592],[15.1555,10.529],[15.216,10.498],[15.2795,10.4055],[15.304,10.312],[15.3455,10.298],[15.4405,10.1975],[15.502,10.099],[15.617,10.0455],[15.6885,9.993],[15.5235,9.952],[15.414,9.9335],[15.3645,9.959],[15.248,9.9895],[15.1485,9.9945],[15.06,9.9455],[15.033,9.9655],[14.958,9.983],[14.8025,9.935],[14.4635,10.002],[14.2065,10.002],[14.1735,9.904],[14.131,9.82],[14.0225,9.728],[13.9765,9.638],[14.04,9.602],[14.0925,9.5505],[14.1045,9.518],[14.15,9.5025],[14.204,9.4465],[14.2685,9.358],[14.3045,9.3715],[14.339,9.31],[14.373,9.294],[14.3475,9.2085],[14.407,9.148],[14.464,9.115],[14.4905,9.058],[14.5665,9.014],[14.745,8.8795],[14.844,8.812],[14.9,8.8135],[14.9225,8.7785],[14.989,8.7285],[14.9825,8.689],[15.06,8.681],[15.109,8.6595],[15.151,8.606],[15.154,8.572],[15.2,8.5125],[15.395,8.0935],[15.394,8.0465],[15.452,7.8855],[15.5065,7.792],[15.5875,7.774],[15.5855,7.668],[15.567,7.5945],[15.4935,7.525],[15.5465,7.511],[15.728,7.519],[15.743,7.4785],[15.8095,7.441],[15.975,7.4835],[16.039,7.534],[16.0565,7.5775],[16.1635,7.5945],[16.1855,7.6145],[16.2475,7.612],[16.296,7.6545],[16.398,7.683],[16.4235,7.7155],[16.407,7.7705],[16.432,7.81],[16.479,7.7895],[16.506,7.8455],[16.577,7.874],[16.5895,7.774],[16.6615,7.742],[16.66,7.672],[16.6845,7.6395],[16.73,7.639],[16.776,7.607],[16.809,7.5375],[16.86,7.545],[16.9015,7.6035],[16.9525,7.645],[17.0355,7.639],[17.106,7.695],[17.1525,7.67],[17.1675,7.7185],[17.208,7.7495],[17.258,7.753],[17.2765,7.8105],[17.36,7.856],[17.4725,7.9],[17.5105,7.8725],[17.535,7.925],[17.606,7.9295],[17.6845,7.983],[17.9215,7.9585],[18.032,8.0095],[18.192,8.018],[18.6015,8.0465],[18.637,8.091],[18.63,8.1465],[18.6735,8.212],[18.7865,8.2545],[18.8335,8.29],[18.8495,8.3455],[18.919,8.387],[18.9295,8.4375],[19.039,8.5375],[19.0465,8.5935],[19.1065,8.664],[19.1005,8.7005],[19.0335,8.7315],[18.907,8.8505],[18.8615,8.878],[18.9115,8.9235],[18.978,8.9405],[19.0725,9.015],[19.147,9.0095],[19.1775,9.027],[19.2635,9.016],[19.51,9.0185],[19.5715,9.0295],[19.684,9.0135],[19.7865,9.048],[19.8785,9.0525],[20.0165,9.093],[20.062,9.1345],[20.147,9.1115],[20.207,9.1395],[20.2535,9.115],[20.3715,9.1085],[20.434,9.137],[20.488,9.2095],[20.498,9.277],[20.538,9.3185],[20.6035,9.298],[20.668,9.2985],[20.678,9.3595],[20.738,9.3705],[20.7745,9.4055],[20.831,9.4285],[20.826,9.458],[20.896,9.5215],[20.9565,9.6055],[20.986,9.612],[21.015,9.7385],[21.103,9.7725],[21.1155,9.822],[21.1885,9.8805],[21.225,9.9425],[21.2765,9.9805],[21.342,9.9585],[21.432,10.038],[21.4735,10.148],[21.5355,10.212],[21.6145,10.2135],[21.667,10.236],[21.688,10.2845],[21.7155,10.2905],[21.7245,10.366],[21.748,10.4065],[21.7015,10.5145],[21.7165,10.6355],[21.811,10.677],[21.8615,10.669],[21.9075,10.714],[22.0085,10.7515],[22.015,10.808],[22.0475,10.8305],[22.113,10.8325],[22.176,10.814],[22.202,10.8705],[22.2505,10.912],[22.271,10.9035],[22.323,10.9415],[22.415,10.963],[22.466,11.0015],[22.529,10.9765],[22.6005,10.9855],[22.653,10.966],[22.7675,10.955],[22.7855,10.931],[22.878,10.9205],[22.9195,11.0665],[22.9755,11.216],[22.972,11.2775],[22.9485,11.316],[22.931,11.416],[22.791,11.4015],[22.7755,11.462],[22.642,11.516],[22.5615,11.6215],[22.5535,11.6725],[22.573,11.799],[22.6145,12.0045],[22.641,12.071],[22.5375,12.058],[22.478,12.03],[22.5015,12.177],[22.4365,12.3545],[22.433,12.4035],[22.3865,12.4545],[22.409,12.487],[22.467,12.6215],[22.335,12.6705],[22.2235,12.747],[22.166,12.672],[22.0745,12.64],[21.977,12.6385],[21.9015,12.6775],[21.8565,12.741],[21.858,12.772],[21.8145,12.8065],[21.847,12.838],[21.91,12.99],[21.941,13.049],[22.0295,13.143],[22.0755,13.151],[22.1595,13.1905],[22.183,13.231],[22.2695,13.321],[22.2885,13.3915],[22.2395,13.454],[22.2265,13.567],[22.1635,13.6235],[22.134,13.6695],[22.1405,13.723],[22.0845,13.779],[22.234,13.966],[22.3445,14.0135],[22.3965,14.052],[22.4345,14.0515],[22.477,14.101],[22.5525,14.121],[22.567,14.164],[22.5555,14.232],[22.4785,14.2445],[22.444,14.272],[22.436,14.315],[22.4695,14.351],[22.4475,14.384],[22.4345,14.496],[22.3845,14.5195],[22.4035,14.592],[22.47,14.6295],[22.7025,14.6915],[22.6775,14.766],[22.667,14.8635],[22.7095,14.8955],[22.76,14.908],[22.7515,14.97],[22.8125,15.0345],[22.882,15.0885],[22.9405,15.116],[22.935,15.169],[22.9675,15.1825],[23.004,15.2405],[22.985,15.2595],[23.003,15.322],[22.988,15.415],[22.9305,15.462],[22.9185,15.4945],[22.937,15.5645],[22.9875,15.5835],[23.0755,15.6675],[23.1105,15.715],[23.207,15.7185],[23.3375,15.694],[23.397,15.6975],[23.552,15.7685],[23.6635,15.7745],[23.8115,15.7515],[23.9995,15.6975],[23.999,16.1995],[24.0,16.733],[23.999,17.0995],[23.999,17.6995],[23.9995,18.033],[23.999,18.5325],[23.999,19.066],[24.0,19.5]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/4b/Flag_of_Chad.svg","name:en":"Chad","wikidata":"Q657","ISO3166-1:alpha2":"TD","ISO3166-1:alpha3":"TCD","ISO3166-1:numeric":"148"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.944,4.619],[35.9645,4.5275],[36.0475,4.4465],[36.234,4.449],[36.5475,4.442],[36.6455,4.4515],[36.681,4.439],[36.8445,4.4475],[36.988,4.3895],[37.0315,4.3795],[37.09,4.341],[37.137,4.293],[37.5015,4.0565],[37.7075,3.9105],[37.9025,3.7835],[37.994,3.7295],[38.13,3.606],[38.1915,3.62],[38.4485,3.602],[38.551,3.642],[38.58,3.604],[38.6655,3.593],[38.7145,3.5725],[38.9065,3.5125],[39.0215,3.51],[39.0875,3.539],[39.191,3.4775],[39.4295,3.451],[39.498,3.4625],[39.515,3.407],[39.554,3.3975],[39.588,3.492],[39.606,3.503],[39.7585,3.659],[39.787,3.702],[39.8695,3.8745],[40.172,4.0275],[40.376,4.1065],[40.652,4.225],[40.7175,4.245],[40.759,4.279],[40.844,4.2465],[40.8825,4.213],[40.904,4.1545],[40.965,4.132],[41.092,4.0135],[41.1055,3.9855],[41.1695,3.9425],[41.271,3.958],[41.33,3.9395],[41.3915,3.963],[41.427,3.9455],[41.4795,3.952],[41.5505,3.9815],[41.567,3.9665],[41.6315,3.9825],[41.6745,3.96],[41.7235,3.987],[41.8445,3.949],[41.907,3.9825],[41.943,4.0165],[41.9465,4.0555],[42.0005,4.0935],[42.0875,4.179],[42.194,4.179],[42.37,4.1895],[42.587,4.216],[42.669,4.2495],[42.7355,4.2645],[42.825,4.267],[42.8975,4.318],[42.9695,4.403],[43.0075,4.468],[43.026,4.5415],[43.0795,4.602],[43.232,4.695],[43.4345,4.7945],[43.552,4.8355],[43.809,4.902],[44.0315,4.9435],[44.154,4.9505],[44.202,4.9425],[44.385,4.941],[44.549,4.926],[44.625,4.933],[44.978,4.9235],[44.9985,4.946],[45.411,5.5365],[45.845,5.9565],[46.511,6.5205],[46.873,6.9075],[47.3375,7.3825],[47.557,7.561],[47.9825,8.0],[46.993,8.0015],[46.2255,8.2635],[45.6265,8.4625],[45.2675,8.5795],[44.69,8.7725],[43.9965,9.0],[43.643,9.357],[43.5155,9.3815],[43.439,9.447],[43.4055,9.545],[43.33,9.6015],[43.298,9.606],[43.257,9.843],[43.15,9.899],[43.086,9.908],[43.036,10.034],[42.8875,10.1895],[42.833,10.278],[42.792,10.4035],[42.747,10.512],[42.7085,10.578],[42.719,10.6475],[42.766,10.7215],[42.93,10.947],[42.9615,10.9845],[42.8675,10.9695],[42.7905,10.9885],[42.783,11.0335],[42.7525,11.0795],[42.688,11.057],[42.629,11.0955],[42.6045,11.07],[42.5065,11.04],[42.4255,10.983],[42.368,10.998],[42.268,10.9715],[42.11,10.9705],[42.0685,10.925],[41.946,10.9155],[41.9175,10.939],[41.7945,10.9795],[41.803,11.006],[41.8075,11.3185],[41.7715,11.494],[41.804,11.572],[41.8075,11.6555],[41.836,11.733],[41.895,11.756],[41.9,11.793],[41.955,11.812],[42.095,12.0085],[42.1605,12.1175],[42.2575,12.255],[42.317,12.3045],[42.337,12.372],[42.404,12.4685],[42.2785,12.6355],[42.2195,12.7625],[42.0575,12.801],[42.03,12.868],[41.9665,12.9235],[41.8905,13.0745],[41.8105,13.1665],[41.738,13.2365],[41.7215,13.2735],[41.6685,13.3235],[41.6425,13.385],[41.503,13.4455],[41.449,13.4885],[41.403,13.501],[41.309,13.5795],[41.2505,13.6105],[41.172,13.728],[41.1155,13.8275],[40.995,14.0165],[40.9435,14.0805],[40.8105,14.172],[40.5725,14.2695],[40.4515,14.307],[40.365,14.3665],[40.2595,14.411],[40.213,14.387],[40.1475,14.5395],[40.0905,14.548],[40.037,14.5045],[40.0325,14.4785],[39.9305,14.415],[39.8385,14.4905],[39.821,14.4835],[39.765,14.5485],[39.713,14.5555],[39.6435,14.605],[39.5865,14.609],[39.5095,14.552],[39.4895,14.505],[39.4475,14.5055],[39.3705,14.537],[39.36,14.4835],[39.234,14.4515],[39.2635,14.49],[39.241,14.563],[39.2055,14.5655],[39.1575,14.6085],[39.154,14.653],[39.0995,14.6355],[39.0295,14.637],[38.9785,14.545],[38.936,14.54],[38.9005,14.505],[38.7945,14.4665],[38.6895,14.4645],[38.647,14.4425],[38.596,14.442],[38.525,14.4165],[38.4555,14.4135],[38.4295,14.453],[38.319,14.557],[38.271,14.617],[38.2655,14.6705],[38.2325,14.692],[38.1435,14.6755],[38.0915,14.7165],[38.0565,14.7155],[38.0165,14.746],[37.9125,14.894],[37.5275,14.1845],[37.4675,14.2155],[37.4715,14.251],[37.406,14.29],[37.404,14.3345],[37.373,14.3835],[37.312,14.4475],[37.2095,14.44],[37.1355,14.4095],[37.1015,14.336],[37.092,14.2715],[37.014,14.2515],[36.965,14.295],[36.853,14.322],[36.744,14.3225],[36.6955,14.3015],[36.6335,14.3135],[36.5605,14.2575],[36.5285,14.212],[36.4465,13.957],[36.487,13.8395],[36.4075,13.652],[36.3975,13.568],[36.2485,13.368],[36.1555,13.0245],[36.1695,12.984],[36.133,12.9235],[36.1655,12.8755],[36.1405,12.704],[36.081,12.7235],[36.0065,12.724],[35.6995,12.666],[35.6995,12.622],[35.649,12.594],[35.438,12.2505],[35.437,12.21],[35.386,12.17],[35.3465,12.0835],[35.347,12.0385],[35.271,11.9755],[35.269,11.9385],[35.227,11.8955],[35.1345,11.864],[35.081,11.801],[35.0585,11.74],[35.064,11.6545],[35.096,11.583],[35.0875,11.536],[35.0045,11.349],[34.9405,11.249],[35.0075,11.1985],[35.0055,11.1745],[34.9335,10.9565],[34.9785,10.9155],[34.9755,10.8645],[34.8685,10.786],[34.8525,10.7265],[34.798,10.7195],[34.733,10.7695],[34.674,10.8345],[34.5945,10.888],[34.4395,10.7845],[34.357,10.6385],[34.3015,10.5715],[34.293,10.522],[34.348,10.2465],[34.3215,10.164],[34.323,10.117],[34.2315,10.0305],[34.207,9.905],[34.14,9.758],[34.086,9.553],[34.1065,9.5],[34.1455,9.033],[34.1455,8.6075],[34.1005,8.556],[34.045,8.53],[34.0215,8.4885],[33.971,8.502],[33.9005,8.4835],[33.876,8.4555],[33.8765,8.412],[33.809,8.402],[33.7675,8.3675],[33.689,8.3875],[33.6755,8.446],[33.6195,8.4715],[33.561,8.473],[33.423,8.453],[33.3975,8.4265],[33.309,8.466],[33.214,8.4275],[33.187,8.383],[33.2045,8.3315],[33.173,8.301],[33.171,8.2025],[33.193,8.137],[33.1185,8.107],[33.0845,8.0735],[33.078,8.024],[33.0395,8.009],[33.038,7.9675],[32.998,7.9455],[33.0065,7.8565],[33.071,7.7915],[33.1785,7.789],[33.2505,7.778],[33.323,7.7075],[33.437,7.7525],[33.5195,7.728],[33.5815,7.689],[33.6285,7.695],[33.724,7.6565],[33.7855,7.587],[33.866,7.559],[33.8995,7.5125],[34.004,7.4185],[34.0365,7.3555],[34.04,7.272],[34.024,7.2435],[34.074,7.218],[34.105,7.17],[34.19,7.1315],[34.1945,7.0405],[34.2705,7.0025],[34.311,6.9505],[34.372,6.913],[34.4605,6.9195],[34.5305,6.852],[34.5435,6.816],[34.53,6.755],[34.589,6.7285],[34.6245,6.743],[34.7045,6.6915],[34.7185,6.653],[34.765,6.6035],[34.8515,6.6095],[34.9275,6.556],[35.009,6.475],[35.022,6.435],[34.9915,6.3935],[35.0005,6.35],[34.95,6.2465],[34.9735,6.203],[34.964,6.1365],[35.0,6.0805],[35.007,6.021],[34.993,5.945],[35.0065,5.89],[35.13,5.6875],[35.126,5.6245],[35.306,5.5035],[35.3095,5.464],[35.2895,5.378],[35.321,5.336],[35.4115,5.3645],[35.505,5.4235],[35.5675,5.412],[35.628,5.383],[35.7205,5.388],[35.7705,5.351],[35.84,5.336],[35.8635,5.301],[35.828,5.261],[35.866,5.184],[35.8185,5.1005],[35.818,4.774],[35.949,4.631],[35.944,4.619]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/71/Flag_of_Ethiopia.svg","name:en":"Ethiopia","wikidata":"Q115","ISO3166-1:alpha2":"ET","ISO3166-1:alpha3":"ETH","ISO3166-1:numeric":"231"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[43.298,12.7925],[43.151,12.7205],[42.9815,12.6495],[42.9195,12.613],[42.8895,12.617],[42.8595,12.5495],[42.8255,12.541],[42.7975,12.477],[42.825,12.434],[42.764,12.4115],[42.752,12.385],[42.688,12.3625],[42.567,12.476],[42.464,12.524],[42.404,12.4685],[42.337,12.372],[42.317,12.3045],[42.2575,12.255],[42.1605,12.1175],[42.095,12.0085],[41.955,11.812],[41.9,11.793],[41.895,11.756],[41.836,11.733],[41.8075,11.6555],[41.804,11.572],[41.7715,11.494],[41.8075,11.3185],[41.803,11.006],[41.7945,10.9795],[41.9175,10.939],[41.946,10.9155],[42.0685,10.925],[42.11,10.9705],[42.268,10.9715],[42.368,10.998],[42.4255,10.983],[42.5065,11.04],[42.6045,11.07],[42.629,11.0955],[42.688,11.057],[42.7525,11.0795],[42.783,11.0335],[42.7905,10.9885],[42.8675,10.9695],[42.9615,10.9845],[42.967,10.997],[43.228,11.412],[43.3445,11.6225],[43.4255,11.7115],[43.41,11.783],[43.47,11.8055],[43.5495,11.885],[43.6045,12.002],[43.617,12.0545],[43.621,12.1565],[43.5995,12.2885],[43.6535,12.3865],[43.6565,12.453],[43.6365,12.518],[43.5355,12.5525],[43.4305,12.5525],[43.3205,12.5955],[43.29,12.633],[43.273,12.687],[43.298,12.7925]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/34/Flag_of_Djibouti.svg","name:en":"Djibouti","wikidata":"Q977","ISO3166-1:alpha2":"DJ","ISO3166-1:alpha3":"DJI","ISO3166-1:numeric":"262"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[8.5905,37.143],[8.418,37.121],[8.2525,37.153],[7.741,37.176],[7.596,37.2415],[7.413,37.286],[7.223,37.296],[7.122,37.285],[6.756,37.179],[6.615,37.263],[6.5325,37.2845],[6.3765,37.289],[6.2575,37.2595],[6.191,37.2315],[6.0795,37.161],[5.9935,37.061],[5.6455,37.0185],[5.5435,36.9875],[5.173,36.9805],[5.096,37.0145],[4.8825,37.0815],[4.7915,37.097],[4.352,37.1175],[4.186,37.1155],[3.8705,37.1265],[3.678,37.083],[3.288,37.02],[3.0205,37.0215],[2.926,37.0165],[2.803,36.993],[2.71,36.941],[2.3925,36.846],[2.3015,36.846],[1.921,36.7865],[1.7835,36.7875],[1.6415,36.756],[1.478,36.7435],[1.3775,36.7555],[1.2755,36.7465],[1.021,36.69],[0.845,36.634],[0.6915,36.5475],[0.5405,36.507],[0.1985,36.3565],[0.053,36.269],[-0.013,36.2125],[-0.369,36.1125],[-0.4365,36.1065],[-0.579,36.0695],[-0.8205,35.9665],[-0.9525,35.9685],[-1.171,35.9285],[-1.2815,35.8905],[-1.349,35.837],[-1.662,35.4635],[-1.8355,35.3635],[-1.98,35.322],[-2.2065,35.337],[-2.211,35.0585],[-2.1705,35.0095],[-2.1305,34.997],[-2.035,34.9245],[-1.973,34.9355],[-1.9745,34.8875],[-1.8935,34.8415],[-1.8885,34.8065],[-1.7395,34.7435],[-1.771,34.7235],[-1.839,34.628],[-1.7415,34.508],[-1.687,34.4905],[-1.782,34.3915],[-1.7055,34.307],[-1.712,34.2615],[-1.692,34.194],[-1.646,34.1025],[-1.6985,33.8695],[-1.672,33.806],[-1.6765,33.759],[-1.721,33.7405],[-1.7325,33.701],[-1.6465,33.676],[-1.5995,33.6155],[-1.588,33.529],[-1.62,33.49],[-1.6215,33.448],[-1.6635,33.378],[-1.6705,33.284],[-1.6065,33.213],[-1.576,33.149],[-1.462,33.042],[-1.486,32.9755],[-1.544,32.958],[-1.399,32.7625],[-1.2715,32.6955],[-1.0005,32.5195],[-1.063,32.453],[-1.1285,32.41],[-1.188,32.407],[-1.2485,32.3205],[-1.235,32.2895],[-1.241,32.2055],[-1.183,32.1635],[-1.154,32.11],[-1.2215,32.08],[-1.41,32.0875],[-1.509,32.104],[-1.5765,32.092],[-1.749,32.121],[-1.9035,32.1645],[-1.9685,32.1575],[-2.015,32.182],[-2.1325,32.1465],[-2.18,32.142],[-2.2775,32.1685],[-2.5425,32.145],[-2.6,32.113],[-2.8735,32.1115],[-2.931,32.035],[-2.8415,31.8805],[-2.844,31.832],[-2.8205,31.7935],[-3.252,31.7135],[-3.6645,31.6335],[-3.661,31.378],[-3.7645,31.334],[-3.759,31.2635],[-3.7905,31.236],[-3.7595,31.176],[-3.778,31.124],[-3.681,31.165],[-3.674,31.097],[-3.606,31.0765],[-3.5365,31.0135],[-3.5565,30.932],[-3.6515,30.8425],[-3.6295,30.7445],[-3.6445,30.6905],[-3.768,30.626],[-3.817,30.5905],[-3.8865,30.6105],[-4.001,30.593],[-4.1485,30.585],[-4.3295,30.5215],[-4.427,30.4395],[-4.488,30.372],[-4.6075,30.2825],[-4.9365,30.1405],[-5.124,30.003],[-5.2395,29.933],[-5.289,29.881],[-5.3235,29.815],[-5.3175,29.789],[-5.445,29.6335],[-5.5655,29.4765],[-5.651,29.5045],[-5.686,29.541],[-5.7505,29.53],[-5.7275,29.591],[-5.8135,29.6075],[-5.8765,29.5905],[-5.9365,29.5935],[-6.07,29.562],[-6.1655,29.5825],[-6.2475,29.5655],[-6.2895,29.5705],[-6.468,29.56],[-6.5275,29.5215],[-6.6185,29.53],[-6.721,29.5055],[-6.777,29.462],[-6.834,29.468],[-6.9085,29.4955],[-7.0225,29.4935],[-7.15,29.5225],[-7.257,29.474],[-7.345,29.392],[-7.4885,29.36],[-7.6135,29.3655],[-7.6345,29.301],[-7.773,29.255],[-7.822,29.211],[-7.8735,29.1935],[-7.958,29.1215],[-8.04,29.08],[-8.202,28.9845],[-8.3515,28.8855],[-8.458,28.792],[-8.5735,28.7475],[-8.6685,28.667],[-8.669,28.241],[-8.668,27.6665],[-8.668,27.315],[-8.2315,27.057],[-7.5785,26.67],[-7.266,26.483],[-6.833,26.2235],[-6.434,25.9805],[-6.0345,25.736],[-5.3905,25.344],[-4.8335,25.0],[-4.4805,24.7715],[-4.0945,24.5215],[-3.602,24.203],[-2.987,23.805],[-2.5945,23.5505],[-2.054,23.201],[-1.668,22.951],[-1.304,22.7155],[-0.923,22.469],[-0.52,22.208],[-0.061,21.911],[0.505,21.5445],[0.9095,21.2825],[1.1665,21.1165],[1.192,21.0035],[1.1915,20.924],[1.164,20.7905],[1.1785,20.7265],[1.3165,20.74],[1.353,20.677],[1.5035,20.6225],[1.591,20.6005],[1.6425,20.56],[1.675,20.4795],[1.668,20.4065],[1.775,20.337],[1.798,20.298],[1.884,20.2945],[1.8975,20.243],[1.947,20.26],[2.0025,20.2425],[2.037,20.2695],[2.084,20.244],[2.1335,20.2445],[2.192,20.28],[2.194,20.3155],[2.2735,20.299],[2.3125,20.2695],[2.318,20.218],[2.3565,20.1905],[2.4015,20.1175],[2.3975,20.061],[2.4745,20.098],[2.525,20.089],[2.562,20.033],[2.666,20.052],[2.701,20.087],[2.723,20.006],[2.8235,19.9645],[2.857,19.973],[2.977,19.953],[3.0765,19.9055],[3.145,19.8585],[3.2375,19.842],[3.2595,19.8175],[3.23,19.711],[3.2505,19.6495],[3.2325,19.6055],[3.265,19.5305],[3.248,19.498],[3.263,19.439],[3.208,19.431],[3.1785,19.359],[3.2005,19.277],[3.1225,19.185],[3.1245,19.1255],[3.1635,19.1145],[3.276,19.0335],[3.2945,19.003],[3.3575,18.968],[3.8105,19.0585],[4.2665,19.144],[4.336,19.1495],[4.8495,19.2535],[5.179,19.3205],[5.8105,19.447],[5.9875,19.599],[6.1275,19.7125],[6.437,19.9755],[6.4695,20.0085],[6.999,20.455],[7.4595,20.8425],[7.994,21.167],[8.0135,21.1835],[8.4345,21.4395],[8.997,21.7785],[9.051,21.8],[9.351,21.976],[9.756,22.211],[10.2385,22.4965],[10.6715,22.7455],[11.0015,22.9335],[11.2925,23.116],[11.729,23.364],[11.9975,23.517],[11.846,23.806],[11.6045,24.2645],[11.4225,24.2005],[11.1385,24.391],[11.0775,24.411],[10.9815,24.491],[10.9495,24.5325],[10.8965,24.556],[10.821,24.5675],[10.7665,24.5105],[10.706,24.573],[10.6335,24.5665],[10.542,24.534],[10.3565,24.537],[10.3025,24.599],[10.2165,24.6505],[10.153,24.784],[10.106,24.846],[10.0975,24.881],[10.038,24.9655],[10.032,25.3595],[9.751,25.725],[9.5375,26.0025],[9.399,26.1955],[9.456,26.2535],[9.5125,26.385],[9.8635,26.52],[9.9135,26.647],[9.902,26.743],[9.9255,26.862],[9.8625,26.9425],[9.818,27.1495],[9.798,27.1745],[9.7825,27.2565],[9.7945,27.395],[9.8135,27.463],[9.863,27.5045],[9.8885,27.609],[9.9425,27.7325],[9.943,27.836],[9.962,27.886],[9.833,28.286],[9.9025,28.759],[9.8715,29.0275],[9.78,29.425],[9.5385,29.901],[9.415,30.0425],[9.3915,30.1465],[9.4155,30.1875],[9.559,30.229],[9.474,30.559],[9.3595,31.0],[9.187,31.676],[9.0985,31.9995],[9.0655,32.0875],[8.8005,32.236],[8.357,32.5025],[8.3225,32.8235],[8.247,32.9245],[8.116,33.05],[8.1175,33.1],[7.9015,33.1855],[7.832,33.188],[7.786,33.2905],[7.749,33.3275],[7.7365,33.4245],[7.634,33.572],[7.565,33.727],[7.562,33.7775],[7.5255,33.7985],[7.547,33.837],[7.522,33.905],[7.5605,34.0145],[7.5305,34.061],[7.614,34.1135],[7.6515,34.201],[7.723,34.1705],[7.7965,34.209],[7.845,34.329],[7.8565,34.4055],[7.9285,34.4185],[7.959,34.4485],[8.011,34.458],[8.043,34.4935],[8.1155,34.5125],[8.1615,34.569],[8.206,34.5775],[8.244,34.6205],[8.25,34.664],[8.2995,34.7415],[8.253,34.9155],[8.3075,34.947],[8.3155,35.009],[8.3615,35.103],[8.4,35.128],[8.4545,35.22],[8.3125,35.3125],[8.3145,35.3775],[8.3535,35.463],[8.36,35.5255],[8.356,35.67],[8.2645,35.7305],[8.2715,35.9075],[8.291,35.957],[8.2975,36.0415],[8.346,36.097],[8.3095,36.1615],[8.355,36.2335],[8.3395,36.267],[8.372,36.298],[8.4045,36.4235],[8.176,36.4595],[8.1615,36.4985],[8.286,36.538],[8.326,36.574],[8.418,36.6225],[8.447,36.6235],[8.4875,36.6975],[8.4595,36.7515],[8.484,36.7795],[8.5725,36.773],[8.6695,36.8175],[8.6295,36.8605],[8.642,36.941],[8.5905,37.143]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/77/Flag_of_Algeria.svg","name:en":"Algeria","wikidata":"Q262","ISO3166-1:alpha2":"DZ","ISO3166-1:alpha3":"DZA","ISO3166-1:numeric":"012"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1.726,42.5025],[1.7505,42.564],[1.7135,42.615],[1.6005,42.626],[1.5495,42.656],[1.479,42.6515],[1.442,42.6035],[1.425,42.558],[1.4445,42.4415],[1.5525,42.4335],[1.597,42.4675],[1.726,42.5025]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/19/Flag_of_Andorra.svg","name:en":"Andorra","wikidata":"Q228","ISO3166-1:alpha2":"AD","ISO3166-1:alpha3":"AND","ISO3166-1:numeric":"020"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[13.2105,45.453],[13.2555,45.3355],[13.2855,45.1795],[13.2885,45.1155],[13.352,44.964],[13.48,44.805],[13.701,44.5985],[14.128,44.3425],[14.402,44.1865],[14.646,43.992],[14.7825,43.916],[14.8555,43.831],[15.0625,43.6595],[15.2685,43.5375],[15.4375,43.4495],[15.7835,43.339],[15.856,43.3025],[15.92,43.2335],[15.879,43.2145],[15.7675,43.235],[15.666,43.2235],[15.6175,43.256],[15.5165,43.2885],[15.3815,43.2845],[15.299,43.2545],[15.225,43.1955],[15.188,43.12],[15.205,43.017],[15.2425,42.969],[15.3255,42.917],[15.3895,42.898],[15.497,42.8935],[15.578,42.8435],[15.748,42.8065],[15.8125,42.805],[15.9215,42.7605],[16.0035,42.753],[16.1245,42.7765],[16.194,42.816],[16.2335,42.8145],[16.2225,42.716],[16.2785,42.592],[16.1945,42.5875],[16.0855,42.548],[16.0125,42.4795],[15.988,42.4255],[15.9905,42.3395],[16.0675,42.244],[16.1385,42.209],[16.268,42.1825],[16.347,42.1765],[16.433,42.19],[16.5015,42.2185],[16.575,42.2815],[16.61,42.345],[16.6045,42.4355],[16.513,42.5495],[16.5985,42.565],[16.795,42.5225],[16.917,42.5225],[17.185,42.5665],[17.2835,42.5715],[17.398,42.552],[17.4845,42.521],[17.5745,42.514],[17.8665,42.4545],[18.0895,42.388],[18.2035,42.333],[18.272,42.279],[18.4195,42.2095],[18.54,42.39],[18.524,42.4205],[18.434,42.484],[18.438,42.5555],[18.3645,42.6155],[18.25,42.6035],[18.194,42.6555],[18.111,42.6865],[18.0415,42.749],[17.9095,42.8105],[17.844,42.9025],[17.786,42.892],[17.6785,42.9205],[17.642,42.884],[17.5485,42.9235],[17.7105,42.973],[17.639,43.089],[17.45,43.1775],[17.428,43.2175],[17.3425,43.2595],[17.252,43.4005],[17.288,43.44],[17.224,43.498],[17.155,43.4925],[17.0825,43.543],[17.008,43.576],[16.9335,43.636],[16.894,43.6935],[16.8075,43.758],[16.7245,43.796],[16.717,43.8515],[16.667,43.875],[16.5735,43.9475],[16.505,44.026],[16.4365,44.0305],[16.43,44.0835],[16.3095,44.1445],[16.2125,44.217],[16.189,44.3095],[16.218,44.346],[16.14,44.392],[16.123,44.5105],[16.019,44.563],[16.0555,44.611],[15.894,44.7495],[15.835,44.714],[15.7885,44.7455],[15.7285,44.8255],[15.76,44.8695],[15.738,44.9355],[15.783,44.9715],[15.741,45.062],[15.7895,45.1175],[15.7645,45.1665],[15.827,45.2195],[15.9445,45.2135],[15.978,45.2305],[16.086,45.132],[16.109,45.0925],[16.174,45.0705],[16.194,45.031],[16.292,44.9985],[16.3545,45.0035],[16.394,45.1125],[16.4785,45.1615],[16.4915,45.208],[16.603,45.2305],[16.7,45.197],[16.731,45.2075],[16.8435,45.194],[16.936,45.2755],[16.9825,45.23],[17.183,45.1465],[17.2455,45.146],[17.2705,45.188],[17.3425,45.1425],[17.5135,45.1085],[17.6015,45.1065],[17.66,45.1335],[17.84,45.046],[17.899,45.0585],[17.939,45.111],[18.002,45.1515],[18.1115,45.0825],[18.172,45.077],[18.2215,45.097],[18.253,45.1395],[18.326,45.1015],[18.406,45.11],[18.479,45.0615],[18.5805,45.082],[18.713,45.0385],[18.728,44.999],[18.794,44.995],[18.793,44.9345],[18.768,44.9025],[18.856,44.853],[19.019,44.855],[18.989,44.906],[19.143,44.941],[19.0925,44.995],[19.106,45.079],[19.077,45.1135],[19.142,45.13],[19.187,45.166],[19.295,45.173],[19.418,45.1665],[19.4475,45.196],[19.4205,45.2335],[19.26,45.243],[19.1045,45.299],[19.093,45.335],[18.9715,45.377],[19.0235,45.403],[18.987,45.455],[18.997,45.491],[19.0825,45.4885],[19.096,45.5205],[18.984,45.537],[18.9085,45.5675],[18.936,45.6385],[18.9285,45.691],[18.9795,45.7395],[18.9585,45.7675],[18.894,45.7515],[18.8735,45.7845],[18.9185,45.819],[18.8855,45.859],[18.89,45.922],[18.807,45.903],[18.791,45.878],[18.705,45.918],[18.628,45.8735],[18.5555,45.7945],[18.4105,45.739],[18.244,45.761],[18.191,45.788],[18.082,45.7645],[18.0015,45.7945],[17.833,45.792],[17.746,45.828],[17.6725,45.835],[17.6145,45.9095],[17.5655,45.9375],[17.4455,45.9505],[17.3955,45.9305],[17.3745,45.982],[17.313,45.9665],[17.1595,46.1695],[17.006,46.223],[16.973,46.225],[16.887,46.28],[16.88,46.334],[16.801,46.3855],[16.7075,46.403],[16.6645,46.459],[16.597,46.476],[16.5385,46.476],[16.4715,46.5165],[16.3655,46.5465],[16.2415,46.4985],[16.3015,46.3785],[16.189,46.378],[16.148,46.405],[16.0505,46.382],[16.0775,46.348],[16.0015,46.3075],[15.8635,46.2675],[15.7915,46.2585],[15.7855,46.2165],[15.6745,46.227],[15.6075,46.167],[15.6325,46.0835],[15.715,46.058],[15.709,45.931],[15.683,45.8635],[15.629,45.833],[15.5385,45.8475],[15.3,45.7545],[15.2595,45.726],[15.311,45.684],[15.3545,45.7145],[15.39,45.6375],[15.3065,45.6325],[15.301,45.5375],[15.3855,45.4865],[15.329,45.4525],[15.277,45.466],[15.222,45.4255],[15.1665,45.422],[15.0535,45.4945],[14.9795,45.4995],[14.8165,45.4605],[14.8035,45.4955],[14.7205,45.534],[14.698,45.568],[14.5955,45.628],[14.507,45.5945],[14.5005,45.5495],[14.4375,45.511],[14.328,45.472],[14.2455,45.5035],[14.143,45.475],[14.0705,45.4855],[13.9145,45.4535],[13.887,45.426],[13.8145,45.434],[13.7655,45.467],[13.6755,45.4425],[13.601,45.468],[13.467,45.5895],[13.312,45.546],[13.2105,45.453]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/1b/Flag_of_Croatia.svg","name:en":"Croatia","wikidata":"Q224","ISO3166-1:alpha2":"HR","ISO3166-1:alpha3":"HRV","ISO3166-1:numeric":"191"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[20.5945,41.877],[20.5765,41.917],[20.6265,41.961],[20.5945,42.043],[20.5545,42.0825],[20.568,42.121],[20.524,42.2105],[20.4565,42.2725],[20.327,42.3275],[20.2575,42.319],[20.2275,42.4195],[20.165,42.5065],[20.0765,42.556],[20.0175,42.5435],[20.0035,42.5095],[19.9435,42.5175],[19.8315,42.466],[19.7665,42.5015],[19.7435,42.545],[19.7755,42.591],[19.723,42.661],[19.662,42.628],[19.61,42.5755],[19.616,42.5435],[19.563,42.5015],[19.468,42.3895],[19.414,42.351],[19.349,42.2405],[19.281,42.1805],[19.4025,42.1035],[19.3705,42.037],[19.377,41.974],[19.347,41.9135],[19.373,41.8445],[19.2645,41.7495],[19.3105,41.5835],[19.3105,41.5175],[19.2675,41.35],[19.3095,41.1485],[19.257,40.892],[19.1625,40.679],[19.135,40.574],[19.1245,40.4175],[19.2045,40.339],[19.284,40.205],[19.671,39.9875],[19.809,39.9195],[19.8635,39.873],[19.937,39.8395],[19.9735,39.7965],[19.964,39.722],[20.088,39.6825],[20.1345,39.6545],[20.22,39.6465],[20.2315,39.685],[20.322,39.735],[20.295,39.7555],[20.3045,39.8165],[20.39,39.788],[20.4105,39.8445],[20.319,39.974],[20.395,39.998],[20.411,40.0515],[20.4495,40.074],[20.55,40.064],[20.677,40.1025],[20.693,40.154],[20.6785,40.1895],[20.7215,40.216],[20.713,40.283],[20.7895,40.362],[20.787,40.429],[20.8425,40.48],[20.969,40.4885],[20.968,40.5185],[21.043,40.558],[21.0575,40.668],[20.96,40.772],[20.985,40.795],[20.9775,40.8555],[20.9485,40.9225],[20.848,40.9375],[20.8015,40.898],[20.7315,40.911],[20.675,41.08],[20.596,41.092],[20.596,41.133],[20.516,41.2305],[20.498,41.322],[20.5515,41.377],[20.557,41.417],[20.5135,41.4375],[20.469,41.4975],[20.4705,41.559],[20.5545,41.5835],[20.517,41.663],[20.5205,41.7515],[20.573,41.788],[20.5585,41.8675],[20.5945,41.877]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/3/36/Flag_of_Albania.svg","name:en":"Albania","wikidata":"Q222","ISO3166-1:alpha2":"AL","ISO3166-1:alpha3":"ALB","ISO3166-1:numeric":"008"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[19.226,43.528],[19.273,43.5965],[19.3575,43.606],[19.481,43.5715],[19.532,43.668],[19.5185,43.7145],[19.4655,43.7645],[19.395,43.798],[19.294,43.92],[19.2535,43.951],[19.235,44.0085],[19.3105,43.996],[19.3885,43.9605],[19.5265,43.956],[19.5645,43.9995],[19.6185,44.0135],[19.5995,44.0705],[19.512,44.081],[19.491,44.1165],[19.3605,44.1835],[19.328,44.269],[19.2285,44.2645],[19.1325,44.317],[19.103,44.3715],[19.147,44.415],[19.1285,44.52],[19.19,44.547],[19.1835,44.573],[19.303,44.688],[19.318,44.8215],[19.3695,44.8805],[19.3095,44.908],[19.2255,44.8985],[19.175,44.9215],[19.065,44.8615],[19.019,44.855],[18.856,44.853],[18.768,44.9025],[18.793,44.9345],[18.794,44.995],[18.728,44.999],[18.713,45.0385],[18.5805,45.082],[18.479,45.0615],[18.406,45.11],[18.326,45.1015],[18.253,45.1395],[18.2215,45.097],[18.172,45.077],[18.1115,45.0825],[18.002,45.1515],[17.939,45.111],[17.899,45.0585],[17.84,45.046],[17.66,45.1335],[17.6015,45.1065],[17.5135,45.1085],[17.3425,45.1425],[17.2705,45.188],[17.2455,45.146],[17.183,45.1465],[16.9825,45.23],[16.936,45.2755],[16.8435,45.194],[16.731,45.2075],[16.7,45.197],[16.603,45.2305],[16.4915,45.208],[16.4785,45.1615],[16.394,45.1125],[16.3545,45.0035],[16.292,44.9985],[16.194,45.031],[16.174,45.0705],[16.109,45.0925],[16.086,45.132],[15.978,45.2305],[15.9445,45.2135],[15.827,45.2195],[15.7645,45.1665],[15.7895,45.1175],[15.741,45.062],[15.783,44.9715],[15.738,44.9355],[15.76,44.8695],[15.7285,44.8255],[15.7885,44.7455],[15.835,44.714],[15.894,44.7495],[16.0555,44.611],[16.019,44.563],[16.123,44.5105],[16.14,44.392],[16.218,44.346],[16.189,44.3095],[16.2125,44.217],[16.3095,44.1445],[16.43,44.0835],[16.4365,44.0305],[16.505,44.026],[16.5735,43.9475],[16.667,43.875],[16.717,43.8515],[16.7245,43.796],[16.8075,43.758],[16.894,43.6935],[16.9335,43.636],[17.008,43.576],[17.0825,43.543],[17.155,43.4925],[17.224,43.498],[17.288,43.44],[17.252,43.4005],[17.3425,43.2595],[17.428,43.2175],[17.45,43.1775],[17.639,43.089],[17.7105,42.973],[17.5485,42.9235],[17.642,42.884],[17.6785,42.9205],[17.786,42.892],[17.844,42.9025],[17.9095,42.8105],[18.0415,42.749],[18.111,42.6865],[18.194,42.6555],[18.25,42.6035],[18.3645,42.6155],[18.438,42.5555],[18.5,42.588],[18.569,42.656],[18.528,42.724],[18.4735,42.757],[18.458,42.8255],[18.509,42.8865],[18.4825,42.9285],[18.498,42.9955],[18.5435,43.0325],[18.6605,43.0375],[18.6485,43.147],[18.6675,43.204],[18.733,43.281],[18.852,43.324],[18.911,43.3605],[18.96,43.3175],[18.9555,43.2875],[19.009,43.251],[19.0835,43.319],[19.04,43.3655],[19.0045,43.443],[18.961,43.4605],[18.95,43.5035],[18.9885,43.5555],[19.062,43.505],[19.153,43.5365],[19.226,43.528]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/bf/Flag_of_Bosnia_and_Herzegovina.svg","name:en":"Bosnia and Herzegovina","wikidata":"Q225","ISO3166-1:alpha2":"BA","ISO3166-1:alpha3":"BIH","ISO3166-1:numeric":"070"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[33.1665,22.0],[33.413,22.0],[34.08,22.0],[34.215,21.9975],[34.3,22.0055],[34.3535,21.996],[34.475,21.9995],[35.1815,22.0],[35.556,22.0],[36.0995,22.0],[36.485,22.0],[36.6195,22.0055],[36.737,21.999],[37.0905,22.0],[37.1155,22.065],[37.102,22.133],[37.019,22.34],[36.944,22.4315],[36.844,22.4785],[36.797,22.661],[36.7545,22.7365],[36.5545,22.958],[36.5625,23.567],[36.549,23.6335],[36.524,23.677],[36.4665,23.729],[35.9845,24.027],[35.9295,24.214],[35.902,24.2705],[35.835,24.364],[35.788,24.411],[35.533,24.58],[35.41,24.7155],[35.4,24.831],[35.3855,24.8705],[35.0595,25.444],[35.022,25.4885],[34.872,25.6115],[34.782,25.8105],[34.497,26.1935],[34.3245,26.7735],[34.2755,26.8635],[34.2275,26.9225],[34.2165,27.177],[34.253,27.3735],[34.4505,27.611],[34.519,27.7135],[34.477,27.7895],[34.457,27.9005],[34.466,28.025],[34.49,28.0645],[34.536,28.193],[34.545,28.277],[34.5825,28.369],[34.6225,28.432],[34.677,28.579],[34.688,28.6725],[34.7115,28.728],[34.737,28.8415],[34.7335,28.905],[34.7615,28.9525],[34.7625,29.053],[34.816,29.193],[34.8305,29.2695],[34.8845,29.375],[34.921,29.4535],[34.8785,29.5435],[34.8665,29.597],[34.8785,29.6435],[34.8565,29.688],[34.8485,29.759],[34.707,30.1175],[34.6115,30.371],[34.5435,30.413],[34.5575,30.4945],[34.5185,30.5325],[34.519,30.592],[34.497,30.679],[34.4035,30.859],[34.332,31.041],[34.2675,31.22],[34.219,31.3235],[34.069,31.478],[33.971,31.418],[33.887,31.3855],[33.679,31.327],[33.589,31.322],[33.5145,31.359],[33.4105,31.3915],[33.28,31.421],[33.141,31.437],[33.0605,31.427],[32.948,31.3925],[32.835,31.3355],[32.654,31.293],[32.522,31.431],[32.463,31.475],[32.39,31.4995],[32.2395,31.527],[32.113,31.6545],[32.032,31.7025],[31.9485,31.7315],[31.8665,31.7355],[31.77,31.7175],[31.607,31.6595],[31.2705,31.775],[31.1655,31.796],[31.028,31.8005],[30.9605,31.792],[30.499,31.672],[30.373,31.705],[30.2955,31.699],[30.2245,31.6715],[29.976,31.5275],[29.6605,31.317],[29.429,31.2165],[29.301,31.181],[29.046,31.0815],[28.916,31.162],[28.6735,31.242],[28.4365,31.292],[28.0855,31.3005],[28.0635,31.345],[28.0085,31.4015],[27.8915,31.4455],[27.806,31.4415],[27.6055,31.403],[27.544,31.4815],[27.4915,31.5295],[27.3865,31.568],[27.1505,31.5995],[27.0385,31.6485],[26.696,31.7165],[26.295,31.8],[25.9115,31.833],[25.829,31.827],[25.377,31.722],[25.134,31.67],[25.076,31.599],[25.0765,31.551],[24.981,31.5],[24.8625,31.406],[24.8445,31.3625],[24.8635,31.2385],[24.8585,31.155],[24.9035,31.0395],[24.9805,30.9],[25.011,30.782],[24.9455,30.6445],[24.89,30.452],[24.849,30.393],[24.7975,30.348],[24.698,30.2295],[24.65,30.1265],[24.7405,30.0985],[24.8345,29.8765],[24.8125,29.7845],[24.887,29.664],[24.876,29.512],[24.998,29.249],[24.9985,28.8885],[25.0005,28.4295],[25.0025,28.1205],[25.001,27.524],[25.0005,27.0665],[24.999,26.4145],[24.999,25.9845],[24.998,25.513],[24.997,24.7865],[24.9985,24.468],[24.999,23.899],[24.9995,23.5665],[25.001,23.04],[25.0015,22.337],[25.0,22.0],[25.527,22.0],[26.2895,22.0],[26.643,22.0],[27.242,22.0],[27.7535,22.0],[28.316,22.0],[29.024,22.0],[29.5745,22.0],[30.0,22.0],[30.4155,22.0],[31.0,22.0],[31.315,22.0],[31.3385,22.081],[31.3765,22.148],[31.47,22.225],[31.506,22.185],[31.43,22.078],[31.399,22.0],[32.0,22.0],[32.5425,22.0],[33.1665,22.0]]],[[[34.6195,26.308],[34.662,26.1975],[34.745,26.1315],[34.818,26.1045],[34.871,26.1005],[34.952,26.117],[35.001,26.143],[35.057,26.201],[35.086,26.2835],[35.0715,26.373],[35.038,26.4255],[34.9225,26.5015],[34.843,26.515],[34.763,26.5015],[34.667,26.4385],[34.624,26.3545],[34.6195,26.308]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fe/Flag_of_Egypt.svg","name:en":"Egypt","wikidata":"Q79","ISO3166-1:alpha2":"EG","ISO3166-1:alpha3":"EGY","ISO3166-1:numeric":"818"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[26.358,41.711],[26.277,41.7135],[26.234,41.739],[26.1665,41.7475],[26.086,41.717],[26.0605,41.6885],[26.094,41.627],[26.154,41.6055],[26.1475,41.5545],[26.185,41.52],[26.1555,41.4735],[26.1925,41.447],[26.132,41.355],[26.0115,41.3445],[25.9745,41.318],[25.884,41.3045],[25.846,41.3375],[25.6275,41.3055],[25.575,41.3195],[25.532,41.2805],[25.4755,41.2885],[25.3365,41.2415],[25.239,41.2455],[25.1815,41.3075],[25.0825,41.338],[25.056,41.3655],[24.977,41.376],[24.9005,41.4075],[24.8205,41.4015],[24.804,41.3465],[24.7455,41.3835],[24.7305,41.416],[24.608,41.4275],[24.5255,41.5685],[24.4505,41.522],[24.303,41.538],[24.2605,41.573],[24.198,41.539],[24.077,41.5415],[24.0735,41.47],[23.966,41.4395],[23.933,41.471],[23.89,41.45],[23.8005,41.4375],[23.7675,41.396],[23.674,41.4105],[23.6155,41.3755],[23.5255,41.405],[23.401,41.4005],[23.3405,41.3635],[23.3225,41.4],[23.2325,41.376],[23.2205,41.333],[23.18,41.3185],[23.0765,41.32],[22.9275,41.3385],[22.812,41.344],[22.7645,41.322],[22.748,41.1665],[22.706,41.143],[22.667,41.186],[22.618,41.134],[22.416,41.1195],[22.2145,41.17],[22.1285,41.1235],[22.0675,41.1565],[21.932,41.104],[21.914,41.0505],[21.801,40.9745],[21.783,40.9285],[21.707,40.943],[21.649,40.9015],[21.5665,40.8665],[21.5325,40.9075],[21.417,40.9175],[21.3635,40.8805],[21.252,40.8625],[21.2065,40.884],[21.1565,40.8575],[20.9775,40.8555],[20.985,40.795],[20.96,40.772],[21.0575,40.668],[21.043,40.558],[20.968,40.5185],[20.969,40.4885],[20.8425,40.48],[20.787,40.429],[20.7895,40.362],[20.713,40.283],[20.7215,40.216],[20.6785,40.1895],[20.693,40.154],[20.677,40.1025],[20.55,40.064],[20.4495,40.074],[20.411,40.0515],[20.395,39.998],[20.319,39.974],[20.4105,39.8445],[20.39,39.788],[20.3045,39.8165],[20.295,39.7555],[20.322,39.735],[20.2315,39.685],[20.22,39.6465],[20.1345,39.6545],[20.088,39.6825],[19.964,39.722],[19.9735,39.7965],[19.937,39.8395],[19.8635,39.873],[19.809,39.9195],[19.729,39.8995],[19.673,39.9755],[19.6165,39.998],[19.5235,39.995],[19.4705,39.965],[19.3785,39.97],[19.2775,39.9295],[19.249,39.8785],[19.2615,39.8005],[19.3125,39.756],[19.374,39.7375],[19.408,39.6855],[19.4705,39.649],[19.5485,39.646],[19.622,39.57],[19.7045,39.5145],[19.7105,39.4565],[19.813,39.3405],[19.9265,39.3155],[20.0025,39.286],[19.9925,39.2075],[20.037,39.1355],[20.1915,39.0305],[20.2715,39.014],[20.3675,39.051],[20.394,39.139],[20.445,39.113],[20.5105,39.0475],[20.5705,39.0085],[20.597,38.927],[20.555,38.906],[20.4755,38.8165],[20.429,38.712],[20.4095,38.567],[20.4225,38.5185],[20.407,38.465],[20.32,38.438],[20.279,38.3955],[20.264,38.331],[20.2275,38.279],[20.2075,38.186],[20.227,38.1275],[20.308,38.066],[20.422,38.0295],[20.482,37.9945],[20.5615,37.9785],[20.495,37.8925],[20.5005,37.7775],[20.6185,37.655],[20.732,37.5695],[20.808,37.5435],[20.905,37.5565],[20.965,37.597],[21.064,37.614],[21.114,37.6675],[21.1215,37.7315],[21.1795,37.7155],[21.1755,37.66],[21.214,37.569],[21.2915,37.5325],[21.363,37.5395],[21.464,37.482],[21.4985,37.447],[21.5685,37.33],[21.489,37.2705],[21.443,37.181],[21.4455,37.127],[21.4125,37.074],[21.436,36.986],[21.5285,36.933],[21.5725,36.8105],[21.5665,36.7255],[21.608,36.6635],[21.7475,36.6015],[21.8695,36.5895],[21.9765,36.6165],[22.0175,36.6805],[22.073,36.734],[22.097,36.8085],[22.15,36.792],[22.238,36.6535],[22.2425,36.583],[22.2225,36.52],[22.258,36.433],[22.353,36.3725],[22.424,36.2995],[22.512,36.2885],[22.5755,36.317],[22.626,36.3845],[22.629,36.5085],[22.6165,36.543],[22.6885,36.6435],[22.7205,36.596],[22.812,36.5305],[22.811,36.4255],[22.7665,36.334],[22.7745,36.237],[22.7955,36.153],[22.916,36.0175],[23.018,35.993],[23.107,36.04],[23.201,35.989],[23.153,35.937],[23.15,35.8725],[23.201,35.7805],[23.264,35.735],[23.3445,35.7225],[23.41,35.747],[23.4765,35.7075],[23.3805,35.653],[23.3575,35.6185],[23.356,35.556],[23.4335,35.4895],[23.39,35.3115],[23.4035,35.241],[23.4435,35.189],[23.531,35.141],[23.646,35.121],[23.832,35.1385],[23.94,35.12],[24.0045,35.095],[24.17,35.09],[24.2745,35.073],[24.333,35.0805],[24.4525,35.047],[24.4475,34.998],[24.483,34.9325],[24.5455,34.904],[24.6095,34.903],[24.638,34.854],[24.6905,34.8245],[24.7905,34.8185],[24.85,34.831],[24.9695,34.8215],[25.2235,34.8475],[25.38,34.889],[25.492,34.88],[25.5585,34.887],[25.569,34.8285],[25.598,34.7965],[25.6805,34.765],[25.7985,34.7835],[25.8675,34.8535],[25.8645,34.9085],[25.937,34.9295],[26.0015,34.9135],[26.024,34.862],[26.0935,34.819],[26.136,34.815],[26.2095,34.838],[26.2635,34.8885],[26.2785,34.931],[26.34,34.973],[26.388,35.052],[26.4705,35.24],[26.475,35.282],[26.4505,35.3335],[26.709,35.436],[26.7215,35.3415],[26.752,35.283],[26.79,35.2575],[26.8775,35.238],[26.92,35.2425],[27.0595,35.3155],[27.1065,35.2975],[27.2085,35.3115],[27.3155,35.3985],[27.3615,35.4635],[27.3655,35.513],[27.303,35.636],[27.3425,35.711],[27.3645,35.8205],[27.366,35.902],[27.341,35.961],[27.5495,36.1115],[27.631,36.0355],[27.6005,35.967],[27.6,35.9195],[27.633,35.8425],[27.7055,35.782],[27.7885,35.777],[27.9175,35.8305],[27.999,35.9055],[28.034,35.96],[28.106,35.9525],[28.201,36.01],[28.227,36.113],[28.289,36.205],[28.3705,36.363],[28.355,36.485],[28.2825,36.55],[28.2095,36.5615],[28.002,36.4695],[27.9605,36.4705],[27.931,36.5375],[27.8955,36.6975],[27.814,36.7085],[27.775,36.6935],[27.6415,36.5745],[27.462,36.543],[27.3515,36.586],[27.298,36.6335],[27.247,36.726],[27.351,36.8075],[27.3935,36.905],[27.246,36.9425],[27.1875,36.981],[27.1435,37.065],[27.1595,37.2425],[27.1535,37.312],[27.109,37.4575],[27.054,37.564],[26.9585,37.649],[27.0085,37.6965],[27.0865,37.6995],[27.165,37.724],[27.1605,37.7905],[27.101,37.846],[27.054,37.914],[26.9185,37.965],[26.363,38.0955],[26.2215,38.1675],[26.1945,38.2415],[26.213,38.3835],[26.243,38.446],[26.3155,38.453],[26.3125,38.537],[26.216,38.6515],[26.2225,38.688],[26.2665,38.736],[26.324,38.756],[26.451,38.7725],[26.6185,38.809],[26.6465,38.9095],[26.6995,38.98],[26.7045,39.032],[26.674,39.122],[26.432,39.434],[26.143,39.4055],[25.9815,39.396],[25.9275,39.48],[25.928,39.7145],[25.8925,39.8415],[25.8915,39.8905],[25.732,39.9575],[25.6985,39.992],[25.6225,40.129],[25.643,40.199],[25.6935,40.2335],[26.05,40.3715],[25.955,40.7295],[26.083,40.73],[26.125,40.7425],[26.158,40.809],[26.248,40.8815],[26.302,40.9025],[26.328,40.977],[26.3665,41.018],[26.3105,41.0805],[26.332,41.097],[26.3075,41.172],[26.323,41.2535],[26.4075,41.254],[26.4605,41.282],[26.498,41.3285],[26.5865,41.3225],[26.6355,41.3835],[26.591,41.5285],[26.595,41.61],[26.5295,41.6215],[26.471,41.6695],[26.358,41.711]]],[[[20.869,37.2625],[20.896,37.1905],[20.9855,37.146],[21.056,37.1515],[21.117,37.189],[21.1355,37.279],[21.0905,37.337],[21.0485,37.358],[20.9715,37.3625],[20.9225,37.345],[20.8805,37.3045],[20.869,37.2625]]],[[[23.8625,34.9445],[23.8845,34.876],[23.9215,34.8155],[23.976,34.7575],[24.07,34.7095],[24.1315,34.701],[24.214,34.7305],[24.2525,34.7805],[24.2445,34.8815],[24.2085,34.9345],[24.121,34.9695],[24.0355,35.0315],[23.962,35.0375],[23.883,34.994],[23.8625,34.9445]]],[[[29.3105,36.011],[29.4,35.9475],[29.556,35.916],[29.7295,35.923],[29.7005,36.086],[29.6135,36.149],[29.484,36.1855],[29.3105,36.011]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/5/5c/Flag_of_Greece.svg","name:en":"Greece","wikidata":"Q41","ISO3166-1:alpha2":"GR","ISO3166-1:alpha3":"GRC","ISO3166-1:numeric":"300"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[28.324,41.9815],[28.28,42.0865],[28.1675,42.2165],[28.1,42.254],[28.073,42.3175],[28.1665,42.6625],[28.1765,42.7165],[28.17,42.8105],[28.181,42.901],[28.1635,42.983],[28.288,43.1845],[28.428,43.162],[28.5205,43.1645],[28.6465,43.21],[28.763,43.314],[28.8315,43.4005],[28.8775,43.486],[28.8875,43.5395],[28.8545,43.6335],[28.8555,43.7385],[28.579,43.7385],[28.4465,43.7335],[28.3485,43.752],[28.236,43.7585],[27.9945,43.843],[27.944,43.9845],[27.917,44.0085],[27.843,43.966],[27.71,43.9595],[27.6735,44.03],[27.612,44.012],[27.469,44.0225],[27.3995,44.012],[27.356,44.0585],[27.2915,44.075],[27.229,44.1155],[27.135,44.1405],[27.042,44.1445],[26.9015,44.1325],[26.774,44.0805],[26.601,44.0525],[26.5535,44.058],[26.4345,44.0355],[26.3075,44.027],[26.198,43.9855],[26.106,43.968],[26.06,43.91],[25.9525,43.8595],[25.8525,43.759],[25.7855,43.7135],[25.6805,43.691],[25.5655,43.646],[25.5085,43.647],[25.391,43.6185],[25.3435,43.6305],[25.252,43.683],[25.1895,43.697],[25.115,43.6845],[25.007,43.7245],[24.8015,43.709],[24.746,43.6845],[24.623,43.742],[24.501,43.7625],[24.407,43.7365],[24.3495,43.698],[24.1745,43.682],[24.0865,43.702],[23.967,43.7445],[23.8725,43.753],[23.705,43.806],[23.6255,43.7915],[23.5115,43.838],[23.433,43.8495],[23.268,43.846],[23.1405,43.8055],[23.0465,43.7965],[22.881,43.832],[22.841,43.882],[22.8795,43.981],[23.0035,44.0105],[23.0485,44.0625],[23.001,44.095],[22.922,44.1],[22.7915,44.164],[22.7545,44.194],[22.6745,44.216],[22.6475,44.2045],[22.6105,44.1065],[22.6215,44.0635],[22.535,44.052],[22.5225,44.019],[22.4095,44.004],[22.409,43.9395],[22.357,43.8095],[22.3955,43.7595],[22.408,43.696],[22.492,43.639],[22.4875,43.5685],[22.5335,43.471],[22.5975,43.4355],[22.6605,43.426],[22.6795,43.3945],[22.7665,43.3775],[22.8275,43.329],[22.8485,43.2755],[22.8965,43.227],[23.0055,43.186],[22.983,43.111],[22.919,43.0775],[22.8985,43.036],[22.7855,42.9825],[22.78,42.932],[22.7445,42.886],[22.6815,42.872],[22.6195,42.896],[22.5355,42.882],[22.441,42.806],[22.487,42.749],[22.4675,42.641],[22.438,42.572],[22.5475,42.507],[22.559,42.485],[22.52,42.3975],[22.478,42.3935],[22.4555,42.3335],[22.3605,42.311],[22.434,42.258],[22.528,42.14],[22.6805,42.0635],[22.798,42.0455],[22.867,42.022],[22.874,41.9535],[22.9025,41.876],[22.928,41.862],[22.965,41.778],[23.0085,41.769],[23.0335,41.7225],[22.9515,41.642],[22.973,41.56],[22.958,41.4945],[22.9795,41.4465],[22.952,41.4175],[22.9655,41.3535],[22.9275,41.3385],[23.0765,41.32],[23.18,41.3185],[23.2205,41.333],[23.2325,41.376],[23.3225,41.4],[23.3405,41.3635],[23.401,41.4005],[23.5255,41.405],[23.6155,41.3755],[23.674,41.4105],[23.7675,41.396],[23.8005,41.4375],[23.89,41.45],[23.933,41.471],[23.966,41.4395],[24.0735,41.47],[24.077,41.5415],[24.198,41.539],[24.2605,41.573],[24.303,41.538],[24.4505,41.522],[24.5255,41.5685],[24.608,41.4275],[24.7305,41.416],[24.7455,41.3835],[24.804,41.3465],[24.8205,41.4015],[24.9005,41.4075],[24.977,41.376],[25.056,41.3655],[25.0825,41.338],[25.1815,41.3075],[25.239,41.2455],[25.3365,41.2415],[25.4755,41.2885],[25.532,41.2805],[25.575,41.3195],[25.6275,41.3055],[25.846,41.3375],[25.884,41.3045],[25.9745,41.318],[26.0115,41.3445],[26.132,41.355],[26.1925,41.447],[26.1555,41.4735],[26.185,41.52],[26.1475,41.5545],[26.154,41.6055],[26.094,41.627],[26.0605,41.6885],[26.086,41.717],[26.1665,41.7475],[26.234,41.739],[26.277,41.7135],[26.358,41.711],[26.3295,41.752],[26.369,41.821],[26.5385,41.8235],[26.584,41.902],[26.561,41.9255],[26.641,41.972],[26.755,41.96],[26.787,41.9895],[26.833,41.969],[26.871,41.991],[26.9575,41.997],[27.0145,42.045],[27.022,42.079],[27.107,42.086],[27.1915,42.0605],[27.2215,42.098],[27.3125,42.0865],[27.3765,42.046],[27.393,42.007],[27.447,41.967],[27.5745,41.9375],[27.714,41.977],[27.778,41.966],[27.8685,41.9995],[27.9075,41.975],[28.0405,41.9815],[28.324,41.9815]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9a/Flag_of_Bulgaria.svg","name:en":"Bulgaria","wikidata":"Q219","ISO3166-1:alpha2":"BG","ISO3166-1:alpha3":"BGR","ISO3166-1:numeric":"100"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[43.474,41.123],[43.568,41.145],[43.597,41.1295],[43.6845,41.134],[43.7505,41.1095],[43.8415,41.161],[43.9385,41.176],[43.9635,41.165],[44.076,41.196],[44.162,41.189],[44.186,41.2435],[44.271,41.2125],[44.3445,41.2265],[44.4765,41.182],[44.596,41.1935],[44.616,41.2405],[44.7915,41.2205],[44.807,41.2875],[44.9615,41.256],[45.014,41.2965],[45.093,41.339],[45.165,41.4065],[45.309,41.473],[45.3915,41.4275],[45.4545,41.455],[45.564,41.407],[45.601,41.3735],[45.6485,41.379],[45.7405,41.344],[45.7005,41.298],[45.7415,41.2585],[45.817,41.222],[45.887,41.211],[45.953,41.178],[46.1895,41.192],[46.2915,41.174],[46.369,41.1195],[46.458,41.098],[46.4925,41.0555],[46.55,41.1125],[46.631,41.098],[46.66,41.131],[46.6675,41.219],[46.7,41.2415],[46.697,41.3195],[46.6165,41.3905],[46.5225,41.4115],[46.331,41.5015],[46.2975,41.572],[46.1955,41.63],[46.2075,41.6535],[46.1815,41.7345],[46.216,41.7635],[46.3045,41.7755],[46.3985,41.8445],[46.422,41.908],[46.3995,41.94],[46.3205,41.9295],[46.246,41.979],[46.237,42.016],[46.1615,41.9905],[46.0385,42.02],[45.9875,42.0455],[45.9295,42.032],[45.9215,42.081],[45.8555,42.1135],[45.786,42.102],[45.748,42.146],[45.6475,42.1865],[45.609,42.234],[45.652,42.292],[45.7535,42.3095],[45.7345,42.3785],[45.7745,42.4285],[45.7755,42.4885],[45.7215,42.4755],[45.6875,42.5025],[45.614,42.5055],[45.5715,42.548],[45.4935,42.535],[45.4185,42.5515],[45.3545,42.522],[45.2835,42.606],[45.252,42.62],[45.1585,42.7075],[45.086,42.7115],[45.053,42.6925],[44.98,42.745],[44.8875,42.747],[44.804,42.6145],[44.7665,42.643],[44.753,42.714],[44.68,42.756],[44.5,42.734],[44.394,42.7065],[44.3195,42.723],[44.2495,42.681],[44.225,42.6305],[44.162,42.6065],[44.1155,42.6175],[44.0105,42.5915],[43.9625,42.5475],[43.8365,42.5985],[43.7705,42.589],[43.715,42.6365],[43.759,42.655],[43.804,42.715],[43.779,42.758],[43.6775,42.802],[43.609,42.8135],[43.561,42.8675],[43.185,42.9385],[43.173,42.963],[43.051,43.019],[42.99,43.119],[42.853,43.179],[42.7765,43.189],[42.698,43.172],[42.6665,43.136],[42.617,43.1635],[42.5435,43.1765],[42.4475,43.22],[42.442,43.251],[42.358,43.247],[42.3365,43.2195],[42.253,43.2125],[42.1995,43.228],[42.038,43.188],[41.867,43.244],[41.804,43.202],[41.7455,43.237],[41.6845,43.217],[41.6215,43.222],[41.5435,43.2705],[41.413,43.3245],[41.363,43.3685],[41.288,43.337],[41.221,43.3805],[41.0395,43.3995],[41.0045,43.432],[40.95,43.4195],[40.8935,43.457],[40.662,43.563],[40.5665,43.519],[40.478,43.549],[40.2995,43.57],[40.2615,43.5855],[40.0905,43.5555],[40.0085,43.3855],[39.8845,43.2245],[40.0135,43.171],[40.058,43.08],[40.1675,43.012],[40.297,42.995],[40.4435,42.9295],[40.64,42.89],[40.7395,42.8925],[40.8015,42.8365],[40.89,42.777],[40.934,42.6885],[41.0435,42.62],[41.1125,42.6045],[41.222,42.601],[41.2665,42.4955],[41.2945,42.3545],[41.3815,42.2155],[41.381,42.142],[41.4145,42.0595],[41.4805,41.9695],[41.5075,41.893],[41.487,41.824],[41.4375,41.7945],[41.3435,41.701],[41.2995,41.612],[41.6355,41.4865],[41.747,41.4715],[41.824,41.4315],[41.8815,41.4605],[41.9555,41.5235],[42.043,41.494],[42.085,41.51],[42.203,41.492],[42.2745,41.494],[42.439,41.4415],[42.5195,41.4365],[42.513,41.472],[42.573,41.51],[42.601,41.585],[42.684,41.6],[42.7745,41.5785],[42.8365,41.585],[42.8015,41.497],[42.8995,41.479],[42.967,41.4515],[43.0215,41.3785],[43.109,41.3495],[43.132,41.321],[43.2125,41.2905],[43.1925,41.253],[43.247,41.177],[43.3675,41.2035],[43.4485,41.1765],[43.474,41.123]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/0f/Flag_of_Georgia.svg","name:en":"Georgia","wikidata":"Q230","ISO3166-1:alpha2":"GE","ISO3166-1:alpha3":"GEO","ISO3166-1:numeric":"268"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[50.9195,26.2145],[50.923,26.271],[50.872,26.391],[50.8075,26.4715],[50.8055,26.527],[50.773,26.5975],[50.719,26.649],[50.655,26.6785],[50.5955,26.687],[50.5565,26.621],[50.4885,26.5845],[50.3875,26.53],[50.3765,26.4075],[50.3415,26.3735],[50.3165,26.252],[50.311,26.171],[50.27,26.0795],[50.304,25.879],[50.366,25.819],[50.4165,25.6985],[50.529,25.594],[50.5675,25.576],[50.58,25.586],[50.7345,25.5725],[50.7695,25.547],[50.7985,25.6225],[50.8395,25.6385],[50.868,25.7495],[50.816,25.895],[50.85,26.011],[50.9075,26.077],[50.9195,26.2145]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/2c/Flag_of_Bahrain.svg","name:en":"Bahrain","wikidata":"Q398","ISO3166-1:alpha2":"BH","ISO3166-1:alpha3":"BHR","ISO3166-1:numeric":"048"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[44.7635,39.7145],[44.888,39.7485],[44.9425,39.7265],[45.007,39.7505],[45.061,39.7945],[45.1345,39.7435],[45.187,39.677],[45.172,39.593],[45.243,39.6115],[45.342,39.542],[45.492,39.5115],[45.5905,39.57],[45.6235,39.5635],[45.6945,39.6035],[45.799,39.5715],[45.838,39.4685],[45.804,39.428],[45.7925,39.369],[45.8615,39.3565],[45.9985,39.2915],[45.9895,39.2005],[46.031,39.0925],[46.07,39.078],[46.1015,39.016],[46.1095,38.954],[46.1495,38.8895],[46.1405,38.8445],[46.187,38.843],[46.2825,38.9005],[46.363,38.9165],[46.425,38.8855],[46.458,38.897],[46.534,38.867],[46.5125,38.936],[46.546,39.0035],[46.531,39.0945],[46.44,39.1705],[46.462,39.1995],[46.544,39.1755],[46.5895,39.2355],[46.547,39.26],[46.494,39.336],[46.448,39.3395],[46.39,39.388],[46.3855,39.433],[46.4585,39.4495],[46.5055,39.483],[46.51,39.525],[46.5735,39.541],[46.5665,39.572],[46.489,39.587],[46.434,39.569],[46.4125,39.631],[46.3345,39.636],[46.2495,39.601],[46.1715,39.615],[46.155,39.651],[46.08,39.685],[45.969,39.792],[45.941,39.7875],[45.826,39.826],[45.7805,39.947],[45.701,39.968],[45.611,39.974],[45.6215,40.021],[45.796,40.0255],[45.834,39.99],[45.8805,40.021],[45.912,40.0755],[45.904,40.104],[45.955,40.135],[45.9785,40.1905],[45.9595,40.2755],[45.9305,40.297],[45.7855,40.329],[45.697,40.367],[45.652,40.367],[45.5895,40.4305],[45.512,40.4555],[45.4385,40.52],[45.4595,40.5815],[45.388,40.6185],[45.3635,40.655],[45.43,40.7465],[45.5455,40.7745],[45.608,40.8505],[45.602,40.8825],[45.4765,40.93],[45.454,40.969],[45.3685,41.008],[45.251,41.0135],[45.215,41.0385],[45.0755,41.069],[45.099,41.12],[45.15,41.1135],[45.202,41.138],[45.127,41.1995],[45.0515,41.193],[45.052,41.2395],[45.014,41.2965],[44.9615,41.256],[44.807,41.2875],[44.7915,41.2205],[44.616,41.2405],[44.596,41.1935],[44.4765,41.182],[44.3445,41.2265],[44.271,41.2125],[44.186,41.2435],[44.162,41.189],[44.076,41.196],[43.9635,41.165],[43.9385,41.176],[43.8415,41.161],[43.7505,41.1095],[43.6845,41.134],[43.597,41.1295],[43.568,41.145],[43.474,41.123],[43.447,41.099],[43.4685,41.0305],[43.5975,40.9835],[43.6715,40.9365],[43.6785,40.8445],[43.707,40.82],[43.7485,40.731],[43.6865,40.593],[43.611,40.514],[43.558,40.4945],[43.56,40.459],[43.6055,40.438],[43.62,40.398],[43.591,40.346],[43.6765,40.2605],[43.714,40.1615],[43.6485,40.132],[43.6785,40.096],[43.761,40.084],[43.8965,40.0215],[43.994,40.032],[44.1705,40.0275],[44.2745,40.0495],[44.351,40.0275],[44.4805,39.967],[44.554,39.9005],[44.652,39.7955],[44.711,39.77],[44.7635,39.7145]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/2f/Flag_of_Armenia.svg","name:en":"Armenia","wikidata":"Q399","ISO3166-1:alpha2":"AM","ISO3166-1:alpha3":"ARM","ISO3166-1:numeric":"051"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[3.08,51.551],[2.991,51.5215],[2.917,51.4755],[2.737,51.4045],[2.6145,51.3455],[2.389,51.2685],[2.546,51.0885],[2.574,51.0035],[2.633,50.946],[2.604,50.9165],[2.635,50.813],[2.718,50.8135],[2.7895,50.729],[2.899,50.694],[2.9385,50.745],[3.019,50.7735],[3.1485,50.79],[3.245,50.713],[3.241,50.669],[3.2885,50.526],[3.3905,50.498],[3.6075,50.497],[3.663,50.456],[3.6655,50.347],[3.838,50.3545],[3.9005,50.327],[3.9675,50.35],[4.036,50.3435],[4.1225,50.298],[4.201,50.275],[4.1555,50.163],[4.197,50.135],[4.231,50.0735],[4.136,50.021],[4.1965,49.9555],[4.31,49.9695],[4.4455,49.937],[4.51,49.947],[4.5975,49.9865],[4.6845,49.997],[4.704,50.0965],[4.824,50.161],[4.8755,50.154],[4.8675,50.0965],[4.827,50.05],[4.791,49.958],[4.849,49.948],[4.89,49.909],[4.851,49.8655],[4.856,49.792],[5.0085,49.782],[5.095,49.762],[5.125,49.727],[5.206,49.696],[5.2685,49.6965],[5.3535,49.6305],[5.425,49.5985],[5.449,49.517],[5.482,49.506],[5.5935,49.522],[5.6645,49.553],[5.7005,49.54],[5.8185,49.5465],[5.9095,49.6645],[5.887,49.7095],[5.792,49.787],[5.755,49.7915],[5.736,49.897],[5.775,49.961],[5.832,49.9765],[5.819,50.013],[5.901,50.116],[5.9615,50.1315],[5.9645,50.1715],[6.025,50.183],[6.1195,50.1635],[6.1375,50.13],[6.19,50.1875],[6.1765,50.236],[6.37,50.3125],[6.4005,50.3435],[6.343,50.3805],[6.3775,50.4415],[6.309,50.5015],[6.23,50.497],[6.197,50.5305],[6.235,50.565],[6.2665,50.642],[6.1915,50.64],[6.1145,50.7225],[6.033,50.727],[6.021,50.7545],[5.8865,50.77],[5.8075,50.756],[5.7765,50.782],[5.695,50.755],[5.694,50.8115],[5.654,50.82],[5.6515,50.875],[5.7145,50.9085],[5.756,50.9575],[5.758,51.0335],[5.8225,51.0925],[5.847,51.1415],[5.767,51.1835],[5.658,51.1845],[5.5605,51.2225],[5.558,51.2625],[5.4875,51.2995],[5.4175,51.2625],[5.226,51.2685],[5.242,51.305],[5.1315,51.347],[5.071,51.3935],[5.1045,51.4315],[5.079,51.4715],[4.9285,51.396],[4.83,51.421],[4.8405,51.4785],[4.774,51.505],[4.6665,51.4445],[4.5355,51.423],[4.538,51.4825],[4.4425,51.4685],[4.3915,51.408],[4.242,51.354],[4.1665,51.293],[4.064,51.2475],[4.0115,51.244],[3.8865,51.2],[3.7915,51.214],[3.7955,51.256],[3.591,51.3045],[3.5155,51.287],[3.5275,51.246],[3.4485,51.2415],[3.3945,51.2655],[3.3585,51.315],[3.3855,51.334],[3.295,51.449],[3.08,51.551]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/65/Flag_of_Belgium.svg","name:en":"Belgium","wikidata":"Q31","ISO3166-1:alpha2":"BE","ISO3166-1:alpha3":"BEL","ISO3166-1:numeric":"056"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[9.5505,47.537],[9.7255,47.533],[9.7665,47.589],[9.816,47.5475],[9.959,47.5345],[10.0375,47.489],[10.1055,47.4285],[10.1195,47.3755],[10.182,47.3925],[10.237,47.3735],[10.174,47.27],[10.3305,47.305],[10.4385,47.4135],[10.476,47.433],[10.4315,47.504],[10.4545,47.556],[10.5735,47.534],[10.598,47.5695],[10.751,47.5375],[10.7775,47.514],[10.8905,47.5375],[10.9095,47.4855],[11.006,47.394],[11.1155,47.3955],[11.2055,47.434],[11.291,47.427],[11.442,47.516],[11.5725,47.5145],[11.605,47.5795],[11.781,47.591],[12.013,47.623],[12.14,47.6055],[12.208,47.614],[12.1675,47.6825],[12.183,47.7005],[12.3635,47.684],[12.429,47.6965],[12.5125,47.6255],[12.575,47.6325],[12.6035,47.672],[12.732,47.6795],[12.8075,47.6105],[12.7945,47.557],[12.9085,47.497],[13.001,47.464],[13.0475,47.492],[13.0435,47.5835],[13.0955,47.6285],[13.0785,47.688],[13.046,47.7125],[12.933,47.7095],[12.9395,47.7785],[13.0015,47.8545],[12.8575,48.007],[12.7705,48.066],[12.765,48.129],[12.848,48.168],[12.873,48.2025],[12.9595,48.2105],[13.0215,48.258],[13.1955,48.2955],[13.2615,48.295],[13.4135,48.377],[13.445,48.5285],[13.5085,48.5905],[13.593,48.5695],[13.727,48.513],[13.8075,48.5845],[13.8265,48.6285],[13.801,48.7165],[13.8395,48.7715],[13.769,48.8315],[13.737,48.8795],[13.6715,48.88],[13.58,48.9705],[13.507,48.969],[13.4025,48.987],[13.392,49.0545],[13.289,49.1185],[13.2365,49.1135],[13.0805,49.2475],[13.034,49.264],[13.029,49.3045],[12.95,49.343],[12.803,49.3415],[12.7575,49.395],[12.656,49.435],[12.644,49.5215],[12.574,49.56],[12.528,49.618],[12.5215,49.6865],[12.44,49.706],[12.403,49.762],[12.467,49.7855],[12.5475,49.9205],[12.4785,49.9355],[12.4605,49.995],[12.3675,50.0195],[12.1995,50.1105],[12.201,50.1885],[12.1545,50.2285],[12.1305,50.319],[12.1845,50.322],[12.2015,50.273],[12.294,50.221],[12.3325,50.234],[12.3985,50.3215],[12.4925,50.356],[12.5125,50.3965],[12.6275,50.416],[12.7075,50.408],[12.8215,50.459],[12.935,50.4125],[13.018,50.445],[13.056,50.501],[13.208,50.522],[13.2485,50.592],[13.4125,50.6195],[13.4625,50.602],[13.5445,50.677],[13.6675,50.7315],[13.8575,50.728],[13.907,50.7925],[13.992,50.8195],[14.035,50.805],[14.305,50.884],[14.3885,50.9005],[14.397,50.9365],[14.3105,50.955],[14.2625,50.9855],[14.292,51.0465],[14.454,51.036],[14.599,50.987],[14.5605,50.925],[14.65,50.9315],[14.619,50.858],[14.7995,50.8225],[14.8235,50.8705],[14.8965,50.9405],[14.9795,51.0775],[14.993,51.1625],[15.038,51.244],[15.033,51.2945],[14.9775,51.3415],[14.9465,51.472],[14.737,51.5255],[14.712,51.5615],[14.766,51.6105],[14.7475,51.6755],[14.6565,51.7405],[14.644,51.7965],[14.5905,51.838],[14.693,51.901],[14.721,51.9525],[14.746,52.0815],[14.683,52.1125],[14.716,52.233],[14.6895,52.257],[14.594,52.274],[14.534,52.396],[14.635,52.4975],[14.604,52.529],[14.638,52.5745],[14.596,52.6105],[14.468,52.6595],[14.3505,52.7515],[14.21,52.818],[14.1415,52.824],[14.158,52.8765],[14.1435,52.9615],[14.2355,52.993],[14.338,53.0465],[14.3875,53.1425],[14.3775,53.202],[14.4495,53.2595],[14.3735,53.409],[14.3715,53.4565],[14.3025,53.5535],[14.317,53.618],[14.284,53.6345],[14.267,53.6985],[14.2835,53.7725],[14.2125,53.8675],[14.242,53.9875],[14.168,54.239],[14.0695,54.2775],[14.0785,54.441],[14.0225,54.6075],[13.9975,54.649],[13.9085,54.7195],[13.65,54.839],[13.5455,54.8735],[13.427,54.885],[13.2025,54.867],[13.111,54.845],[12.9715,54.786],[12.749,54.744],[12.322,54.578],[12.2585,54.451],[12.165,54.3855],[11.9515,54.3335],[11.6445,54.331],[11.5385,54.4075],[11.311,54.5295],[11.1415,54.577],[10.9795,54.556],[10.82,54.5475],[10.7545,54.5145],[10.592,54.5205],[10.339,54.593],[10.1695,54.738],[10.0515,54.766],[9.8945,54.842],[9.74,54.8235],[9.6045,54.8545],[9.383,54.839],[9.342,54.8035],[9.25,54.8095],[9.2405,54.85],[9.1555,54.8705],[9.0475,54.872],[8.948,54.9025],[8.6865,54.909],[8.5555,54.921],[8.557,54.993],[8.3955,55.0695],[8.29,55.065],[8.0445,55.099],[7.985,55.0045],[7.9455,54.9115],[7.931,54.7535],[7.946,54.651],[7.9745,54.587],[8.151,54.346],[7.9985,54.387],[7.862,54.394],[7.721,54.376],[7.576,54.303],[7.5355,54.252],[7.5245,54.1675],[7.5505,54.1135],[7.698,53.984],[7.533,53.9605],[7.3665,53.949],[7.29,53.93],[7.1125,53.9195],[6.96,53.885],[6.727,53.8635],[6.592,53.8165],[6.5005,53.8025],[6.3955,53.7615],[6.346,53.7245],[6.4125,53.6045],[6.5515,53.5825],[6.699,53.4935],[6.8825,53.4475],[6.9025,53.3535],[7.0525,53.3065],[7.1915,53.315],[7.2175,53.198],[7.179,53.1385],[7.203,53.1135],[7.213,53.011],[7.1815,52.9415],[7.0715,52.8105],[7.055,52.6445],[6.94,52.638],[6.897,52.6515],[6.742,52.6455],[6.715,52.626],[6.7265,52.563],[6.6975,52.4865],[6.7745,52.4595],[6.8545,52.4595],[6.9415,52.4355],[6.994,52.4655],[7.072,52.352],[7.0265,52.292],[7.0615,52.2345],[6.9815,52.2215],[6.8555,52.1205],[6.7605,52.119],[6.751,52.085],[6.688,52.04],[6.8265,51.9935],[6.8285,51.964],[6.722,51.896],[6.675,51.916],[6.4645,51.855],[6.407,51.829],[6.2795,51.874],[6.1665,51.8405],[6.0555,51.8525],[5.945,51.8235],[6.029,51.726],[6.0365,51.673],[6.1095,51.647],[6.0915,51.606],[6.212,51.5135],[6.2235,51.475],[6.2055,51.3995],[6.2265,51.3605],[6.1695,51.3295],[6.0725,51.2425],[6.0915,51.1345],[5.938,51.035],[5.955,50.9885],[6.0175,50.9835],[6.018,50.9345],[6.0925,50.9175],[6.074,50.8465],[6.019,50.8465],[6.021,50.7545],[6.033,50.727],[6.1145,50.7225],[6.1915,50.64],[6.2665,50.642],[6.235,50.565],[6.197,50.5305],[6.23,50.497],[6.309,50.5015],[6.3775,50.4415],[6.343,50.3805],[6.4005,50.3435],[6.37,50.3125],[6.1765,50.236],[6.19,50.1875],[6.1375,50.13],[6.112,50.0595],[6.1985,49.9485],[6.2615,49.881],[6.364,49.8505],[6.397,49.8225],[6.5165,49.805],[6.506,49.7165],[6.431,49.67],[6.4215,49.622],[6.3745,49.5915],[6.356,49.5295],[6.3675,49.4695],[6.428,49.477],[6.552,49.425],[6.588,49.3835],[6.589,49.322],[6.6615,49.282],[6.7115,49.1885],[6.834,49.1515],[6.9185,49.2225],[7.035,49.1915],[7.067,49.1145],[7.245,49.13],[7.2985,49.1175],[7.3655,49.172],[7.44,49.1835],[7.5285,49.097],[7.57,49.0795],[7.868,49.0335],[7.9315,49.058],[8.07,48.9965],[8.216,48.9755],[8.129,48.8795],[8.106,48.8245],[7.894,48.6655],[7.8415,48.6435],[7.803,48.5905],[7.8045,48.5125],[7.7705,48.4915],[7.737,48.4065],[7.7455,48.3325],[7.6945,48.3025],[7.668,48.2245],[7.577,48.118],[7.5705,48.0315],[7.622,47.9725],[7.5595,47.8825],[7.5315,47.7865],[7.548,47.74],[7.5195,47.6675],[7.589,47.59],[7.6975,47.533],[7.7955,47.5575],[7.8225,47.588],[7.892,47.5875],[7.9165,47.548],[8.099,47.562],[8.1845,47.6045],[8.288,47.6105],[8.325,47.5725],[8.4315,47.5665],[8.467,47.6415],[8.4055,47.674],[8.473,47.764],[8.6115,47.802],[8.6825,47.7835],[8.7235,47.7455],[8.728,47.6925],[8.7925,47.675],[8.824,47.711],[8.899,47.648],[9.0265,47.6865],[9.256,47.659],[9.445,47.595],[9.5145,47.537],[9.5505,47.537]]],[[[7.409,54.1365],[7.409,54.0075],[7.55,54.0275],[7.4485,54.1385],[7.409,54.1365]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/ba/Flag_of_Germany.svg","name:en":"Germany","wikidata":"Q183","ISO3166-1:alpha2":"DE","ISO3166-1:alpha3":"DEU","ISO3166-1:numeric":"276"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[9.5305,47.2705],[9.585,47.2055],[9.573,47.1755],[9.626,47.146],[9.6345,47.101],[9.607,47.061],[9.68,47.062],[9.8735,47.0065],[9.88,46.935],[10.0265,46.896],[10.0595,46.861],[10.123,46.8485],[10.232,46.8665],[10.2415,46.9315],[10.3095,46.95],[10.3555,46.9925],[10.488,46.9385],[10.4695,46.855],[10.5705,46.8425],[10.6625,46.8745],[10.763,46.8205],[10.814,46.775],[11.021,46.7665],[11.072,46.856],[11.187,46.9695],[11.3205,46.9925],[11.3825,46.9705],[11.4425,46.9765],[11.479,47.011],[11.538,46.984],[11.627,47.0135],[11.7645,46.9725],[11.9315,47.037],[12.02,47.047],[12.186,47.092],[12.227,47.082],[12.2045,47.027],[12.126,47.013],[12.1385,46.9565],[12.211,46.8775],[12.2665,46.887],[12.306,46.8335],[12.309,46.785],[12.3575,46.775],[12.384,46.7165],[12.5485,46.659],[12.6905,46.657],[12.9335,46.6095],[13.0905,46.601],[13.193,46.5725],[13.322,46.553],[13.5045,46.5665],[13.714,46.523],[13.7935,46.5055],[13.912,46.521],[14.0195,46.4815],[14.1215,46.4765],[14.1915,46.443],[14.308,46.4305],[14.4295,46.447],[14.447,46.4215],[14.54,46.4115],[14.675,46.4505],[14.759,46.5045],[14.8185,46.5095],[14.844,46.577],[14.8865,46.613],[14.9795,46.6015],[15.016,46.641],[15.1105,46.6595],[15.236,46.6395],[15.414,46.6555],[15.464,46.6145],[15.5435,46.632],[15.5465,46.667],[15.65,46.706],[15.7215,46.696],[15.8625,46.722],[15.95,46.688],[16.0425,46.686],[15.985,46.753],[15.986,46.8275],[16.114,46.869],[16.2465,46.9495],[16.3025,46.9985],[16.4525,47.003],[16.5205,47.0615],[16.4645,47.099],[16.517,47.1495],[16.428,47.185],[16.442,47.2485],[16.4895,47.2805],[16.4455,47.4065],[16.588,47.423],[16.662,47.4555],[16.6525,47.5005],[16.7055,47.523],[16.653,47.622],[16.59,47.6175],[16.5145,47.646],[16.477,47.6905],[16.552,47.7225],[16.548,47.7515],[16.6375,47.756],[16.721,47.7355],[16.749,47.6815],[16.913,47.688],[17.093,47.7085],[17.0705,47.728],[17.072,47.808],[17.01,47.8585],[17.078,47.8775],[17.1125,47.9275],[17.0945,47.971],[17.161,48.0065],[17.0675,48.0315],[17.0775,48.106],[16.976,48.172],[16.895,48.313],[16.843,48.3515],[16.85,48.4495],[16.954,48.5435],[16.94,48.6165],[16.9055,48.714],[16.776,48.712],[16.6745,48.7715],[16.462,48.8045],[16.4095,48.7445],[16.36,48.728],[16.2685,48.7415],[16.093,48.747],[15.9945,48.7795],[15.8275,48.871],[15.7535,48.852],[15.6865,48.857],[15.6195,48.8955],[15.5135,48.914],[15.4665,48.9515],[15.367,48.982],[15.291,48.9845],[15.269,48.9585],[15.1895,48.943],[15.1585,48.99],[15.0645,48.9995],[15.0215,49.0205],[14.976,48.971],[14.993,48.904],[14.973,48.8745],[14.956,48.758],[14.8255,48.784],[14.7945,48.73],[14.741,48.7015],[14.706,48.591],[14.6635,48.582],[14.607,48.6285],[14.391,48.5935],[14.342,48.5545],[14.1955,48.5925],[14.054,48.604],[14.053,48.6525],[14.0035,48.7085],[13.9555,48.715],[13.8395,48.7715],[13.801,48.7165],[13.8265,48.6285],[13.8075,48.5845],[13.727,48.513],[13.593,48.5695],[13.5085,48.5905],[13.445,48.5285],[13.4135,48.377],[13.2615,48.295],[13.1955,48.2955],[13.0215,48.258],[12.9595,48.2105],[12.873,48.2025],[12.848,48.168],[12.765,48.129],[12.7705,48.066],[12.8575,48.007],[13.0015,47.8545],[12.9395,47.7785],[12.933,47.7095],[13.046,47.7125],[13.0785,47.688],[13.0955,47.6285],[13.0435,47.5835],[13.0475,47.492],[13.001,47.464],[12.9085,47.497],[12.7945,47.557],[12.8075,47.6105],[12.732,47.6795],[12.6035,47.672],[12.575,47.6325],[12.5125,47.6255],[12.429,47.6965],[12.3635,47.684],[12.183,47.7005],[12.1675,47.6825],[12.208,47.614],[12.14,47.6055],[12.013,47.623],[11.781,47.591],[11.605,47.5795],[11.5725,47.5145],[11.442,47.516],[11.291,47.427],[11.2055,47.434],[11.1155,47.3955],[11.006,47.394],[10.9095,47.4855],[10.8905,47.5375],[10.7775,47.514],[10.751,47.5375],[10.598,47.5695],[10.5735,47.534],[10.4545,47.556],[10.4315,47.504],[10.476,47.433],[10.4385,47.4135],[10.3305,47.305],[10.174,47.27],[10.237,47.3735],[10.182,47.3925],[10.1195,47.3755],[10.1055,47.4285],[10.0375,47.489],[9.959,47.5345],[9.816,47.5475],[9.7665,47.589],[9.7255,47.533],[9.5505,47.537],[9.593,47.466],[9.6545,47.4545],[9.652,47.4085],[9.5835,47.3125],[9.5305,47.2705]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/41/Flag_of_Austria.svg","name:en":"Austria","wikidata":"Q40","ISO3166-1:alpha2":"AT","ISO3166-1:alpha3":"AUT","ISO3166-1:numeric":"040"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[14.8235,50.8705],[14.7995,50.8225],[14.619,50.858],[14.65,50.9315],[14.5605,50.925],[14.599,50.987],[14.454,51.036],[14.292,51.0465],[14.2625,50.9855],[14.3105,50.955],[14.397,50.9365],[14.3885,50.9005],[14.305,50.884],[14.035,50.805],[13.992,50.8195],[13.907,50.7925],[13.8575,50.728],[13.6675,50.7315],[13.5445,50.677],[13.4625,50.602],[13.4125,50.6195],[13.2485,50.592],[13.208,50.522],[13.056,50.501],[13.018,50.445],[12.935,50.4125],[12.8215,50.459],[12.7075,50.408],[12.6275,50.416],[12.5125,50.3965],[12.4925,50.356],[12.3985,50.3215],[12.3325,50.234],[12.294,50.221],[12.2015,50.273],[12.1845,50.322],[12.1305,50.319],[12.1545,50.2285],[12.201,50.1885],[12.1995,50.1105],[12.3675,50.0195],[12.4605,49.995],[12.4785,49.9355],[12.5475,49.9205],[12.467,49.7855],[12.403,49.762],[12.44,49.706],[12.5215,49.6865],[12.528,49.618],[12.574,49.56],[12.644,49.5215],[12.656,49.435],[12.7575,49.395],[12.803,49.3415],[12.95,49.343],[13.029,49.3045],[13.034,49.264],[13.0805,49.2475],[13.2365,49.1135],[13.289,49.1185],[13.392,49.0545],[13.4025,48.987],[13.507,48.969],[13.58,48.9705],[13.6715,48.88],[13.737,48.8795],[13.769,48.8315],[13.8395,48.7715],[13.9555,48.715],[14.0035,48.7085],[14.053,48.6525],[14.054,48.604],[14.1955,48.5925],[14.342,48.5545],[14.391,48.5935],[14.607,48.6285],[14.6635,48.582],[14.706,48.591],[14.741,48.7015],[14.7945,48.73],[14.8255,48.784],[14.956,48.758],[14.973,48.8745],[14.993,48.904],[14.976,48.971],[15.0215,49.0205],[15.0645,48.9995],[15.1585,48.99],[15.1895,48.943],[15.269,48.9585],[15.291,48.9845],[15.367,48.982],[15.4665,48.9515],[15.5135,48.914],[15.6195,48.8955],[15.6865,48.857],[15.7535,48.852],[15.8275,48.871],[15.9945,48.7795],[16.093,48.747],[16.2685,48.7415],[16.36,48.728],[16.4095,48.7445],[16.462,48.8045],[16.6745,48.7715],[16.776,48.712],[16.9055,48.714],[16.94,48.6165],[17.043,48.7635],[17.089,48.786],[17.115,48.8335],[17.1925,48.8745],[17.242,48.8685],[17.3965,48.813],[17.477,48.8415],[17.52,48.813],[17.633,48.855],[17.704,48.861],[17.7815,48.9255],[17.8855,48.9275],[17.9245,49.02],[18.0245,49.021],[18.082,49.0465],[18.116,49.092],[18.1065,49.134],[18.184,49.287],[18.297,49.3045],[18.379,49.3305],[18.492,49.435],[18.548,49.468],[18.5455,49.5005],[18.7105,49.5025],[18.7545,49.4885],[18.851,49.517],[18.8045,49.679],[18.719,49.684],[18.636,49.715],[18.5695,49.8345],[18.604,49.857],[18.431,49.938],[18.278,49.9635],[18.2065,49.998],[18.117,49.994],[18.103,50.0225],[17.954,50.005],[17.8685,49.9725],[17.7775,50.02],[17.75,50.0775],[17.6765,50.103],[17.5925,50.16],[17.7585,50.2065],[17.689,50.302],[17.6105,50.266],[17.3425,50.281],[17.29,50.3175],[17.1105,50.405],[16.9835,50.42],[16.908,50.4495],[16.8605,50.411],[16.908,50.391],[16.9405,50.32],[17.02,50.2785],[16.999,50.216],[16.898,50.222],[16.81,50.1895],[16.7835,50.1455],[16.706,50.0965],[16.641,50.112],[16.5605,50.1645],[16.5485,50.23],[16.4895,50.264],[16.361,50.3795],[16.2675,50.3795],[16.199,50.442],[16.3605,50.501],[16.445,50.5795],[16.343,50.6615],[16.235,50.6715],[16.188,50.6275],[16.1035,50.6635],[16.029,50.604],[15.9885,50.685],[15.8635,50.68],[15.816,50.7555],[15.7055,50.7375],[15.579,50.779],[15.5245,50.777],[15.439,50.809],[15.392,50.776],[15.353,50.8505],[15.277,50.891],[15.292,50.9535],[15.2295,50.9965],[15.105,50.9915],[15.0165,51.022],[15.0175,50.967],[14.9895,50.9215],[15.002,50.869],[14.8235,50.8705]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/cb/Flag_of_the_Czech_Republic.svg","name:en":"Czechia","wikidata":"Q213","ISO3166-1:alpha2":"CZ","ISO3166-1:alpha3":"CZE","ISO3166-1:numeric":"203"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[26.44,59.9385],[26.1535,59.9],[26.0215,59.815],[25.5785,59.8265],[24.4815,59.7035],[23.9535,59.5765],[23.5215,59.4815],[23.1915,59.4835],[23.125,59.464],[22.7335,59.2915],[22.496,59.2885],[22.3085,59.245],[21.85,59.0565],[21.6515,58.9185],[21.6965,58.8315],[21.6065,58.6885],[21.514,58.6475],[21.453,58.5985],[21.4215,58.5365],[21.3825,58.3015],[21.4035,58.2565],[21.6135,57.895],[21.7165,57.7865],[21.843,57.763],[21.916,57.7495],[22.1435,57.779],[22.5835,57.9275],[22.7075,57.941],[22.999,57.702],[23.181,57.5865],[23.406,57.594],[23.5825,57.6695],[23.6485,57.783],[23.601,57.899],[24.2095,57.899],[24.261,57.917],[24.357,57.8745],[24.46,57.88],[24.4625,57.925],[24.585,57.962],[24.6285,57.942],[24.742,57.982],[24.8325,57.972],[25.0205,58.018],[25.106,58.078],[25.2165,58.074],[25.3035,58.0345],[25.467,58.005],[25.486,57.975],[25.7035,57.9035],[25.7725,57.9125],[25.8205,57.865],[26.0585,57.8365],[26.027,57.775],[26.136,57.751],[26.2055,57.715],[26.275,57.5975],[26.4015,57.572],[26.4695,57.5755],[26.5,57.5245],[26.6145,57.5295],[26.676,57.5665],[26.782,57.5725],[26.9455,57.603],[27.0315,57.6045],[27.1175,57.562],[27.2255,57.5545],[27.3515,57.518],[27.3305,57.5705],[27.405,57.6125],[27.3765,57.668],[27.4475,57.717],[27.5215,57.716],[27.5055,57.7665],[27.5415,57.823],[27.6155,57.839],[27.741,57.8275],[27.813,57.86],[27.6565,57.9555],[27.6215,58.005],[27.602,58.1175],[27.4915,58.2245],[27.487,58.3035],[27.553,58.366],[27.357,58.7875],[27.731,58.973],[27.745,59.027],[27.792,59.07],[27.8085,59.1295],[27.865,59.161],[27.9015,59.2385],[27.954,59.2705],[28.1295,59.291],[28.21,59.3705],[28.191,59.4005],[28.0425,59.4715],[27.857,59.582],[26.915,59.6315],[26.44,59.9385]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/8f/Flag_of_Estonia.svg","name:en":"Estonia","wikidata":"Q191","ISO3166-1:alpha2":"EE","ISO3166-1:alpha3":"EST","ISO3166-1:numeric":"233"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[24.1445,65.3955],[24.1545,65.2925],[24.084,65.08],[23.8685,64.733],[23.836,64.6655],[23.206,64.5045],[23.0625,64.4455],[23.002,64.388],[22.961,64.22],[22.5965,64.1375],[22.397,64.072],[22.213,63.952],[22.12,63.8475],[21.8845,63.701],[21.66,63.6725],[21.509,63.667],[21.376,63.633],[20.9385,63.5195],[20.6965,63.483],[20.401,63.332],[20.276,63.2435],[20.166,63.165],[20.4185,62.7245],[20.665,62.366],[20.8115,61.987],[20.903,61.646],[20.7715,61.127],[20.4375,60.902],[20.025,60.7275],[19.6355,60.6945],[19.22,60.6115],[19.1965,60.517],[19.161,60.375],[19.083,60.1915],[19.6585,59.7915],[19.9405,59.595],[19.9945,59.591],[20.2845,59.466],[20.384,59.4575],[20.761,59.5325],[21.4345,59.4785],[21.5335,59.4765],[22.3965,59.513],[22.6345,59.608],[22.8575,59.6355],[23.352,59.657],[24.206,59.7835],[24.4885,59.817],[24.7855,59.892],[24.93,59.9215],[25.1695,59.9435],[25.5825,59.928],[26.0175,59.977],[26.075,60.014],[26.2945,60.0465],[26.492,60.1535],[26.611,60.162],[26.7655,60.2005],[27.294,60.2005],[27.455,60.2235],[27.725,60.3915],[27.686,60.4335],[27.747,60.451],[27.7735,60.5335],[27.8735,60.6045],[28.1355,60.741],[28.5245,60.957],[28.658,60.951],[28.714,61.0445],[28.819,61.1215],[28.957,61.1515],[28.9855,61.1735],[29.2335,61.2685],[29.3345,61.355],[29.503,61.4615],[29.7405,61.5735],[29.8185,61.6555],[30.0385,61.765],[30.0705,61.8175],[30.1555,61.858],[30.305,61.964],[30.423,62.0225],[30.602,62.1415],[30.6565,62.2085],[30.7205,62.209],[31.1385,62.442],[31.222,62.4985],[31.3455,62.6405],[31.439,62.7855],[31.5865,62.9085],[31.463,63.0245],[31.2655,63.115],[31.24,63.218],[31.1485,63.2615],[30.979,63.308],[30.9345,63.3555],[30.85,63.3685],[30.7875,63.4055],[30.484,63.4665],[30.3845,63.5455],[30.244,63.6075],[29.972,63.757],[30.2605,63.822],[30.321,63.9095],[30.447,63.983],[30.528,64.049],[30.5535,64.132],[30.4665,64.2045],[30.4825,64.2625],[30.21,64.3505],[30.11,64.365],[30.045,64.402],[30.0585,64.451],[29.987,64.534],[29.9895,64.5875],[30.136,64.649],[30.0415,64.741],[30.043,64.793],[29.7395,64.79],[29.645,64.8665],[29.5995,64.995],[29.627,65.0605],[29.7325,65.0915],[29.897,65.105],[29.8195,65.1445],[29.8935,65.193],[29.764,65.229],[29.635,65.2315],[29.602,65.26],[29.7465,65.3475],[29.733,65.4725],[29.864,65.5605],[29.7225,65.637],[30.017,65.6965],[30.1385,65.6685],[30.068,65.895],[29.9975,65.979],[29.924,66.126],[29.7095,66.268],[29.5245,66.4905],[29.3505,66.644],[29.1185,66.8015],[29.061,66.8525],[29.033,66.9255],[29.0735,66.996],[29.5155,67.281],[29.527,67.311],[29.717,67.394],[29.9305,67.523],[30.0155,67.673],[29.6595,67.803],[29.327,68.0745],[28.646,68.1965],[28.434,68.5395],[28.707,68.7325],[28.8015,68.8695],[28.661,68.8865],[28.468,68.8855],[28.4955,68.93],[28.9295,69.052],[28.8055,69.111],[28.8315,69.2245],[29.2195,69.3975],[29.3365,69.4785],[29.1705,69.639],[29.134,69.6955],[28.404,69.8185],[28.3455,69.881],[28.1605,69.921],[27.9845,70.014],[27.9595,70.092],[27.5745,70.067],[27.5235,70.023],[27.437,70.0205],[27.3055,69.958],[27.0315,69.911],[26.986,69.935],[26.7325,69.9465],[26.676,69.9645],[26.4665,69.94],[26.3605,69.845],[26.136,69.7385],[26.007,69.723],[25.8925,69.6655],[25.976,69.61],[25.876,69.529],[25.793,69.421],[25.8465,69.394],[25.742,69.319],[25.694,69.1955],[25.742,69.145],[25.7205,69.109],[25.7775,69.0195],[25.627,68.8925],[25.4815,68.905],[25.2675,68.851],[25.157,68.8],[25.0765,68.622],[24.917,68.605],[24.6085,68.682],[24.3025,68.7175],[24.0755,68.78],[23.9835,68.827],[23.7755,68.819],[23.6735,68.7055],[23.4405,68.692],[23.1675,68.6285],[23.046,68.6895],[22.801,68.6875],[22.5355,68.7445],[22.3745,68.7165],[22.341,68.827],[22.192,68.919],[22.176,68.9565],[21.7235,69.2145],[21.627,69.2765],[21.279,69.312],[21.096,69.261],[20.9875,69.192],[21.1085,69.104],[21.0575,69.0365],[20.7175,69.12],[20.5485,69.06],[20.775,69.0325],[20.9135,68.9605],[20.844,68.9365],[20.997,68.8965],[21.234,68.814],[21.3195,68.7595],[21.3895,68.765],[21.43,68.6915],[21.565,68.6755],[21.7015,68.6305],[21.7015,68.5965],[21.89,68.5845],[22.018,68.496],[22.153,68.47],[22.351,68.4805],[22.466,68.4415],[22.804,68.393],[22.918,68.3335],[23.0705,68.2995],[23.153,68.231],[23.1415,68.1545],[23.2785,68.1575],[23.3965,68.044],[23.531,68.0065],[23.663,67.942],[23.641,67.915],[23.51,67.88],[23.493,67.664],[23.559,67.6195],[23.545,67.583],[23.408,67.5015],[23.4105,67.4675],[23.5365,67.46],[23.7635,67.426],[23.718,67.385],[23.775,67.3395],[23.731,67.2875],[23.5835,67.2695],[23.596,67.207],[23.557,67.166],[23.6535,67.104],[23.674,67.065],[23.8565,66.956],[23.864,66.922],[23.9945,66.8235],[23.978,66.784],[23.8795,66.762],[23.908,66.7215],[23.8685,66.657],[23.8605,66.5595],[23.65,66.454],[23.689,66.3875],[23.646,66.3005],[23.7335,66.206],[23.916,66.162],[23.9365,66.0795],[24.0375,66.009],[24.042,65.9635],[24.153,65.8625],[24.132,65.7715],[24.172,65.7255],[24.1775,65.6605],[24.132,65.5155],[24.1445,65.3955]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/bc/Flag_of_Finland.svg","name:en":"Finland","wikidata":"Q33","ISO3166-1:alpha2":"FI","ISO3166-1:alpha3":"FIN","ISO3166-1:numeric":"246"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[102.621,11.3895],[102.5795,11.296],[102.5755,11.24],[102.6245,10.653],[102.684,9.9175],[102.697,9.8545],[102.7305,9.7985],[102.939,9.549],[103.003,9.497],[103.118,9.4755],[103.7785,9.949],[103.86,10.0045],[103.935,10.154],[103.901,10.2615],[103.8265,10.291],[103.8005,10.3675],[103.838,10.402],[103.914,10.3905],[103.9265,10.4305],[103.9715,10.48],[104.011,10.4805],[104.0885,10.4205],[104.1105,10.3835],[104.1245,10.232],[104.2275,10.2715],[104.439,10.423],[104.5505,10.4555],[104.588,10.5275],[104.6945,10.537],[104.753,10.518],[104.869,10.522],[104.897,10.547],[104.957,10.638],[105.0985,10.721],[105.0655,10.7805],[105.0295,10.8925],[105.083,10.9565],[105.1075,10.9185],[105.1575,10.9225],[105.2385,10.893],[105.265,10.897],[105.344,10.8665],[105.4335,10.9725],[105.4955,10.9485],[105.5365,10.952],[105.6675,10.9875],[105.73,11.0255],[105.7795,11.0315],[105.807,10.9735],[105.8645,10.8995],[105.8485,10.864],[105.9,10.8445],[105.9725,10.8935],[106.0725,10.811],[106.1655,10.8115],[106.1865,10.842],[106.14,10.915],[106.154,10.983],[106.206,10.978],[106.188,11.0505],[106.154,11.102],[106.1215,11.0885],[106.023,11.1405],[106.014,11.1905],[105.9415,11.2055],[105.8755,11.2875],[105.885,11.361],[105.874,11.4055],[105.8855,11.5325],[105.8625,11.5655],[105.8095,11.5965],[105.8525,11.662],[105.884,11.677],[105.9535,11.6405],[106.016,11.7285],[106.024,11.7745],[106.069,11.776],[106.1155,11.743],[106.19,11.754],[106.2495,11.7285],[106.3125,11.674],[106.3685,11.697],[106.4405,11.669],[106.4535,11.6905],[106.418,11.77],[106.465,11.8725],[106.412,11.9735],[106.4555,11.989],[106.496,11.969],[106.724,11.9755],[106.7915,12.0815],[106.927,12.0635],[106.99,12.085],[107.054,12.1425],[107.1005,12.2065],[107.14,12.2335],[107.1555,12.278],[107.2305,12.296],[107.268,12.3245],[107.363,12.3195],[107.387,12.271],[107.434,12.246],[107.447,12.291],[107.544,12.351],[107.5435,12.3995],[107.58,12.4955],[107.5895,12.5595],[107.5695,12.642],[107.582,12.6795],[107.559,12.716],[107.562,12.7925],[107.492,12.8975],[107.4845,12.9445],[107.5045,12.976],[107.496,13.028],[107.548,13.17],[107.6275,13.3665],[107.617,13.5265],[107.5745,13.6205],[107.5705,13.6625],[107.536,13.7405],[107.4515,13.7945],[107.4555,13.98],[107.3865,13.9895],[107.3655,14.013],[107.367,14.0835],[107.3385,14.128],[107.3775,14.1795],[107.4095,14.2645],[107.391,14.3265],[107.4465,14.403],[107.488,14.406],[107.532,14.5465],[107.5225,14.588],[107.5595,14.623],[107.5565,14.686],[107.469,14.6215],[107.4375,14.524],[107.329,14.5915],[107.2875,14.5795],[107.2535,14.5225],[107.2085,14.4885],[107.165,14.416],[107.0835,14.395],[107.0775,14.4265],[107.0155,14.397],[106.967,14.314],[106.895,14.3335],[106.838,14.294],[106.7175,14.422],[106.6395,14.4485],[106.612,14.4975],[106.5705,14.521],[106.546,14.59],[106.5235,14.5915],[106.438,14.5235],[106.4355,14.4815],[106.4045,14.45],[106.3295,14.439],[106.264,14.4785],[106.2415,14.4645],[106.2375,14.3985],[106.1875,14.3515],[106.0955,14.373],[106.023,14.312],[106.027,14.246],[106.0515,14.2025],[106.1075,14.188],[106.1215,14.1095],[106.1875,14.063],[106.168,14.0195],[106.1,13.979],[106.1055,13.9095],[105.996,13.9305],[105.911,13.9305],[105.7925,14.0235],[105.7885,14.0775],[105.699,14.094],[105.5565,14.1605],[105.4435,14.1085],[105.3615,14.1065],[105.3245,14.159],[105.282,14.171],[105.247,14.26],[105.206,14.3125],[105.2065,14.3435],[105.151,14.2835],[105.154,14.2545],[105.1025,14.213],[105.05,14.215],[105.026,14.238],[105.0005,14.305],[105.0025,14.362],[104.8795,14.412],[104.8335,14.4025],[104.806,14.4375],[104.7525,14.4055],[104.6985,14.4275],[104.641,14.423],[104.5645,14.354],[104.5,14.3765],[104.4765,14.361],[104.307,14.383],[104.2775,14.408],[104.217,14.3675],[104.1715,14.359],[104.108,14.3785],[104.0935,14.3535],[104.029,14.34],[104.0095,14.356],[103.9,14.338],[103.837,14.371],[103.78,14.3615],[103.7005,14.392],[103.707,14.433],[103.6555,14.4395],[103.642,14.4145],[103.578,14.4335],[103.497,14.4105],[103.4675,14.3645],[103.435,14.391],[103.402,14.358],[103.2665,14.3495],[103.219,14.3235],[103.1885,14.3345],[103.079,14.294],[103.022,14.2285],[102.952,14.207],[102.941,14.1525],[102.9005,14.0835],[102.9085,14.021],[102.869,14.005],[102.783,13.933],[102.7665,13.8565],[102.6885,13.752],[102.5805,13.7005],[102.549,13.6595],[102.5715,13.635],[102.57,13.586],[102.5375,13.567],[102.439,13.56],[102.3665,13.578],[102.334,13.5405],[102.363,13.506],[102.3625,13.4245],[102.345,13.347],[102.3495,13.2775],[102.386,13.218],[102.399,13.1565],[102.494,13.014],[102.488,12.944],[102.508,12.8975],[102.5325,12.7675],[102.5005,12.7385],[102.511,12.67],[102.567,12.658],[102.6435,12.612],[102.6775,12.543],[102.7655,12.457],[102.7875,12.4155],[102.726,12.3645],[102.7175,12.2225],[102.705,12.18],[102.734,12.111],[102.769,12.0755],[102.785,11.9765],[102.815,11.9335],[102.8195,11.8925],[102.8665,11.8065],[102.9115,11.7455],[102.914,11.6545],[102.621,11.3895]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/83/Flag_of_Cambodia.svg","name:en":"Cambodia","wikidata":"Q424","ISO3166-1:alpha2":"KH","ISO3166-1:alpha3":"KHM","ISO3166-1:numeric":"116"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[89.14,21.4265],[89.2645,21.4835],[89.585,21.5995],[89.824,21.7035],[89.9065,21.7525],[89.946,21.793],[89.9785,21.866],[90.0585,21.838],[90.083,21.815],[90.1655,21.788],[90.2345,21.8015],[90.267,21.824],[90.392,21.7825],[90.5315,21.812],[90.607,21.8155],[90.689,21.853],[90.773,21.83],[90.8595,21.8385],[90.9125,21.8835],[90.9495,21.943],[91.051,21.9545],[91.109,21.972],[91.176,22.016],[91.2725,22.113],[91.273,22.314],[91.3205,22.358],[91.3825,22.3645],[91.4805,22.31],[91.645,22.3505],[91.6615,22.3305],[91.652,22.23],[91.6895,22.1725],[91.719,22.0605],[91.7105,21.7775],[91.7155,21.4965],[91.7465,21.4495],[91.836,21.3755],[91.909,21.284],[91.906,21.161],[91.9355,21.0905],[92.03,20.9705],[92.13,20.7375],[92.0945,20.627],[92.1105,20.5505],[92.1875,20.434],[92.2565,20.391],[92.338,20.3755],[92.3985,20.385],[92.4245,20.4815],[92.4285,20.565],[92.363,20.5895],[92.343,20.649],[92.3715,20.7025],[92.34,20.819],[92.272,20.947],[92.2535,21.021],[92.2585,21.0775],[92.176,21.16],[92.218,21.2355],[92.1975,21.328],[92.2585,21.363],[92.27,21.4305],[92.3245,21.4195],[92.3425,21.4665],[92.381,21.4795],[92.425,21.433],[92.4275,21.381],[92.475,21.3545],[92.5175,21.3885],[92.564,21.372],[92.5945,21.2495],[92.674,21.2835],[92.643,21.408],[92.618,21.4695],[92.5985,21.66],[92.5995,21.7945],[92.6215,21.8755],[92.601,21.982],[92.5665,22.1415],[92.599,22.137],[92.591,22.255],[92.543,22.491],[92.5165,22.7225],[92.479,22.7535],[92.4545,22.82],[92.4645,22.8545],[92.4195,22.911],[92.378,22.9295],[92.374,22.971],[92.3925,23.0565],[92.361,23.0985],[92.354,23.236],[92.402,23.2365],[92.381,23.352],[92.3535,23.39],[92.3165,23.4865],[92.306,23.583],[92.273,23.6435],[92.291,23.681],[92.279,23.7265],[92.205,23.705],[92.141,23.7325],[92.068,23.648],[92.0005,23.673],[91.978,23.735],[91.9295,23.683],[91.9495,23.617],[91.966,23.484],[91.8535,23.4065],[91.813,23.349],[91.7625,23.3095],[91.8085,23.135],[91.834,23.094],[91.7835,23.037],[91.7215,22.989],[91.627,22.956],[91.5775,22.9675],[91.544,23.0],[91.5515,23.0385],[91.4975,23.147],[91.503,23.1935],[91.449,23.261],[91.418,23.2685],[91.384,23.193],[91.3835,23.1555],[91.412,23.077],[91.3705,23.0715],[91.3255,23.1585],[91.325,23.2385],[91.2915,23.319],[91.251,23.4765],[91.202,23.523],[91.1605,23.639],[91.2065,23.65],[91.21,23.6875],[91.1575,23.69],[91.1695,23.7485],[91.21,23.747],[91.2535,23.8345],[91.231,23.8815],[91.272,23.9605],[91.3225,23.993],[91.361,23.984],[91.398,24.05],[91.376,24.1055],[91.474,24.099],[91.586,24.0715],[91.6435,24.1135],[91.6455,24.1705],[91.7255,24.144],[91.762,24.1545],[91.75,24.241],[91.8235,24.2085],[91.9115,24.144],[91.9475,24.2645],[91.923,24.3335],[91.996,24.3875],[92.091,24.371],[92.169,24.4355],[92.1545,24.4795],[92.17,24.5305],[92.2145,24.503],[92.26,24.683],[92.2975,24.7345],[92.27,24.832],[92.244,24.8425],[92.249,24.909],[92.317,24.895],[92.424,24.853],[92.499,24.904],[92.4845,24.9325],[92.4205,24.967],[92.428,25.0315],[92.347,25.05],[92.224,25.0995],[92.193,25.14],[92.0345,25.1885],[91.9825,25.1705],[91.915,25.181],[91.791,25.1655],[91.7575,25.174],[91.6945,25.134],[91.636,25.127],[91.5755,25.172],[91.548,25.1495],[91.44,25.151],[91.328,25.1765],[91.269,25.2045],[91.1795,25.1955],[91.082,25.198],[90.816,25.151],[90.776,25.176],[90.741,25.1585],[90.5235,25.1745],[90.4385,25.147],[90.3835,25.154],[90.289,25.1955],[90.1105,25.2245],[89.904,25.3105],[89.838,25.296],[89.8145,25.374],[89.861,25.5155],[89.8825,25.6215],[89.8355,25.7155],[89.8265,25.81],[89.833,25.8705],[89.8875,25.945],[89.8255,25.9455],[89.85,25.99],[89.8215,26.01],[89.7655,26.1285],[89.6875,26.1815],[89.6185,26.1795],[89.601,26.1295],[89.6295,26.1165],[89.6435,26.063],[89.589,26.039],[89.5865,25.9805],[89.54,25.97],[89.5165,26.0095],[89.4635,25.998],[89.4275,26.013],[89.3405,26.0155],[89.254,26.064],[89.2285,26.123],[89.155,26.139],[89.148,26.209],[89.125,26.264],[89.1355,26.3095],[89.105,26.3265],[89.09,26.392],[89.0375,26.4],[88.9575,26.4575],[88.911,26.3705],[88.9825,26.309],[89.04,26.281],[89.0455,26.241],[88.9535,26.242],[88.9185,26.288],[88.8755,26.2865],[88.8385,26.2325],[88.803,26.3055],[88.6675,26.272],[88.702,26.336],[88.65,26.4295],[88.5605,26.461],[88.448,26.5355],[88.397,26.626],[88.35,26.5105],[88.4155,26.4695],[88.4845,26.459],[88.524,26.36],[88.434,26.335],[88.3495,26.2825],[88.3475,26.221],[88.325,26.2045],[88.1775,26.148],[88.1585,26.095],[88.1855,26.0635],[88.1775,26.0215],[88.1415,26.0145],[88.111,25.934],[88.0855,25.9145],[88.102,25.8285],[88.172,25.787],[88.2365,25.81],[88.357,25.7215],[88.402,25.673],[88.454,25.6645],[88.45,25.6045],[88.548,25.5175],[88.603,25.516],[88.6465,25.478],[88.7105,25.4815],[88.7595,25.5265],[88.8035,25.5245],[88.838,25.3695],[88.9055,25.338],[88.9155,25.312],[89.0095,25.2985],[88.952,25.247],[88.949,25.181],[88.875,25.179],[88.8315,25.206],[88.7995,25.171],[88.7155,25.207],[88.6205,25.2055],[88.559,25.192],[88.477,25.213],[88.444,25.198],[88.463,25.0795],[88.438,25.009],[88.3975,24.969],[88.396,24.9375],[88.3425,24.8705],[88.264,24.8855],[88.2295,24.958],[88.1695,24.9515],[88.152,24.9065],[88.165,24.8615],[88.109,24.8125],[88.063,24.7255],[88.008,24.668],[88.0755,24.6335],[88.1065,24.573],[88.111,24.524],[88.226,24.469],[88.3655,24.412],[88.498,24.321],[88.577,24.3165],[88.652,24.294],[88.7065,24.303],[88.7395,24.2445],[88.744,24.1875],[88.7005,24.153],[88.699,24.0845],[88.7455,24.0325],[88.723,23.997],[88.737,23.919],[88.6695,23.868],[88.587,23.873],[88.59,23.7995],[88.5595,23.712],[88.5905,23.639],[88.637,23.605],[88.6515,23.556],[88.753,23.4795],[88.7575,23.3845],[88.703,23.307],[88.734,23.2435],[88.8095,23.255],[88.85,23.2305],[88.912,23.234],[88.9415,23.2065],[88.916,23.13],[88.8685,23.101],[88.884,23.0405],[88.855,22.9585],[88.8905,22.9275],[88.9105,22.88],[88.95,22.876],[88.963,22.819],[88.9115,22.7575],[88.96,22.685],[88.931,22.652],[88.9425,22.558],[88.9595,22.552],[89.0005,22.4315],[88.985,22.3255],[88.996,22.2855],[89.033,22.2585],[89.0705,22.197],[89.07,22.1505],[89.04,22.1025],[89.0825,22.003],[89.073,21.929],[89.0275,21.9195],[89.0425,21.867],[89.0285,21.791],[89.14,21.6085],[89.14,21.4265]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/f9/Flag_of_Bangladesh.svg","name:en":"Bangladesh","wikidata":"Q902","ISO3166-1:alpha2":"BD","ISO3166-1:alpha3":"BGD","ISO3166-1:numeric":"050"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[137.2235,8.389],[137.2285,8.343],[137.2615,8.271],[137.343,8.156],[137.415,8.11],[137.466,8.098],[137.5495,8.106],[137.611,8.132],[137.691,8.218],[137.715,8.295],[137.696,8.402],[137.7425,8.451],[137.7735,8.52],[137.7725,8.614],[137.7325,8.692],[137.686,8.733],[137.5735,8.765],[137.492,8.747],[137.427,8.703],[137.382,8.64],[137.365,8.579],[137.3055,8.549],[137.2495,8.486],[137.2235,8.389]]],[[[137.852,9.453],[137.8695,9.367],[137.9295,9.287],[138.037,9.243],[138.151,9.262],[138.232,9.329],[138.322,9.383],[138.368,9.44],[138.3915,9.5],[138.4005,9.576],[138.3575,9.701],[138.2765,9.797],[138.2,9.832],[138.0825,9.825],[138.018,9.788],[137.977,9.743],[137.873,9.567],[137.852,9.453]]],[[[139.3705,10.042],[139.3825,9.974],[139.4235,9.897],[139.435,9.783],[139.4955,9.684],[139.547,9.624],[139.6535,9.581],[139.732,9.588],[139.801,9.623],[139.8715,9.715],[139.976,9.742],[140.05,9.79],[140.089,9.847],[140.109,9.905],[140.111,9.981],[140.065,10.077],[140.026,10.112],[139.9555,10.143],[139.8935,10.214],[139.8115,10.268],[139.7285,10.29],[139.6725,10.288],[139.5005,10.229],[139.4265,10.179],[139.399,10.143],[139.3705,10.042]]],[[[140.1345,8.158],[140.1575,8.062],[140.23,7.985],[140.3615,7.921],[140.439,7.914],[140.4905,7.926],[140.5525,7.963],[140.6015,8.028],[140.6205,8.106],[140.616,8.156],[140.5835,8.229],[140.5505,8.27],[140.485,8.32],[140.412,8.347],[140.3205,8.356],[140.256,8.339],[140.205,8.307],[140.1475,8.226],[140.1345,8.158]]],[[[140.3055,9.759],[140.3235,9.675],[140.3605,9.62],[140.443,9.567],[140.507,9.556],[140.6035,9.575],[140.6525,9.606],[140.707,9.669],[140.7335,9.763],[140.708,9.866],[140.6495,9.932],[140.5625,9.969],[140.4735,9.965],[140.3955,9.929],[140.352,9.887],[140.3185,9.829],[140.3055,9.759]]],[[[142.8285,6.698],[142.836,6.644],[142.875,6.571],[142.9205,6.531],[143.035,6.487],[143.125,6.488],[143.1895,6.515],[143.229,6.548],[143.27,6.616],[143.276,6.73],[143.254,6.784],[143.197,6.846],[143.0885,6.892],[143.01,6.899],[142.9515,6.884],[142.87,6.821],[142.8415,6.77],[142.8285,6.698]]],[[[143.6185,7.355],[143.6275,7.293],[143.683,7.196],[143.768,7.137],[143.862,7.125],[143.9805,7.156],[144.061,7.21],[144.103,7.279],[144.117,7.351],[144.1135,7.405],[144.09,7.476],[144.0165,7.553],[143.932,7.581],[143.816,7.575],[143.729,7.546],[143.6595,7.481],[143.626,7.413],[143.6185,7.355]]],[[[144.2335,7.247],[144.256,7.156],[144.316,7.083],[144.368,7.054],[144.466,7.039],[144.5295,7.05],[144.6105,7.102],[144.648,7.157],[144.666,7.231],[144.6595,7.291],[144.6185,7.376],[144.5555,7.433],[144.497,7.456],[144.4115,7.458],[144.3075,7.406],[144.256,7.34],[144.2335,7.247]]],[[[144.302,8.608],[144.313,8.54],[144.3585,8.449],[144.4125,8.404],[144.494,8.378],[144.5525,8.38],[144.617,8.403],[144.7035,8.49],[144.728,8.565],[144.7275,8.617],[144.699,8.695],[144.6085,8.779],[144.5405,8.804],[144.4645,8.803],[144.3665,8.753],[144.3275,8.703],[144.302,8.608]]],[[[145.165,9.214],[145.183,9.13],[145.22,9.075],[145.3025,9.0215],[145.366,9.0105],[145.463,9.03],[145.512,9.061],[145.5665,9.124],[145.593,9.218],[145.5675,9.321],[145.509,9.3875],[145.44,9.4205],[145.351,9.4245],[145.255,9.3845],[145.211,9.342],[145.178,9.284],[145.165,9.214]]],[[[145.65,7.684],[145.683,7.575],[145.7505,7.512],[145.8555,7.485],[145.946,7.507],[145.9425,7.431],[145.988,7.302],[146.063,7.229],[146.1275,7.206],[146.205,7.207],[146.2625,7.228],[146.31,7.264],[146.3855,7.252],[146.4595,7.267],[146.5115,7.298],[146.5745,7.389],[146.585,7.467],[146.5755,7.519],[146.544,7.58],[146.4675,7.669],[146.3755,7.717],[146.309,7.721],[146.227,7.709],[146.1695,7.716],[146.087,7.698],[146.071,7.784],[146.029,7.846],[145.9355,7.9],[145.841,7.903],[145.7325,7.848],[145.681,7.793],[145.65,7.684]]],[[[146.526,8.087],[146.5295,8.047],[146.5625,7.97],[146.603,7.927],[146.6895,7.887],[146.7515,7.884],[146.8285,7.908],[146.875,7.943],[146.9265,8.032],[146.9265,8.138],[146.898,8.199],[146.8295,8.261],[146.7285,8.286],[146.665,8.275],[146.581,8.221],[146.5455,8.169],[146.526,8.087]]],[[[146.8225,7.378],[146.838,7.298],[146.8865,7.229],[146.9905,7.177],[147.09,7.184],[147.181,7.239],[147.228,7.308],[147.242,7.37],[147.2295,7.454],[147.177,7.531],[147.107,7.573],[146.994,7.583],[146.943,7.567],[146.888,7.529],[146.8325,7.438],[146.8225,7.378]]],[[[147.443,8.105],[147.4505,8.051],[147.479,7.99],[147.5485,7.928],[147.626,7.904],[147.688,7.907],[147.7455,7.928],[147.8205,7.999],[147.848,8.073],[147.8455,8.147],[147.822,8.208],[147.7615,8.272],[147.6825,8.304],[147.626,8.306],[147.5485,8.282],[147.5125,8.257],[147.4605,8.187],[147.443,8.105]]],[[[148.967,7.372],[148.9775,7.308],[149.011,7.245],[149.08,7.178],[149.128,7.156],[149.2095,7.148],[149.3105,7.184],[149.3845,7.268],[149.4035,7.336],[149.5015,7.348],[149.5955,7.418],[149.629,7.489],[149.6355,7.643],[149.616,7.727],[149.546,7.808],[149.4735,7.838],[149.3885,7.838],[149.3385,7.821],[149.2655,7.76],[149.2055,7.66],[149.1915,7.572],[149.098,7.559],[149.032,7.518],[148.9735,7.42],[148.967,7.372]]],[[[149.095,6.677],[149.1175,6.584],[149.1495,6.538],[149.198,6.5],[149.2625,6.477],[149.3665,6.485],[149.434,6.524],[149.473,6.57],[149.508,6.67],[149.5055,6.738],[149.4775,6.808],[149.4065,6.875],[149.342,6.898],[149.264,6.898],[149.1755,6.859],[149.118,6.79],[149.095,6.677]]],[[[149.479,8.584],[149.507,8.474],[149.5825,8.397],[149.665,8.37],[149.7265,8.372],[149.814,8.41],[149.868,8.472],[149.9045,8.586],[149.898,8.658],[149.8715,8.715],[149.8025,8.778],[149.738,8.801],[149.68,8.803],[149.6065,8.781],[149.521,8.707],[149.4855,8.636],[149.479,8.584]]],[[[149.8615,8.947],[149.889,8.843],[149.9715,8.766],[150.0615,8.744],[150.095,8.696],[150.178,8.628],[150.1835,8.52],[150.203,8.479],[150.274,8.41],[150.366,8.374],[150.4585,8.37],[150.5095,8.386],[150.568,8.427],[150.6025,8.474],[150.626,8.568],[150.594,8.677],[150.543,8.732],[150.525,8.836],[150.4835,8.897],[150.394,8.974],[150.372,9.058],[150.3135,9.139],[150.261,9.172],[150.1745,9.189],[150.0735,9.179],[150.022,9.158],[149.937,9.102],[149.883,9.035],[149.8615,8.947]]],[[[151.2385,7.387],[151.255,7.307],[151.337,7.165],[151.41,7.112],[151.4775,7.096],[151.548,7.042],[151.6135,7.024],[151.691,7.026],[151.7185,6.983],[151.774,6.937],[151.8255,6.848],[151.8775,6.81],[151.9765,6.788],[152.06,6.807],[152.108,6.838],[152.1665,6.908],[152.197,6.965],[152.207,7.049],[152.183,7.128],[152.228,7.224],[152.2315,7.284],[152.2015,7.408],[152.1975,7.476],[152.1685,7.592],[152.1215,7.699],[152.097,7.733],[152.0145,7.807],[151.951,7.844],[151.817,7.88],[151.7335,7.88],[151.635,7.854],[151.572,7.825],[151.502,7.743],[151.48,7.664],[151.4335,7.596],[151.3635,7.571],[151.275,7.501],[151.246,7.439],[151.2385,7.387]]],[[[151.4835,8.547],[151.4895,8.489],[151.564,8.322],[151.6275,8.263],[151.726,8.23],[151.8295,8.246],[151.9015,8.288],[151.9965,8.376],[152.0825,8.36],[152.17,8.381],[152.2135,8.41],[152.2815,8.412],[152.36,8.443],[152.4215,8.506],[152.476,8.54],[152.534,8.624],[152.542,8.738],[152.5055,8.842],[152.468,8.897],[152.384,8.959],[152.2805,8.981],[152.1885,8.974],[152.099,8.948],[152.048,8.916],[151.9985,8.852],[151.9795,8.792],[151.931,8.774],[151.865,8.798],[151.714,8.807],[151.6625,8.795],[151.5855,8.748],[151.5385,8.702],[151.499,8.635],[151.4835,8.547]]],[[[152.3685,6.993],[152.3855,6.915],[152.4195,6.859],[152.4815,6.809],[152.501,6.751],[152.547,6.687],[152.5865,6.658],[152.693,6.632],[152.773,6.646],[152.8655,6.714],[152.924,6.801],[152.9465,6.898],[152.9335,6.964],[152.879,7.044],[152.824,7.08],[152.748,7.101],[152.717,7.139],[152.654,7.182],[152.593,7.198],[152.5,7.186],[152.41,7.119],[152.3745,7.045],[152.3685,6.993]]],[[[152.911,5.925],[152.94,5.821],[153.0115,5.745],[153.055,5.717],[153.13,5.694],[153.175,5.694],[153.257,5.721],[153.301,5.756],[153.3365,5.808],[153.3555,5.9],[153.349,5.972],[153.3265,6.028],[153.27,6.089],[153.216,6.117],[153.141,6.13],[153.0275,6.108],[152.9735,6.071],[152.9285,6.007],[152.911,5.925]]],[[[153.2535,5.454],[153.2865,5.344],[153.364,5.237],[153.457,5.188],[153.4985,5.141],[153.598,5.089],[153.6455,5.083],[153.756,5.107],[153.8465,5.158],[153.8935,5.202],[153.928,5.262],[153.941,5.34],[153.9945,5.401],[154.0155,5.45],[154.021,5.538],[154.0075,5.588],[153.9515,5.664],[153.877,5.717],[153.8255,5.735],[153.7295,5.733],[153.645,5.787],[153.546,5.794],[153.4535,5.752],[153.4005,5.686],[153.3325,5.633],[153.278,5.557],[153.2535,5.454]]],[[[154.04,8.101],[154.057,8.017],[154.1095,7.944],[154.19,7.891],[154.2605,7.864],[154.336,7.863],[154.4085,7.89],[154.446,7.919],[154.5045,8.01],[154.5165,8.07],[154.5115,8.16],[154.4925,8.213],[154.412,8.308],[154.357,8.337],[154.2835,8.348],[154.177,8.321],[154.1115,8.275],[154.055,8.177],[154.04,8.101]]],[[[154.5785,1.104],[154.5805,1.0],[154.6025,0.933],[154.656,0.869],[154.7135,0.838],[154.821,0.831],[154.916,0.872],[154.97,0.927],[155.006,1.017],[155.01,1.089],[154.9805,1.187],[154.9185,1.256],[154.8015,1.302],[154.744,1.3],[154.6785,1.276],[154.64,1.247],[154.5935,1.178],[154.5785,1.104]]],[[[154.72,3.824],[154.741,3.735],[154.7835,3.678],[154.8235,3.649],[154.9065,3.624],[155.0315,3.639],[155.0855,3.668],[155.145,3.731],[155.173,3.806],[155.175,3.858],[155.134,3.973],[155.069,4.036],[154.966,4.071],[154.848,4.053],[154.79,4.015],[154.75,3.964],[154.728,3.904],[154.72,3.824]]],[[[154.945,7.555],[154.95,7.509],[154.9795,7.442],[155.0335,7.388],[155.0825,7.301],[155.1825,7.239],[155.348,7.207],[155.462,7.23],[155.5135,7.261],[155.5595,7.314],[155.5965,7.382],[155.616,7.474],[155.607,7.532],[155.6115,7.628],[155.5705,7.719],[155.504,7.785],[155.405,7.819],[155.2845,7.801],[155.1815,7.826],[155.091,7.816],[155.0345,7.786],[154.994,7.747],[154.9525,7.655],[154.945,7.555]]],[[[156.949,5.789],[156.9525,5.751],[156.9875,5.67],[157.0215,5.633],[157.122,5.587],[157.221,5.59],[157.2795,5.575],[157.387,5.586],[157.479,5.637],[157.521,5.689],[157.5465,5.773],[157.542,5.837],[157.5125,5.914],[157.4405,5.998],[157.3275,6.051],[157.233,6.046],[157.1425,5.991],[157.08,5.978],[156.999,5.922],[156.9585,5.851],[156.949,5.789]]],[[[157.5665,7.084],[157.5885,6.992],[157.622,6.943],[157.721,6.866],[157.717,6.792],[157.7315,6.688],[157.7635,6.612],[157.844,6.544],[157.9115,6.525],[157.9785,6.528],[158.1125,6.587],[158.273,6.564],[158.3235,6.571],[158.411,6.608],[158.5155,6.715],[158.548,6.765],[158.5675,6.841],[158.564,6.897],[158.4935,7.089],[158.444,7.157],[158.3375,7.238],[158.2765,7.254],[158.183,7.242],[158.1345,7.216],[158.082,7.159],[158.024,7.115],[157.9725,7.202],[157.9075,7.25],[157.7925,7.284],[157.728,7.281],[157.641,7.24],[157.6,7.195],[157.5665,7.084]]],[[[159.547,6.665],[159.563,6.585],[159.61,6.518],[159.714,6.466],[159.7745,6.464],[159.8585,6.491],[159.9325,6.565],[159.9605,6.633],[159.9695,6.693],[159.9515,6.777],[159.8805,6.86],[159.803,6.891],[159.732,6.894],[159.6325,6.856],[159.582,6.802],[159.5605,6.757],[159.547,6.665]]],[[[160.489,6.222],[160.5075,6.136],[160.552,6.066],[160.6405,6.008],[160.7425,6.001],[160.843,6.05],[160.9025,6.142],[160.917,6.214],[160.9,6.3],[160.8685,6.35],[160.79,6.409],[160.6995,6.431],[160.6275,6.42],[160.5535,6.375],[160.501,6.29],[160.489,6.222]]],[[[162.698,5.302],[162.7045,5.248],[162.766,5.138],[162.826,5.095],[162.898,5.071],[162.9975,5.059],[163.1035,5.086],[163.191,5.168],[163.219,5.224],[163.236,5.3],[163.2255,5.392],[163.1985,5.46],[163.159,5.512],[163.073,5.561],[162.9705,5.568],[162.9015,5.552],[162.832,5.514],[162.7545,5.438],[162.709,5.364],[162.698,5.302]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/e/e4/Flag_of_the_Federated_States_of_Micronesia.svg","name:en":"Federated States of Micronesia","wikidata":"Q702","ISO3166-1:alpha2":"FM","ISO3166-1:alpha3":"FSM","ISO3166-1:numeric":"583"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-109.6795,-27.2],[-109.649,-27.302],[-109.576,-27.37],[-109.504,-27.397],[-109.376,-27.392],[-109.1165,-27.291],[-109.0305,-27.205],[-109.003,-27.103],[-109.0375,-26.997],[-109.0875,-26.943],[-109.1335,-26.915],[-109.2755,-26.87],[-109.37,-26.855],[-109.4625,-26.869],[-109.5175,-26.895],[-109.597,-26.975],[-109.654,-27.086],[-109.6795,-27.2]]],[[[-105.5885,-26.4715],[-105.5635,-26.564],[-105.5085,-26.6265],[-105.436,-26.6645],[-105.367,-26.6755],[-105.258,-26.653],[-105.2025,-26.6165],[-105.1585,-26.56],[-105.141,-26.5125],[-105.14,-26.436],[-105.177,-26.3555],[-105.221,-26.314],[-105.282,-26.283],[-105.347,-26.2695],[-105.4575,-26.287],[-105.505,-26.3135],[-105.565,-26.381],[-105.5885,-26.4715]]],[[[-81.0785,-33.751],[-81.0395,-33.879],[-80.9695,-33.953],[-80.9245,-33.982],[-80.802,-34.015],[-80.71,-34.008],[-80.645,-33.984],[-80.563,-33.92],[-80.528,-33.859],[-80.515,-33.795],[-80.521,-33.719],[-80.555,-33.639],[-80.6285,-33.564],[-80.7585,-33.523],[-80.8445,-33.526],[-80.951,-33.564],[-80.9925,-33.593],[-81.0595,-33.675],[-81.0785,-33.751]]],[[[-80.3375,-26.296],[-80.3105,-26.392],[-80.253,-26.453],[-80.1855,-26.491],[-80.1245,-26.509],[-80.0395,-26.509],[-79.932,-26.548],[-79.851,-26.546],[-79.7355,-26.505],[-79.6775,-26.449],[-79.6515,-26.397],[-79.6455,-26.315],[-79.6605,-26.267],[-79.705,-26.205],[-79.781,-26.157],[-79.84,-26.14],[-79.939,-26.14],[-79.9845,-26.111],[-80.068,-26.088],[-80.177,-26.099],[-80.254,-26.137],[-80.297,-26.179],[-80.33,-26.244],[-80.3375,-26.296]]],[[[-79.1955,-33.698],[-79.18,-33.77],[-79.097,-33.867],[-79.025,-33.901],[-78.92,-33.915],[-78.7905,-33.882],[-78.681,-33.863],[-78.5855,-33.802],[-78.5325,-33.703],[-78.532,-33.643],[-78.5595,-33.572],[-78.642,-33.483],[-78.74,-33.435],[-78.824,-33.41],[-78.9235,-33.41],[-79.0475,-33.464],[-79.141,-33.556],[-79.171,-33.602],[-79.1955,-33.698]]],[[[-70.591,-18.35],[-70.583,-18.405],[-70.5445,-18.4755],[-70.561,-18.639],[-70.5535,-18.705],[-70.569,-18.825],[-70.5265,-18.99],[-70.5275,-19.038],[-70.49,-19.17],[-70.4955,-19.33],[-70.483,-19.38],[-70.4275,-19.507],[-70.451,-19.583],[-70.448,-19.641],[-70.425,-19.7],[-70.375,-19.757],[-70.376,-19.849],[-70.344,-20.017],[-70.3485,-20.079],[-70.375,-20.189],[-70.3685,-20.262],[-70.3955,-20.342],[-70.3865,-20.437],[-70.4135,-20.528],[-70.4065,-20.67],[-70.4235,-20.82],[-70.413,-20.874],[-70.3725,-20.941],[-70.392,-21.029],[-70.3765,-21.099],[-70.301,-21.274],[-70.311,-21.374],[-70.2915,-21.442],[-70.2995,-21.496],[-70.3365,-21.543],[-70.362,-21.618],[-70.3715,-21.794],[-70.398,-21.851],[-70.4,-21.943],[-70.4265,-22.049],[-70.462,-22.265],[-70.499,-22.555],[-70.5055,-22.675],[-70.5205,-22.711],[-70.531,-22.826],[-70.605,-22.843],[-70.701,-22.911],[-70.7735,-22.995],[-70.7935,-23.053],[-70.796,-23.139],[-70.819,-23.221],[-70.8285,-23.393],[-70.846,-23.495],[-70.8415,-23.557],[-70.814,-23.622],[-70.76,-23.677],[-70.6995,-23.709],[-70.7435,-23.847],[-70.731,-24.069],[-70.74,-24.179],[-70.7765,-24.367],[-70.7725,-24.411],[-70.796,-24.511],[-70.789,-24.613],[-70.8005,-24.725],[-70.7665,-24.836],[-70.739,-24.972],[-70.714,-25.023],[-70.728,-25.091],[-70.7115,-25.181],[-70.6695,-25.246],[-70.725,-25.327],[-70.803,-25.383],[-70.8645,-25.486],[-70.8725,-25.558],[-70.9145,-25.655],[-70.954,-25.718],[-70.963,-25.842],[-70.9435,-25.91],[-70.8775,-26.02],[-70.9105,-26.085],[-70.9195,-26.171],[-70.893,-26.249],[-70.914,-26.341],[-70.9305,-26.565],[-70.9805,-26.686],[-71.0395,-26.8],[-71.0575,-26.866],[-71.049,-26.937],[-71.1055,-26.978],[-71.179,-27.088],[-71.1935,-27.128],[-71.2025,-27.268],[-71.1865,-27.326],[-71.188,-27.404],[-71.1505,-27.486],[-71.2215,-27.542],[-71.2485,-27.585],[-71.276,-27.677],[-71.3325,-27.812],[-71.3715,-27.946],[-71.375,-28.0],[-71.4035,-28.063],[-71.411,-28.127],[-71.3945,-28.199],[-71.425,-28.313],[-71.4855,-28.395],[-71.4975,-28.447],[-71.5405,-28.536],[-71.541,-28.616],[-71.572,-28.659],[-71.6485,-28.711],[-71.7265,-28.824],[-71.7455,-28.881],[-71.7865,-28.927],[-71.816,-28.992],[-71.8095,-29.1],[-71.758,-29.179],[-71.7775,-29.257],[-71.7645,-29.353],[-71.7215,-29.4165],[-71.757,-29.463],[-71.778,-29.523],[-71.7635,-29.639],[-71.7285,-29.705],[-71.692,-29.741],[-71.5715,-29.789],[-71.555,-29.84],[-71.6055,-29.886],[-71.6385,-29.947],[-71.644,-30.039],[-71.7535,-30.071],[-71.8335,-30.144],[-71.8745,-30.216],[-71.91,-30.324],[-71.929,-30.5],[-71.9485,-30.586],[-71.9465,-30.734],[-71.9125,-30.954],[-71.895,-31.006],[-71.9015,-31.156],[-71.8725,-31.272],[-71.8445,-31.444],[-71.809,-31.536],[-71.814,-31.62],[-71.78,-31.724],[-71.794,-31.842],[-71.7625,-31.953],[-71.7615,-32.057],[-71.7755,-32.217],[-71.752,-32.297],[-71.7085,-32.364],[-71.688,-32.424],[-71.7135,-32.53],[-71.7035,-32.597],[-71.77,-32.722],[-71.785,-32.804],[-71.779,-32.854],[-71.922,-32.966],[-71.979,-33.061],[-71.9785,-33.141],[-71.946,-33.213],[-71.9245,-33.305],[-71.949,-33.415],[-71.9405,-33.479],[-71.868,-33.577],[-71.9815,-33.649],[-72.015,-33.695],[-72.068,-33.834],[-72.212,-34.025],[-72.2435,-34.088],[-72.2495,-34.146],[-72.224,-34.277],[-72.2795,-34.366],[-72.2905,-34.416],[-72.2885,-34.61],[-72.304,-34.657],[-72.3695,-34.747],[-72.4205,-34.864],[-72.432,-34.997],[-72.514,-35.047],[-72.5915,-35.117],[-72.644,-35.22],[-72.7305,-35.328],[-72.743,-35.38],[-72.7945,-35.419],[-72.8425,-35.48],[-72.8735,-35.554],[-72.878,-35.636],[-72.8395,-35.72],[-72.903,-35.793],[-72.976,-35.849],[-73.0135,-35.901],[-73.0535,-36.039],[-73.07,-36.187],[-73.0635,-36.245],[-73.1275,-36.368],[-73.1895,-36.423],[-73.3075,-36.484],[-73.3635,-36.565],[-73.3815,-36.617],[-73.4525,-36.71],[-73.4695,-36.768],[-73.545,-36.76],[-73.627,-36.772],[-73.7225,-36.822],[-73.759,-36.861],[-73.7895,-36.931],[-73.805,-37.038],[-73.86,-37.095],[-73.902,-37.189],[-73.9325,-37.305],[-73.9355,-37.373],[-73.914,-37.447],[-73.8835,-37.488],[-73.919,-37.553],[-73.9325,-37.625],[-73.895,-37.783],[-73.779,-37.942],[-73.709,-38.086],[-73.705,-38.127],[-73.748,-38.182],[-73.8335,-38.137],[-73.906,-38.119],[-74.015,-38.121],[-74.1265,-38.166],[-74.1785,-38.215],[-74.215,-38.311],[-74.1985,-38.419],[-74.135,-38.54],[-74.046,-38.613],[-73.981,-38.637],[-73.859,-38.643],[-73.7585,-38.611],[-73.715,-38.754],[-73.6575,-38.854],[-73.5695,-39.04],[-73.4875,-39.272],[-73.489,-39.328],[-73.543,-39.514],[-73.6175,-39.58],[-73.651,-39.631],[-73.671,-39.725],[-73.691,-39.747],[-73.842,-39.811],[-73.901,-39.863],[-73.9615,-39.963],[-73.9655,-40.033],[-73.944,-40.092],[-73.9755,-40.145],[-74.0125,-40.281],[-74.0395,-40.461],[-74.0245,-40.527],[-74.087,-40.612],[-74.15,-40.846],[-74.1905,-40.907],[-74.2095,-41.001],[-74.1745,-41.201],[-74.1255,-41.283],[-74.109,-41.3395],[-74.1205,-41.413],[-74.317,-41.7265],[-74.336,-41.7685],[-74.477,-42.2375],[-74.701,-43.177],[-75.033,-43.413],[-75.091,-43.4725],[-75.1325,-43.561],[-75.145,-43.632],[-75.1275,-43.884],[-75.0865,-44.4645],[-75.3465,-44.625],[-75.4495,-44.7185],[-75.4845,-44.796],[-75.4845,-44.84],[-75.437,-44.9695],[-75.245,-45.606],[-75.855,-46.5295],[-75.8955,-46.631],[-75.919,-46.742],[-75.913,-46.86],[-75.825,-46.9925],[-75.7145,-47.0845],[-75.7085,-47.6705],[-75.9665,-48.017],[-75.9985,-48.1005],[-76.0335,-48.7885],[-76.047,-49.126],[-75.976,-49.496],[-75.82,-50.1125],[-75.8375,-50.3635],[-75.8345,-50.6685],[-75.7925,-50.87],[-75.772,-50.9175],[-75.587,-51.2015],[-75.6845,-51.5535],[-75.677,-51.642],[-75.471,-52.0925],[-75.432,-52.4125],[-75.421,-52.45],[-75.3495,-52.5305],[-75.269,-52.5705],[-75.0465,-52.6475],[-75.0745,-52.731],[-75.0715,-52.84],[-75.0335,-52.9305],[-74.599,-53.418],[-73.7695,-54.16],[-73.4035,-54.704],[-73.3575,-54.751],[-73.255,-54.8035],[-73.152,-54.8255],[-72.209,-54.9145],[-71.183,-55.3525],[-71.0295,-55.389],[-70.671,-55.4195],[-70.315,-55.5435],[-69.9115,-55.6395],[-69.7305,-55.8375],[-69.639,-55.9],[-69.494,-55.9395],[-69.411,-55.9445],[-68.2195,-55.9275],[-68.0615,-56.0235],[-67.9775,-56.049],[-67.7405,-56.1],[-67.3805,-56.1685],[-67.176,-56.1725],[-66.9165,-56.134],[-66.7555,-56.0815],[-66.5505,-55.9645],[-66.4835,-55.91],[-66.4505,-55.8365],[-66.429,-55.6235],[-66.1205,-55.315],[-66.0775,-55.2395],[-66.0785,-55.1835],[-66.4165,-55.1215],[-66.671,-55.119],[-66.7915,-55.0135],[-66.8525,-54.988],[-66.958,-54.9715],[-67.032,-54.9765],[-67.1325,-54.9275],[-67.273,-54.905],[-67.477,-54.9235],[-67.5725,-54.8945],[-67.705,-54.9055],[-67.9505,-54.8735],[-68.253,-54.8855],[-68.4115,-54.8825],[-68.6095,-54.9135],[-68.5955,-54.8085],[-68.6075,-54.5555],[-68.608,-54.1885],[-68.6065,-53.48],[-68.6065,-52.9755],[-68.607,-52.659],[-68.4325,-52.3975],[-68.4185,-52.3325],[-68.5735,-52.325],[-68.586,-52.3065],[-68.7095,-52.2855],[-68.838,-52.278],[-68.987,-52.2035],[-69.19,-52.1505],[-69.4865,-52.1515],[-69.9955,-52.0005],[-70.667,-52.0],[-71.0645,-52.0],[-71.9225,-52.0],[-72.0265,-51.9555],[-71.9385,-51.9085],[-71.949,-51.8735],[-72.121,-51.7445],[-72.2,-51.713],[-72.299,-51.6975],[-72.2875,-51.6325],[-72.3385,-51.584],[-72.441,-51.584],[-72.391,-51.515],[-72.3245,-51.476],[-72.337,-51.4215],[-72.308,-51.3775],[-72.3575,-51.318],[-72.274,-51.286],[-72.2585,-51.244],[-72.347,-51.2025],[-72.4015,-51.1355],[-72.374,-51.023],[-72.2655,-51.035],[-72.267,-50.965],[-72.246,-50.9],[-72.2605,-50.836],[-72.349,-50.7545],[-72.286,-50.66],[-72.4105,-50.6375],[-72.4955,-50.602],[-72.5835,-50.6585],[-72.697,-50.645],[-72.7325,-50.6155],[-72.893,-50.67],[-72.957,-50.741],[-73.094,-50.773],[-73.1965,-50.7415],[-73.153,-50.658],[-73.1935,-50.6155],[-73.2875,-50.59],[-73.3645,-50.518],[-73.3575,-50.429],[-73.3975,-50.3705],[-73.3615,-50.3015],[-73.4525,-50.2415],[-73.4615,-50.185],[-73.522,-50.1535],[-73.436,-50.011],[-73.477,-49.97],[-73.5585,-49.947],[-73.5105,-49.8795],[-73.4905,-49.8165],[-73.4385,-49.758],[-73.504,-49.7095],[-73.506,-49.666],[-73.4605,-49.6245],[-73.5355,-49.564],[-73.5445,-49.5045],[-73.4635,-49.4825],[-73.489,-49.4345],[-73.4275,-49.3935],[-73.432,-49.314],[-73.4965,-49.2685],[-73.473,-49.2045],[-73.149,-49.284],[-73.1395,-49.178],[-73.0785,-49.1405],[-73.042,-49.0775],[-73.001,-49.0595],[-72.923,-48.9345],[-72.85,-48.965],[-72.7945,-48.9645],[-72.747,-48.908],[-72.6965,-48.8965],[-72.534,-48.8005],[-72.542,-48.631],[-72.586,-48.4875],[-72.5025,-48.5175],[-72.44,-48.514],[-72.3735,-48.4495],[-72.4055,-48.417],[-72.277,-48.3635],[-72.2345,-48.316],[-72.3155,-48.2355],[-72.2985,-48.16],[-72.361,-48.0745],[-72.414,-48.079],[-72.427,-47.966],[-72.503,-47.9585],[-72.523,-47.902],[-72.475,-47.865],[-72.467,-47.8135],[-72.504,-47.7635],[-72.4345,-47.7345],[-72.4205,-47.661],[-72.3455,-47.6325],[-72.291,-47.512],[-72.31,-47.479],[-72.266,-47.4155],[-72.1985,-47.416],[-72.127,-47.328],[-72.046,-47.3405],[-71.997,-47.295],[-72.0155,-47.25],[-71.9735,-47.2095],[-71.879,-47.2295],[-71.8905,-47.1865],[-71.8505,-47.162],[-71.881,-47.107],[-71.9495,-47.087],[-71.88,-47.011],[-71.9525,-46.959],[-71.9705,-46.904],[-71.931,-46.8685],[-71.9485,-46.81],[-71.839,-46.799],[-71.6485,-46.689],[-71.6685,-46.613],[-71.669,-46.528],[-71.757,-46.346],[-71.724,-46.2845],[-71.751,-46.229],[-71.7995,-46.1915],[-71.8645,-46.1895],[-71.889,-46.1275],[-71.762,-46.112],[-71.676,-46.049],[-71.6465,-45.983],[-71.6145,-45.9625],[-71.656,-45.886],[-71.769,-45.8465],[-71.741,-45.807],[-71.798,-45.711],[-71.7955,-45.6645],[-71.727,-45.577],[-71.746,-45.5415],[-71.6815,-45.514],[-71.5605,-45.5235],[-71.477,-45.497],[-71.532,-45.397],[-71.3845,-45.3495],[-71.333,-45.319],[-71.331,-45.2505],[-71.4185,-45.1755],[-71.4705,-45.1485],[-71.511,-45.061],[-71.5565,-45.0325],[-71.5605,-44.9785],[-71.6845,-44.9695],[-71.812,-44.921],[-71.933,-44.9425],[-71.979,-44.9035],[-72.05,-44.882],[-72.069,-44.851],[-72.0065,-44.7835],[-71.9135,-44.777],[-71.8565,-44.802],[-71.7835,-44.751],[-71.6755,-44.7865],[-71.494,-44.737],[-71.396,-44.79],[-71.2765,-44.8095],[-71.207,-44.75],[-71.198,-44.689],[-71.2355,-44.641],[-71.1865,-44.593],[-71.1215,-44.597],[-71.092,-44.533],[-71.137,-44.4695],[-71.24,-44.4175],[-71.332,-44.429],[-71.3645,-44.3895],[-71.4755,-44.391],[-71.5725,-44.409],[-71.6545,-44.4095],[-71.695,-44.384],[-71.8075,-44.4205],[-71.8405,-44.35],[-71.791,-44.3145],[-71.811,-44.254],[-71.8075,-44.1875],[-71.8515,-44.1295],[-71.752,-44.094],[-71.682,-43.9745],[-71.649,-43.9455],[-71.6655,-43.902],[-71.759,-43.8445],[-71.7515,-43.7855],[-71.703,-43.7395],[-71.6105,-43.701],[-71.581,-43.6515],[-71.608,-43.6285],[-71.69,-43.6265],[-71.716,-43.5775],[-71.7765,-43.5385],[-71.871,-43.553],[-71.8485,-43.4835],[-71.889,-43.45],[-71.8965,-43.32],[-71.8425,-43.335],[-71.7915,-43.298],[-71.7315,-43.3075],[-71.757,-43.228],[-71.73,-43.201],[-71.7605,-43.1645],[-71.8875,-43.1265],[-71.924,-43.101],[-71.9445,-43.046],[-72.0365,-43.0135],[-72.076,-42.949],[-72.143,-42.898],[-72.165,-42.799],[-72.129,-42.7325],[-72.1795,-42.698],[-72.096,-42.642],[-72.1035,-42.6005],[-72.028,-42.515],[-72.04,-42.4595],[-72.023,-42.4175],[-72.133,-42.384],[-72.137,-42.328],[-72.168,-42.2605],[-72.1585,-42.1935],[-72.173,-42.1405],[-72.0925,-42.154],[-72.049,-42.116],[-71.9705,-42.145],[-71.927,-42.19],[-71.73,-42.122],[-71.7935,-41.9625],[-71.775,-41.9075],[-71.7895,-41.831],[-71.7645,-41.817],[-71.784,-41.735],[-71.886,-41.605],[-71.837,-41.566],[-71.851,-41.515],[-71.8345,-41.4335],[-71.911,-41.358],[-71.8755,-41.2965],[-71.8575,-41.212],[-71.888,-41.161],[-71.841,-41.15],[-71.8515,-41.0985],[-71.8195,-41.0605],[-71.9085,-40.973],[-71.855,-40.9445],[-71.8685,-40.892],[-71.9275,-40.8335],[-71.923,-40.783],[-71.9595,-40.7585],[-71.9495,-40.719],[-71.866,-40.658],[-71.8375,-40.61],[-71.87,-40.5725],[-71.8415,-40.452],[-71.8035,-40.406],[-71.7085,-40.4225],[-71.6715,-40.311],[-71.736,-40.3055],[-71.828,-40.208],[-71.7995,-40.136],[-71.814,-40.0805],[-71.7,-40.117],[-71.6665,-40.0745],[-71.6895,-40.0365],[-71.5925,-39.901],[-71.6465,-39.853],[-71.691,-39.8425],[-71.6805,-39.769],[-71.7125,-39.7245],[-71.684,-39.692],[-71.6985,-39.588],[-71.618,-39.5985],[-71.61,-39.628],[-71.518,-39.6255],[-71.461,-39.583],[-71.5345,-39.5305],[-71.473,-39.4935],[-71.4465,-39.3965],[-71.4015,-39.352],[-71.3885,-39.2655],[-71.4075,-39.096],[-71.441,-38.9775],[-71.4255,-38.9215],[-71.2675,-38.8515],[-71.2605,-38.815],[-71.2065,-38.8175],[-71.1075,-38.772],[-71.0275,-38.7585],[-70.925,-38.763],[-70.892,-38.721],[-70.904,-38.667],[-70.829,-38.5995],[-70.8405,-38.5415],[-70.911,-38.5035],[-70.976,-38.442],[-70.985,-38.361],[-71.0195,-38.218],[-70.997,-38.17],[-71.0435,-38.124],[-70.9865,-38.1045],[-71.053,-38.0455],[-71.051,-37.9925],[-71.1145,-37.953],[-71.108,-37.8925],[-71.1705,-37.78],[-71.156,-37.7575],[-71.212,-37.689],[-71.194,-37.6305],[-71.1265,-37.5825],[-71.1195,-37.4865],[-71.15,-37.4025],[-71.199,-37.3795],[-71.2155,-37.2725],[-71.114,-37.205],[-71.136,-37.1645],[-71.0925,-37.1145],[-71.1705,-36.9965],[-71.1065,-36.9715],[-71.1445,-36.929],[-71.122,-36.864],[-71.1325,-36.8155],[-71.1115,-36.7285],[-71.013,-36.6975],[-71.0405,-36.6495],[-71.0555,-36.5665],[-71.0385,-36.4775],[-70.956,-36.5045],[-70.894,-36.4715],[-70.89,-36.4035],[-70.7875,-36.429],[-70.713,-36.427],[-70.6795,-36.3895],[-70.7125,-36.3395],[-70.6845,-36.3045],[-70.705,-36.2725],[-70.6395,-36.243],[-70.5835,-36.147],[-70.5345,-36.1415],[-70.4905,-36.179],[-70.421,-36.156],[-70.4105,-36.056],[-70.3715,-36.0435],[-70.3905,-35.951],[-70.425,-35.9135],[-70.421,-35.874],[-70.3155,-35.819],[-70.3795,-35.787],[-70.3695,-35.7375],[-70.418,-35.63],[-70.3865,-35.604],[-70.4135,-35.529],[-70.382,-35.5125],[-70.4465,-35.464],[-70.4525,-35.3925],[-70.4185,-35.3545],[-70.439,-35.316],[-70.4965,-35.323],[-70.583,-35.28],[-70.5425,-35.2025],[-70.4705,-35.203],[-70.388,-35.171],[-70.362,-35.1365],[-70.3755,-35.046],[-70.3345,-34.9915],[-70.3195,-34.9315],[-70.257,-34.8205],[-70.309,-34.768],[-70.2145,-34.681],[-70.223,-34.64],[-70.1035,-34.482],[-70.015,-34.413],[-70.0145,-34.3385],[-70.031,-34.288],[-69.967,-34.251],[-69.9315,-34.2815],[-69.795,-34.243],[-69.7915,-34.2005],[-69.869,-34.1405],[-69.8395,-34.007],[-69.892,-33.925],[-69.8555,-33.8925],[-69.899,-33.8475],[-69.904,-33.7715],[-69.865,-33.715],[-69.8845,-33.6845],[-69.8655,-33.583],[-69.872,-33.5395],[-69.835,-33.515],[-69.82,-33.4325],[-69.7705,-33.361],[-69.7995,-33.2885],[-69.9175,-33.267],[-70.003,-33.323],[-70.039,-33.27],[-70.0275,-33.2325],[-70.0645,-33.205],[-70.066,-33.104],[-70.081,-33.029],[-70.0215,-33.008],[-70.0015,-32.946],[-70.001,-32.8845],[-70.0445,-32.87],[-70.0545,-32.832],[-70.108,-32.8005],[-70.1705,-32.6255],[-70.123,-32.5355],[-70.149,-32.466],[-70.2365,-32.429],[-70.2225,-32.325],[-70.319,-32.2735],[-70.301,-32.207],[-70.305,-32.1425],[-70.3415,-32.1095],[-70.322,-32.0505],[-70.2485,-32.024],[-70.2085,-31.978],[-70.272,-31.8905],[-70.392,-31.8795],[-70.455,-31.8485],[-70.4675,-31.7005],[-70.513,-31.6895],[-70.567,-31.583],[-70.553,-31.51],[-70.5695,-31.4735],[-70.53,-31.3755],[-70.545,-31.3065],[-70.517,-31.234],[-70.5235,-31.19],[-70.4985,-31.125],[-70.432,-31.102],[-70.408,-31.1665],[-70.319,-31.043],[-70.2985,-30.9625],[-70.3215,-30.9245],[-70.278,-30.81],[-70.247,-30.635],[-70.1795,-30.5115],[-70.195,-30.4915],[-70.117,-30.431],[-70.144,-30.372],[-70.1035,-30.3565],[-70.0705,-30.391],[-69.955,-30.3855],[-69.9035,-30.3435],[-69.8855,-30.2185],[-69.8355,-30.219],[-69.8095,-30.1455],[-69.905,-30.1065],[-69.961,-30.0685],[-69.912,-30.028],[-69.926,-30.0005],[-69.8985,-29.958],[-69.896,-29.8795],[-69.8775,-29.8615],[-69.9135,-29.807],[-69.8795,-29.7335],[-69.942,-29.646],[-69.9315,-29.5845],[-69.9555,-29.535],[-69.948,-29.491],[-69.9815,-29.452],[-69.967,-29.4145],[-70.0215,-29.3835],[-70.0205,-29.3305],[-69.9855,-29.277],[-69.946,-29.263],[-69.9335,-29.189],[-69.883,-29.144],[-69.787,-29.13],[-69.787,-29.0285],[-69.7535,-28.957],[-69.7665,-28.924],[-69.693,-28.771],[-69.7295,-28.736],[-69.7265,-28.6705],[-69.6955,-28.599],[-69.6645,-28.5815],[-69.6645,-28.482],[-69.642,-28.442],[-69.65,-28.396],[-69.5325,-28.3345],[-69.457,-28.1835],[-69.384,-28.213],[-69.3795,-28.1815],[-69.3275,-28.1555],[-69.299,-28.0695],[-69.264,-28.0465],[-69.2675,-28.0105],[-69.2105,-27.9645],[-69.182,-27.972],[-69.134,-27.909],[-69.089,-27.7855],[-69.0935,-27.7175],[-69.0455,-27.699],[-69.053,-27.617],[-68.9765,-27.5605],[-68.943,-27.515],[-68.9635,-27.4685],[-68.9365,-27.4445],[-68.8685,-27.317],[-68.8245,-27.294],[-68.8685,-27.2605],[-68.841,-27.1545],[-68.8085,-27.1465],[-68.778,-27.099],[-68.6685,-27.104],[-68.6185,-27.17],[-68.573,-27.175],[-68.5565,-27.1145],[-68.4965,-27.135],[-68.4325,-27.0655],[-68.3475,-27.0335],[-68.2985,-27.039],[-68.315,-26.974],[-68.281,-26.9035],[-68.3655,-26.801],[-68.5875,-26.493],[-68.5575,-26.287],[-68.381,-26.178],[-68.479,-25.708],[-68.5415,-25.6385],[-68.525,-25.567],[-68.573,-25.4935],[-68.5865,-25.4275],[-68.5225,-25.3745],[-68.5435,-25.3025],[-68.511,-25.181],[-68.4595,-25.126],[-68.4095,-25.1455],[-68.3435,-25.1105],[-68.3795,-25.0415],[-68.407,-25.0285],[-68.432,-24.9055],[-68.4865,-24.8955],[-68.531,-24.8585],[-68.568,-24.7975],[-68.5445,-24.7245],[-68.5155,-24.703],[-68.475,-24.6215],[-68.431,-24.5775],[-68.387,-24.484],[-68.309,-24.492],[-68.2965,-24.455],[-68.2455,-24.396],[-67.961,-24.285],[-67.3225,-24.0345],[-67.112,-23.3805],[-66.9905,-23.0005],[-67.1805,-22.814],[-67.5675,-22.8985],[-67.7985,-22.881],[-67.8815,-22.834],[-67.8675,-22.745],[-67.89,-22.719],[-67.8675,-22.6795],[-67.874,-22.64],[-67.8385,-22.5885],[-67.8455,-22.531],[-67.893,-22.4995],[-67.903,-22.4035],[-67.9335,-22.391],[-67.9385,-22.2945],[-67.9215,-22.2755],[-67.933,-22.2015],[-67.957,-22.1515],[-67.939,-22.108],[-67.971,-22.0545],[-68.0715,-21.979],[-68.063,-21.8525],[-68.0655,-21.7675],[-68.1175,-21.6785],[-68.1805,-21.6045],[-68.179,-21.3025],[-68.295,-21.0975],[-68.4125,-20.9345],[-68.493,-20.946],[-68.553,-20.8765],[-68.541,-20.7965],[-68.5535,-20.7285],[-68.526,-20.6965],[-68.4505,-20.6475],[-68.535,-20.5695],[-68.5735,-20.575],[-68.6135,-20.5365],[-68.6665,-20.5165],[-68.7385,-20.457],[-68.7415,-20.3685],[-68.672,-20.355],[-68.6605,-20.3195],[-68.7135,-20.23],[-68.705,-20.1335],[-68.7735,-20.1295],[-68.7625,-20.0785],[-68.64,-20.049],[-68.5665,-20.05],[-68.5215,-19.9245],[-68.5425,-19.8895],[-68.5285,-19.8455],[-68.5995,-19.828],[-68.6205,-19.7895],[-68.69,-19.75],[-68.6825,-19.718],[-68.625,-19.702],[-68.5985,-19.641],[-68.557,-19.5965],[-68.441,-19.4405],[-68.405,-19.416],[-68.4515,-19.368],[-68.5425,-19.297],[-68.62,-19.2765],[-68.6285,-19.251],[-68.806,-19.0835],[-68.8895,-19.0435],[-68.9505,-18.9365],[-68.92,-18.885],[-68.9255,-18.843],[-68.977,-18.738],[-68.988,-18.674],[-69.0325,-18.629],[-69.0045,-18.476],[-69.0475,-18.4265],[-69.053,-18.347],[-69.075,-18.2925],[-69.064,-18.2325],[-69.146,-18.163],[-69.057,-18.0615],[-69.121,-18.005],[-69.281,-17.9695],[-69.3085,-17.896],[-69.3075,-17.8055],[-69.337,-17.7315],[-69.468,-17.605],[-69.4685,-17.4985],[-69.5525,-17.5755],[-69.6665,-17.6605],[-69.7965,-17.646],[-69.826,-17.706],[-69.801,-17.762],[-69.799,-17.8655],[-69.75,-17.9475],[-69.7545,-17.9895],[-69.816,-18.1165],[-69.8605,-18.17],[-69.966,-18.262],[-70.0505,-18.269],[-70.1495,-18.3195],[-70.302,-18.309],[-70.3765,-18.35],[-70.591,-18.35]]],[[[-69.105,-56.447],[-69.086,-56.569],[-68.999,-56.657],[-68.8775,-56.706],[-68.705,-56.725],[-68.563,-56.708],[-68.405,-56.641],[-68.3405,-56.563],[-68.338,-56.487],[-68.3995,-56.375],[-68.5295,-56.286],[-68.661,-56.253],[-68.8665,-56.26],[-68.967,-56.291],[-69.0825,-56.379],[-69.105,-56.447]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/7/78/Flag_of_Chile.svg","name:en":"Chile","wikidata":"Q298","ISO3166-1:alpha2":"CL","ISO3166-1:alpha3":"CHL","ISO3166-1:numeric":"152"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-53.181,-33.869],[-53.0905,-33.801],[-52.642,-33.4455],[-52.585,-33.3965],[-52.46,-33.2535],[-52.405,-33.1735],[-52.296,-32.975],[-52.2265,-32.796],[-52.197,-32.6685],[-52.138,-32.5105],[-52.061,-32.385],[-51.938,-32.335],[-51.8585,-32.252],[-51.82,-32.178],[-51.742,-32.1055],[-51.6605,-32.0535],[-51.3975,-31.915],[-51.188,-31.7765],[-51.1015,-31.707],[-50.77,-31.4015],[-50.6045,-31.2415],[-50.5085,-31.1045],[-50.376,-30.927],[-50.281,-30.7885],[-50.1735,-30.643],[-50.1075,-30.5275],[-50.0405,-30.367],[-49.962,-30.1525],[-49.8965,-30.007],[-49.8255,-29.8655],[-49.7405,-29.714],[-49.6305,-29.5575],[-49.464,-29.342],[-49.3245,-29.185],[-49.158,-29.0285],[-49.027,-28.933],[-48.9155,-28.863],[-48.777,-28.791],[-48.686,-28.76],[-48.6155,-28.6895],[-48.407,-28.4325],[-48.39,-28.3965],[-48.2115,-27.856],[-48.1585,-27.5845],[-48.138,-27.5],[-48.111,-27.282],[-48.134,-27.1645],[-48.329,-26.7805],[-48.349,-26.751],[-48.28,-26.2305],[-47.9805,-25.6805],[-47.9,-25.5235],[-47.854,-25.47],[-47.5855,-25.2985],[-47.172,-25.038],[-47.089,-24.982],[-46.571,-24.654],[-46.187,-24.535],[-46.0065,-24.471],[-45.707,-24.348],[-45.583,-24.301],[-45.335,-24.197],[-45.135,-24.12],[-44.4775,-23.6785],[-44.207,-23.49],[-44.0775,-23.405],[-43.9245,-23.3765],[-43.6785,-23.3435],[-43.196,-23.267],[-42.816,-23.2445],[-42.4165,-23.225],[-41.9905,-23.206],[-41.915,-23.1855],[-41.859,-23.1545],[-41.65,-22.917],[-41.5965,-22.8365],[-41.568,-22.7555],[-41.5085,-22.5265],[-41.482,-22.5035],[-41.181,-22.331],[-40.9455,-22.204],[-40.856,-22.148],[-40.785,-22.0465],[-40.7705,-21.9855],[-40.7695,-21.878],[-40.8145,-21.6765],[-40.6945,-21.5065],[-40.6315,-21.4365],[-40.1885,-20.7915],[-40.1615,-20.739],[-40.0495,-20.4215],[-39.991,-20.2655],[-39.9585,-20.1155],[-39.8995,-19.988],[-39.841,-19.885],[-39.799,-19.855],[-39.7305,-19.8385],[-39.6715,-19.802],[-39.6315,-19.757],[-39.536,-19.5515],[-39.499,-19.4485],[-39.4825,-19.375],[-39.4765,-19.2835],[-39.494,-19.2015],[-39.54,-18.8365],[-39.534,-18.6945],[-39.519,-18.6335],[-39.5215,-18.5545],[-39.461,-18.3935],[-39.4675,-18.3035],[-39.3875,-18.1825],[-39.353,-18.109],[-39.289,-18.141],[-38.962,-18.163],[-38.6705,-18.1685],[-38.593,-18.136],[-38.5365,-18.097],[-38.4915,-18.0045],[-38.4935,-17.9095],[-38.532,-17.8005],[-38.5845,-17.6835],[-38.7285,-17.3185],[-38.877,-16.944],[-38.9285,-16.7845],[-38.896,-16.7205],[-38.8725,-16.5375],[-38.8215,-16.3815],[-38.8015,-16.2485],[-38.7605,-16.153],[-38.716,-16.004],[-38.647,-15.8835],[-38.6435,-15.8305],[-38.6645,-15.7475],[-38.7155,-15.675],[-38.744,-15.601],[-38.7395,-15.425],[-38.77,-15.341],[-38.8015,-15.185],[-38.793,-14.96],[-38.8215,-14.8515],[-38.82,-14.748],[-38.8535,-14.655],[-38.85,-14.611],[-38.774,-14.2845],[-38.7845,-14.195],[-38.752,-14.09],[-38.7055,-13.7955],[-38.6765,-13.6585],[-38.637,-13.3985],[-38.6115,-13.2655],[-38.502,-13.206],[-38.388,-13.183],[-38.333,-13.147],[-38.2645,-13.1265],[-38.2215,-13.097],[-38.0995,-12.9795],[-38.004,-12.8675],[-37.8965,-12.7585],[-37.831,-12.675],[-37.7785,-12.5835],[-37.7095,-12.4885],[-37.675,-12.4255],[-37.602,-12.342],[-37.426,-12.041],[-37.378,-11.9335],[-37.301,-11.7845],[-37.282,-11.7185],[-37.2315,-11.6205],[-37.167,-11.544],[-37.1295,-11.477],[-37.1135,-11.4125],[-37.0785,-11.363],[-36.995,-11.296],[-36.9275,-11.153],[-36.7875,-10.9515],[-36.7,-10.861],[-36.5675,-10.788],[-36.4975,-10.7415],[-36.3625,-10.7045],[-36.2365,-10.6215],[-36.187,-10.544],[-36.131,-10.4875],[-36.0615,-10.3745],[-35.964,-10.313],[-35.857,-10.126],[-35.7545,-10.016],[-35.7165,-9.965],[-35.6965,-9.9005],[-35.6635,-9.8685],[-35.5585,-9.8125],[-35.4935,-9.7195],[-35.3255,-9.528],[-35.2945,-9.4795],[-35.226,-9.422],[-35.0255,-9.1375],[-34.9505,-8.9615],[-34.8805,-8.763],[-34.815,-8.6245],[-34.779,-8.468],[-34.7415,-8.374],[-34.7415,-8.304],[-34.7055,-8.182],[-34.651,-8.0355],[-34.635,-7.915],[-34.644,-7.808],[-34.634,-7.7455],[-34.604,-7.2705],[-34.618,-7.207],[-34.5995,-7.1115],[-34.619,-7.003],[-34.623,-6.903],[-34.716,-6.746],[-34.742,-6.611],[-34.764,-6.562],[-34.7735,-6.421],[-34.813,-6.284],[-34.837,-6.2405],[-34.85,-6.1705],[-34.9,-6.082],[-34.919,-5.9325],[-34.954,-5.857],[-34.996,-5.72],[-34.9985,-5.6485],[-35.0255,-5.5845],[-35.0385,-5.5105],[-35.0675,-5.458],[-35.089,-5.379],[-35.139,-5.278],[-35.155,-5.173],[-35.204,-5.085],[-35.337,-4.9925],[-35.5145,-4.931],[-35.5645,-4.9075],[-35.6745,-4.892],[-35.7635,-4.8665],[-35.8405,-4.859],[-35.9365,-4.8335],[-36.0595,-4.8415],[-36.1895,-4.885],[-36.2915,-4.853],[-36.3755,-4.86],[-36.5045,-4.8475],[-36.623,-4.858],[-36.681,-4.8405],[-36.8115,-4.7515],[-36.917,-4.7135],[-37.0785,-4.7135],[-37.1355,-4.598],[-37.1855,-4.5385],[-37.254,-4.49],[-37.346,-4.4685],[-37.4305,-4.428],[-37.512,-4.4155],[-37.566,-4.364],[-37.5915,-4.3015],[-37.665,-4.221],[-37.7795,-4.1805],[-37.902,-4.0725],[-37.9575,-4.016],[-38.035,-3.9095],[-38.1645,-3.7665],[-38.214,-3.741],[-38.3125,-3.577],[-38.3875,-3.5145],[-38.4485,-3.4885],[-38.5555,-3.4845],[-38.628,-3.4465],[-38.6925,-3.3785],[-38.7955,-3.3305],[-38.817,-3.2865],[-38.8625,-3.238],[-38.981,-3.189],[-39.033,-3.1575],[-39.069,-3.112],[-39.152,-3.0445],[-39.256,-2.991],[-39.3705,-2.954],[-39.528,-2.84],[-39.641,-2.796],[-39.817,-2.6885],[-39.8905,-2.6535],[-39.9815,-2.6305],[-40.107,-2.62],[-40.178,-2.6005],[-40.386,-2.601],[-40.464,-2.578],[-40.5435,-2.578],[-40.6185,-2.6025],[-40.6635,-2.6385],[-40.7735,-2.636],[-40.8245,-2.6465],[-40.956,-2.643],[-41.032,-2.676],[-41.1825,-2.681],[-41.2915,-2.672],[-41.3485,-2.68],[-41.5085,-2.685],[-41.641,-2.5955],[-41.689,-2.5765],[-41.739,-2.5345],[-41.827,-2.508],[-42.0005,-2.477],[-42.244,-2.4275],[-42.782,-2.3305],[-42.8125,-2.31],[-43.028,-2.214],[-43.1995,-2.1565],[-43.265,-2.1465],[-44.778,-1.1145],[-44.834,-1.088],[-45.944,-0.7595],[-46.1515,-0.698],[-47.248,-0.4025],[-47.291,-0.3945],[-47.607,-0.272],[-48.2555,-0.0125],[-48.597,0.4645],[-49.336,1.3205],[-49.771,1.8315],[-50.2875,2.3025],[-50.7865,3.006],[-50.8135,3.1485],[-50.8215,3.2495],[-50.855,3.4555],[-50.8485,3.5415],[-50.8475,3.8605],[-50.869,4.011],[-50.9425,4.2015],[-51.03,4.3425],[-51.1145,4.423],[-51.212,4.489],[-51.3065,4.542],[-51.4,4.6215],[-51.4945,4.6745],[-51.5695,4.585],[-51.637,4.5085],[-51.6455,4.395],[-51.6285,4.2685],[-51.6145,4.2355],[-51.6335,4.176],[-51.6445,4.083],[-51.6585,4.053],[-51.778,3.974],[-51.7975,3.889],[-51.8705,3.807],[-51.927,3.768],[-51.9215,3.724],[-51.971,3.707],[-51.9885,3.63],[-52.087,3.4885],[-52.161,3.3525],[-52.2365,3.2395],[-52.2925,3.2275],[-52.3,3.175],[-52.349,3.1355],[-52.326,3.079],[-52.3955,2.931],[-52.478,2.7805],[-52.4755,2.7635],[-52.533,2.65],[-52.5275,2.581],[-52.5605,2.551],[-52.5515,2.5205],[-52.6165,2.4755],[-52.6605,2.374],[-52.8425,2.2915],[-52.9045,2.189],[-52.9565,2.173],[-53.052,2.19],[-53.105,2.2225],[-53.2225,2.201],[-53.273,2.222],[-53.2255,2.2625],[-53.344,2.351],[-53.371,2.313],[-53.432,2.2925],[-53.4615,2.253],[-53.659,2.2795],[-53.723,2.349],[-53.768,2.3755],[-53.81,2.34],[-53.883,2.3085],[-53.892,2.277],[-53.9375,2.283],[-53.9395,2.2215],[-53.987,2.2115],[-54.015,2.183],[-54.081,2.1745],[-54.1065,2.121],[-54.1395,2.118],[-54.204,2.1625],[-54.2675,2.1435],[-54.363,2.18],[-54.398,2.2035],[-54.478,2.2155],[-54.5365,2.325],[-54.601,2.3375],[-54.642,2.322],[-54.7,2.406],[-54.6965,2.4625],[-54.7605,2.4635],[-54.828,2.4215],[-54.87,2.431],[-54.87,2.468],[-54.9125,2.485],[-54.954,2.5835],[-55.0035,2.591],[-55.103,2.5255],[-55.1235,2.5675],[-55.173,2.5595],[-55.2345,2.5035],[-55.32,2.5155],[-55.356,2.4755],[-55.3455,2.448],[-55.3855,2.4185],[-55.4295,2.439],[-55.4995,2.443],[-55.5705,2.4235],[-55.6985,2.4215],[-55.747,2.4105],[-55.7665,2.455],[-55.83,2.459],[-55.9345,2.5335],[-55.978,2.5275],[-55.9895,2.427],[-56.0365,2.374],[-56.0505,2.335],[-56.094,2.321],[-56.1255,2.278],[-56.1205,2.2505],[-56.043,2.228],[-56.0565,2.1895],[-56.003,2.1675],[-55.967,2.0885],[-55.909,2.0475],[-55.9365,1.9865],[-55.904,1.888],[-55.9565,1.845],[-55.999,1.8315],[-56.0365,1.85],[-56.0985,1.8465],[-56.1525,1.8905],[-56.2345,1.8985],[-56.244,1.878],[-56.335,1.937],[-56.412,1.9285],[-56.469,1.949],[-56.58,1.906],[-56.6215,1.946],[-56.651,1.917],[-56.721,1.926],[-56.774,1.869],[-56.8935,1.8995],[-56.9195,1.9305],[-57.0005,1.9075],[-57.071,1.9685],[-57.071,1.9965],[-57.12,2.01],[-57.2555,1.9505],[-57.277,1.983],[-57.3455,1.9805],[-57.3675,1.9235],[-57.4335,1.906],[-57.432,1.8585],[-57.451,1.8065],[-57.4975,1.767],[-57.553,1.6925],[-57.6295,1.6985],[-57.6505,1.6825],[-57.705,1.731],[-57.7925,1.7265],[-57.8,1.685],[-57.895,1.6745],[-57.927,1.645],[-57.981,1.6595],[-57.9785,1.573],[-58.0055,1.517],[-58.061,1.5255],[-58.1295,1.499],[-58.1605,1.56],[-58.317,1.5685],[-58.3935,1.52],[-58.3855,1.47],[-58.5085,1.463],[-58.505,1.403],[-58.47,1.371],[-58.471,1.3195],[-58.496,1.268],[-58.5885,1.27],[-58.6285,1.2885],[-58.71,1.2835],[-58.7395,1.2],[-58.821,1.171],[-58.8545,1.1835],[-58.904,1.251],[-58.918,1.317],[-59.048,1.323],[-59.253,1.389],[-59.339,1.519],[-59.378,1.521],[-59.431,1.567],[-59.447,1.62],[-59.483,1.627],[-59.493,1.674],[-59.566,1.736],[-59.617,1.716],[-59.632,1.741],[-59.69,1.757],[-59.678,1.807],[-59.691,1.854],[-59.746,1.852],[-59.745,1.929],[-59.73,2.0305],[-59.7425,2.068],[-59.723,2.0985],[-59.7405,2.1795],[-59.72,2.237],[-59.741,2.293],[-59.7805,2.2855],[-59.905,2.3755],[-59.8935,2.4645],[-59.93,2.5655],[-59.978,2.642],[-59.991,2.683],[-59.9935,2.7825],[-59.984,2.9295],[-59.9475,3.003],[-59.9555,3.082],[-59.9015,3.1605],[-59.9125,3.195],[-59.8745,3.229],[-59.8815,3.259],[-59.805,3.357],[-59.84,3.4435],[-59.802,3.493],[-59.859,3.571],[-59.8505,3.6045],[-59.7655,3.626],[-59.7425,3.66],[-59.6685,3.703],[-59.6655,3.7815],[-59.5955,3.7955],[-59.5775,3.832],[-59.593,3.8855],[-59.523,3.932],[-59.5265,3.969],[-59.5865,3.9755],[-59.602,4.03],[-59.6425,4.0675],[-59.6195,4.133],[-59.685,4.147],[-59.7305,4.181],[-59.732,4.286],[-59.6755,4.3465],[-59.735,4.4235],[-59.805,4.466],[-59.911,4.4765],[-59.971,4.5095],[-60.0015,4.495],[-60.071,4.4935],[-60.161,4.5175],[-60.1235,4.605],[-60.07,4.618],[-60.0755,4.6555],[-60.026,4.7055],[-60.0135,4.843],[-59.9795,5.0155],[-59.9825,5.0825],[-60.094,5.14],[-60.106,5.194],[-60.132,5.225],[-60.183,5.2275],[-60.2195,5.269],[-60.313,5.211],[-60.384,5.222],[-60.434,5.182],[-60.527,5.208],[-60.569,5.196],[-60.614,5.219],[-60.682,5.232],[-60.7375,5.202],[-60.694,5.197],[-60.661,5.164],[-60.644,5.057],[-60.599,5.0],[-60.584,4.956],[-60.659,4.849],[-60.726,4.792],[-60.751,4.756],[-60.806,4.749],[-60.852,4.704],[-60.899,4.717],[-60.964,4.544],[-60.994,4.519],[-61.065,4.524],[-61.1195,4.5105],[-61.1475,4.481],[-61.188,4.521],[-61.269,4.54],[-61.323,4.535],[-61.322,4.504],[-61.278,4.478],[-61.351,4.419],[-61.462,4.437],[-61.513,4.406],[-61.508,4.322],[-61.56,4.252],[-61.631,4.241],[-61.658,4.262],[-61.769,4.249],[-61.802,4.229],[-61.818,4.168],[-61.922,4.1615],[-61.9265,4.1245],[-61.993,4.175],[-62.064,4.159],[-62.148,4.079],[-62.252,4.13],[-62.329,4.135],[-62.389,4.178],[-62.462,4.178],[-62.46,4.143],[-62.552,4.109],[-62.534,4.052],[-62.572,4.015],[-62.609,4.044],[-62.701,4.031],[-62.736,4.04],[-62.768,4.007],[-62.743,3.975],[-62.782,3.935],[-62.788,3.894],[-62.729,3.805],[-62.731,3.708],[-62.748,3.673],[-62.804,3.73],[-62.835,3.739],[-62.886,3.684],[-62.919,3.673],[-62.952,3.612],[-62.984,3.61],[-63.032,3.666],[-63.072,3.686],[-63.06,3.752],[-63.123,3.805],[-63.204,3.812],[-63.233,3.882],[-63.204,3.952],[-63.338,3.957],[-63.393,3.981],[-63.452,3.956],[-63.411,3.912],[-63.452,3.859],[-63.489,3.874],[-63.512,3.848],[-63.593,3.906],[-63.592,3.929],[-63.652,4.002],[-63.685,4.009],[-63.7,3.945],[-63.796,3.933],[-63.85,3.95],[-63.928,3.925],[-63.959,3.888],[-64.035,3.935],[-64.097,4.026],[-64.112,4.092],[-64.171,4.129],[-64.218,4.116],[-64.274,4.143],[-64.335,4.129],[-64.431,4.135],[-64.5,4.113],[-64.592,4.114],[-64.623,4.135],[-64.659,4.22],[-64.695,4.253],[-64.78,4.287],[-64.8195,4.235],[-64.802,4.174],[-64.7225,4.1175],[-64.6335,3.962],[-64.587,3.9325],[-64.5435,3.857],[-64.4775,3.7935],[-64.3675,3.76],[-64.281,3.7095],[-64.2675,3.6685],[-64.201,3.605],[-64.172,3.559],[-64.1755,3.515],[-64.2425,3.4355],[-64.222,3.3875],[-64.2285,3.3155],[-64.198,3.201],[-64.2255,3.1365],[-64.1695,3.072],[-64.124,2.9905],[-64.0715,2.9215],[-64.0405,2.829],[-63.9825,2.715],[-63.9905,2.6365],[-64.057,2.5065],[-64.0305,2.481],[-63.931,2.4555],[-63.8425,2.491],[-63.7725,2.4415],[-63.703,2.4535],[-63.6555,2.4365],[-63.5925,2.4475],[-63.523,2.417],[-63.4385,2.436],[-63.3795,2.4195],[-63.3635,2.376],[-63.384,2.352],[-63.3675,2.2685],[-63.401,2.1505],[-63.511,2.1115],[-63.564,2.134],[-63.6355,2.099],[-63.664,2.065],[-63.7115,2.0465],[-63.741,2.0035],[-63.8355,1.9665],[-63.893,1.9895],[-63.972,1.992],[-64.061,1.925],[-64.067,1.8535],[-64.0545,1.803],[-64.076,1.782],[-64.061,1.7025],[-64.077,1.645],[-64.1315,1.576],[-64.173,1.5735],[-64.1965,1.5215],[-64.2435,1.507],[-64.3135,1.456],[-64.3435,1.382],[-64.41,1.403],[-64.348,1.495],[-64.3945,1.5285],[-64.436,1.47],[-64.4665,1.4735],[-64.537,1.4155],[-64.581,1.3595],[-64.6455,1.3005],[-64.6955,1.284],[-64.721,1.2395],[-64.756,1.228],[-64.8025,1.314],[-64.8555,1.266],[-64.8955,1.251],[-64.9715,1.18],[-65.0105,1.1115],[-65.1055,1.156],[-65.1515,1.109],[-65.1755,1.0155],[-65.1755,0.941],[-65.265,0.9205],[-65.3255,0.936],[-65.356,0.865],[-65.396,0.822],[-65.3965,0.7775],[-65.445,0.689],[-65.556,0.654],[-65.6045,0.715],[-65.502,0.849],[-65.512,0.896],[-65.5575,0.9915],[-65.627,1.0145],[-65.743,1.001],[-65.788,0.965],[-65.888,0.91],[-65.941,0.891],[-65.9435,0.8635],[-66.0145,0.804],[-66.07,0.8125],[-66.077,0.762],[-66.1455,0.7475],[-66.1975,0.7815],[-66.3105,0.745],[-66.598,1.0],[-66.851,1.229],[-67.087,1.167],[-67.0995,1.2585],[-67.135,1.3],[-67.134,1.344],[-67.083,1.373],[-67.0855,1.4635],[-67.128,1.543],[-67.1275,1.64],[-67.155,1.665],[-67.16,1.735],[-67.146,1.762],[-67.154,1.8315],[-67.2285,1.8415],[-67.3085,1.9085],[-67.346,1.974],[-67.351,2.012],[-67.3255,2.0545],[-67.358,2.0965],[-67.351,2.16],[-67.3735,2.217],[-67.4405,2.2155],[-67.449,2.1695],[-67.497,2.161],[-67.551,2.0475],[-67.6215,2.0275],[-67.729,2.029],[-67.762,2.014],[-67.835,1.947],[-67.8365,1.9025],[-67.889,1.8645],[-67.9015,1.8115],[-67.9435,1.843],[-67.9885,1.849],[-68.0335,1.891],[-68.096,1.903],[-68.1495,1.9825],[-68.1845,1.986],[-68.245,1.9195],[-68.2855,1.836],[-68.2375,1.8215],[-68.2445,1.7835],[-68.178,1.7675],[-68.1865,1.73],[-68.735,1.731],[-69.391,1.7295],[-69.4825,1.751],[-69.534,1.7765],[-69.655,1.7175],[-69.7445,1.7295],[-69.809,1.695],[-69.843,1.715],[-69.8425,1.0725],[-69.816,1.063],[-69.746,1.1135],[-69.7015,1.1175],[-69.703,1.075],[-69.61,1.0985],[-69.5735,1.073],[-69.4875,1.079],[-69.3825,1.0735],[-69.321,1.0885],[-69.2585,1.054],[-69.1975,0.998],[-69.207,0.965],[-69.1815,0.9025],[-69.149,0.7675],[-69.19,0.7405],[-69.118,0.6555],[-69.1945,0.65],[-69.213,0.61],[-69.2705,0.602],[-69.298,0.649],[-69.349,0.618],[-69.415,0.688],[-69.483,0.733],[-69.6335,0.6365],[-69.6875,0.664],[-69.8095,0.5765],[-69.9155,0.5795],[-69.935,0.553],[-70.047,0.563],[-70.047,0.26],[-70.043,0.069],[-70.043,-0.19],[-69.888,-0.3415],[-69.8435,-0.3385],[-69.7735,-0.3915],[-69.736,-0.4375],[-69.6675,-0.4655],[-69.6085,-0.5045],[-69.591,-0.561],[-69.6015,-0.5965],[-69.563,-0.6405],[-69.622,-0.733],[-69.574,-0.81],[-69.573,-0.8385],[-69.5265,-0.8675],[-69.5295,-0.9175],[-69.4725,-0.9575],[-69.4425,-1.034],[-69.397,-1.1145],[-69.43,-1.235],[-69.407,-1.29],[-69.3975,-1.365],[-69.436,-1.402],[-69.4325,-1.454],[-69.4565,-1.491],[-69.4505,-1.53],[-69.535,-2.003],[-69.613,-2.44],[-69.714,-3.0025],[-69.836,-3.686],[-69.9335,-4.2195],[-69.948,-4.2315],[-69.952,-4.311],[-70.04,-4.348],[-70.126,-4.2865],[-70.202,-4.3555],[-70.211,-4.3175],[-70.256,-4.31],[-70.284,-4.272],[-70.2875,-4.1655],[-70.3295,-4.1445],[-70.43,-4.131],[-70.467,-4.1765],[-70.523,-4.1375],[-70.5845,-4.1855],[-70.6355,-4.172],[-70.672,-4.1955],[-70.7265,-4.185],[-70.7475,-4.161],[-70.8025,-4.1845],[-70.8365,-4.2555],[-70.878,-4.2935],[-70.9335,-4.3795],[-71.017,-4.383],[-71.038,-4.402],[-71.085,-4.373],[-71.107,-4.401],[-71.145,-4.3815],[-71.2615,-4.3995],[-71.264,-4.4305],[-71.34,-4.443],[-71.3795,-4.425],[-71.422,-4.4685],[-71.537,-4.4635],[-71.566,-4.5045],[-71.6195,-4.528],[-71.6445,-4.5015],[-71.708,-4.511],[-71.784,-4.4835],[-71.887,-4.53],[-71.9865,-4.6135],[-72.059,-4.6495],[-72.102,-4.706],[-72.208,-4.7465],[-72.2295,-4.7755],[-72.31,-4.7745],[-72.3265,-4.8015],[-72.3825,-4.8315],[-72.388,-4.8655],[-72.429,-4.9085],[-72.4855,-4.935],[-72.5205,-4.933],[-72.5995,-4.9875],[-72.607,-5.028],[-72.6595,-5.063],[-72.7085,-5.049],[-72.822,-5.1405],[-72.887,-5.1615],[-72.864,-5.2345],[-72.867,-5.29],[-72.8955,-5.325],[-72.9245,-5.408],[-72.958,-5.4645],[-72.9495,-5.5475],[-72.9735,-5.6135],[-72.959,-5.6565],[-73.0575,-5.7965],[-73.152,-5.868],[-73.1855,-5.95],[-73.1935,-6.0055],[-73.2375,-6.043],[-73.25,-6.133],[-73.221,-6.177],[-73.1865,-6.2625],[-73.1605,-6.2765],[-73.1565,-6.327],[-73.1115,-6.447],[-73.171,-6.518],[-73.1995,-6.5695],[-73.2315,-6.5855],[-73.3525,-6.5935],[-73.392,-6.6415],[-73.521,-6.676],[-73.5615,-6.7215],[-73.6035,-6.732],[-73.7105,-6.84],[-73.724,-6.8875],[-73.7615,-6.94],[-73.762,-7.0625],[-73.7985,-7.113],[-73.7165,-7.2275],[-73.695,-7.299],[-73.7225,-7.341],[-73.8185,-7.338],[-73.8615,-7.388],[-73.9235,-7.362],[-73.9175,-7.5005],[-73.9585,-7.5645],[-73.8895,-7.603],[-73.9,-7.639],[-73.869,-7.674],[-73.8375,-7.6705],[-73.8035,-7.714],[-73.714,-7.743],[-73.6765,-7.8],[-73.678,-7.858],[-73.7355,-7.879],[-73.767,-7.9105],[-73.7455,-7.9495],[-73.706,-7.962],[-73.669,-8.0125],[-73.6405,-8.0065],[-73.585,-8.127],[-73.5955,-8.166],[-73.563,-8.2455],[-73.526,-8.2745],[-73.543,-8.3475],[-73.4415,-8.4255],[-73.408,-8.4305],[-73.373,-8.472],[-73.331,-8.476],[-73.344,-8.6025],[-73.3195,-8.6105],[-73.251,-8.6905],[-73.167,-8.6985],[-73.1145,-8.7855],[-73.112,-8.8195],[-73.037,-8.916],[-73.0015,-8.917],[-72.948,-9.029],[-72.94,-9.093],[-72.9635,-9.144],[-73.0195,-9.1855],[-73.034,-9.234],[-73.064,-9.2275],[-73.156,-9.3565],[-73.189,-9.363],[-73.2005,-9.4115],[-72.717,-9.4125],[-72.6975,-9.436],[-72.6425,-9.4435],[-72.5305,-9.4825],[-72.407,-9.4775],[-72.341,-9.5065],[-72.318,-9.544],[-72.2815,-9.5425],[-72.2885,-9.603],[-72.246,-9.6575],[-72.259,-9.712],[-72.215,-9.779],[-72.1515,-9.7975],[-72.162,-9.831],[-72.1505,-9.9055],[-72.174,-9.93],[-72.1615,-9.9805],[-72.181,-10.0005],[-71.8225,-10.0005],[-71.378,-10.0005],[-71.3425,-9.967],[-71.2975,-9.9925],[-71.192,-9.9405],[-71.1575,-9.8725],[-71.051,-9.816],[-70.9985,-9.818],[-70.974,-9.7605],[-70.9245,-9.7415],[-70.868,-9.665],[-70.799,-9.642],[-70.75,-9.5695],[-70.6735,-9.5295],[-70.5845,-9.443],[-70.5055,-9.422],[-70.4945,-9.4405],[-70.5415,-9.538],[-70.5915,-9.5485],[-70.598,-9.6075],[-70.551,-9.67],[-70.528,-9.726],[-70.5365,-9.766],[-70.6125,-9.8055],[-70.613,-10.162],[-70.6135,-10.608],[-70.614,-10.9965],[-70.5305,-10.9345],[-70.4375,-11.0265],[-70.3765,-11.06],[-70.3085,-11.071],[-70.256,-11.0505],[-70.171,-11.043],[-70.092,-10.9855],[-70.067,-10.9945],[-69.901,-10.92],[-69.7885,-10.9295],[-69.7395,-10.964],[-69.572,-10.9455],[-69.522,-10.936],[-69.479,-10.955],[-69.401,-10.9305],[-69.2915,-10.95],[-69.2325,-10.94],[-69.148,-10.972],[-69.0715,-10.967],[-68.9975,-11.0025],[-68.9685,-10.9875],[-68.9125,-11.0215],[-68.8345,-10.993],[-68.748,-11.009],[-68.7655,-11.0665],[-68.73,-11.0985],[-68.7155,-11.1475],[-68.5905,-11.1155],[-68.4835,-11.0775],[-68.429,-11.036],[-68.2765,-10.99],[-68.236,-10.9605],[-68.196,-10.8605],[-68.1255,-10.7985],[-68.0975,-10.7485],[-68.105,-10.715],[-68.015,-10.649],[-67.977,-10.662],[-67.866,-10.6405],[-67.8275,-10.6515],[-67.73,-10.7135],[-67.7075,-10.71],[-67.6755,-10.605],[-67.612,-10.5635],[-67.579,-10.503],[-67.451,-10.4585],[-67.4135,-10.427],[-67.4195,-10.388],[-67.318,-10.3665],[-67.3165,-10.32],[-67.2625,-10.331],[-67.232,-10.315],[-67.17,-10.3375],[-67.125,-10.2905],[-67.079,-10.2895],[-67.016,-10.2585],[-66.9825,-10.1965],[-66.898,-10.111],[-66.8435,-10.087],[-66.8335,-10.061],[-66.7285,-9.98],[-66.6365,-9.943],[-66.62,-9.8935],[-66.4885,-9.868],[-66.3585,-9.864],[-66.312,-9.831],[-66.284,-9.844],[-66.2375,-9.8135],[-66.212,-9.835],[-66.173,-9.7925],[-66.0385,-9.785],[-65.984,-9.8045],[-65.975,-9.7765],[-65.886,-9.752],[-65.855,-9.7955],[-65.8205,-9.7675],[-65.7845,-9.7875],[-65.727,-9.7585],[-65.6695,-9.7815],[-65.627,-9.838],[-65.5125,-9.7935],[-65.5015,-9.7305],[-65.442,-9.6905],[-65.393,-9.687],[-65.355,-9.7225],[-65.2885,-9.869],[-65.331,-9.929],[-65.32,-10.001],[-65.329,-10.046],[-65.301,-10.098],[-65.306,-10.165],[-65.2885,-10.2145],[-65.324,-10.301],[-65.3905,-10.371],[-65.381,-10.429],[-65.4295,-10.4795],[-65.399,-10.5815],[-65.4055,-10.6395],[-65.354,-10.6615],[-65.343,-10.7675],[-65.363,-10.801],[-65.321,-10.8225],[-65.275,-10.873],[-65.2705,-10.96],[-65.2515,-10.99],[-65.2985,-11.026],[-65.287,-11.094],[-65.353,-11.124],[-65.3585,-11.2685],[-65.339,-11.311],[-65.3085,-11.496],[-65.224,-11.516],[-65.234,-11.605],[-65.22,-11.6635],[-65.2585,-11.697],[-65.221,-11.7415],[-65.1845,-11.721],[-65.099,-11.703],[-65.064,-11.7715],[-65.0725,-11.857],[-65.0275,-11.892],[-65.035,-11.9945],[-64.9805,-12.0175],[-64.944,-11.995],[-64.9175,-12.0265],[-64.832,-12.016],[-64.8035,-12.0865],[-64.7605,-12.0945],[-64.7405,-12.1445],[-64.693,-12.1895],[-64.5605,-12.2365],[-64.509,-12.2285],[-64.4955,-12.2675],[-64.5035,-12.3655],[-64.4555,-12.3865],[-64.428,-12.432],[-64.3625,-12.4605],[-64.2995,-12.4565],[-64.277,-12.4985],[-64.228,-12.4545],[-64.1725,-12.47],[-64.174,-12.503],[-64.0555,-12.4935],[-64.0365,-12.517],[-63.99,-12.503],[-63.9455,-12.528],[-63.901,-12.503],[-63.8765,-12.445],[-63.831,-12.461],[-63.7835,-12.428],[-63.623,-12.4725],[-63.559,-12.504],[-63.507,-12.5645],[-63.436,-12.5665],[-63.3885,-12.6495],[-63.297,-12.6825],[-63.24,-12.6905],[-63.1825,-12.6275],[-63.09,-12.638],[-63.084,-12.7165],[-63.0465,-12.741],[-62.9705,-12.8595],[-62.9215,-12.84],[-62.8745,-12.905],[-62.8045,-12.943],[-62.7965,-12.993],[-62.7005,-13.002],[-62.6615,-12.9685],[-62.631,-12.99],[-62.6145,-13.038],[-62.539,-13.074],[-62.459,-13.077],[-62.394,-13.1335],[-62.2645,-13.147],[-62.2155,-13.1105],[-62.1985,-13.152],[-62.1215,-13.151],[-62.115,-13.256],[-62.017,-13.35],[-61.976,-13.367],[-61.926,-13.4285],[-61.8885,-13.441],[-61.869,-13.526],[-61.7865,-13.5375],[-61.6645,-13.503],[-61.578,-13.5115],[-61.4665,-13.554],[-61.331,-13.5035],[-61.2545,-13.4965],[-61.2345,-13.5255],[-61.149,-13.532],[-61.086,-13.492],[-61.0425,-13.5085],[-61.009,-13.5495],[-60.927,-13.5395],[-60.877,-13.62],[-60.832,-13.627],[-60.771,-13.6795],[-60.687,-13.7005],[-60.654,-13.7355],[-60.6245,-13.719],[-60.569,-13.7685],[-60.5205,-13.77],[-60.453,-13.881],[-60.4505,-13.927],[-60.4185,-13.9375],[-60.4045,-13.9985],[-60.4475,-14.0845],[-60.4845,-14.1145],[-60.4545,-14.3135],[-60.3945,-14.369],[-60.407,-14.4015],[-60.379,-14.4645],[-60.34,-14.51],[-60.323,-14.6105],[-60.2735,-14.621],[-60.245,-15.0975],[-60.575,-15.0975],[-60.239,-15.475],[-60.203,-15.885],[-60.174,-16.2665],[-59.47,-16.279],[-58.785,-16.306],[-58.431,-16.3225],[-58.3665,-16.2745],[-58.3295,-16.272],[-58.3035,-16.31],[-58.308,-16.3715],[-58.344,-16.389],[-58.356,-16.428],[-58.334,-16.478],[-58.343,-16.517],[-58.436,-16.592],[-58.47,-16.703],[-58.476,-16.827],[-58.461,-16.841],[-58.462,-16.958],[-58.431,-16.99],[-58.425,-17.112],[-58.398,-17.104],[-58.396,-17.181],[-58.356,-17.212],[-58.317,-17.268],[-58.276,-17.3],[-58.254,-17.352],[-58.202,-17.357],[-58.151,-17.384],[-58.116,-17.451],[-58.06,-17.45],[-58.043,-17.492],[-57.98,-17.51],[-57.883,-17.449],[-57.746,-17.551],[-57.791,-17.5865],[-57.685,-17.8105],[-57.723,-17.8305],[-57.6,-18.046],[-57.574,-18.131],[-57.505,-18.2005],[-57.557,-18.24],[-57.766,-18.899],[-57.719,-18.899],[-57.71,-19.0345],[-57.784,-19.033],[-57.9875,-19.46],[-58.131,-19.758],[-57.858,-19.9705],[-57.905,-20.043],[-57.961,-20.0245],[-57.9795,-20.057],[-58.0215,-20.0585],[-58.055,-20.109],[-58.1695,-20.167],[-58.128,-20.183],[-58.166,-20.231],[-58.1435,-20.272],[-58.0915,-20.26],[-58.1025,-20.3225],[-58.075,-20.3865],[-58.022,-20.4005],[-57.9965,-20.448],[-58.016,-20.527],[-57.995,-20.5615],[-58.0135,-20.6055],[-57.9705,-20.6425],[-57.988,-20.6845],[-57.9515,-20.702],[-57.9155,-20.6685],[-57.87,-20.7255],[-57.935,-20.7455],[-57.856,-20.848],[-57.927,-20.893],[-57.8815,-20.914],[-57.8165,-20.9795],[-57.867,-21.04],[-57.8465,-21.098],[-57.8645,-21.14],[-57.8515,-21.2245],[-57.92,-21.2765],[-57.8525,-21.32],[-57.9095,-21.4065],[-57.9275,-21.4705],[-57.9685,-21.529],[-57.9205,-21.581],[-57.941,-21.6355],[-57.896,-21.7045],[-57.946,-21.741],[-57.9085,-21.773],[-57.93,-21.823],[-57.9165,-21.8765],[-57.974,-22.0155],[-58.009,-22.0395],[-57.93,-22.119],[-57.8005,-22.1505],[-57.733,-22.0995],[-57.6895,-22.0885],[-57.637,-22.106],[-57.5865,-22.145],[-57.5805,-22.1755],[-57.5205,-22.1695],[-57.4835,-22.1925],[-57.389,-22.205],[-57.362,-22.234],[-57.304,-22.224],[-57.237,-22.251],[-57.1785,-22.233],[-57.0605,-22.2305],[-56.946,-22.256],[-56.8835,-22.2475],[-56.8445,-22.3005],[-56.7445,-22.241],[-56.7055,-22.2365],[-56.638,-22.258],[-56.61,-22.223],[-56.5565,-22.195],[-56.517,-22.108],[-56.4715,-22.08],[-56.419,-22.0795],[-56.368,-22.137],[-56.3575,-22.176],[-56.316,-22.218],[-56.243,-22.2355],[-56.2135,-22.2755],[-56.1235,-22.2715],[-55.9695,-22.292],[-55.853,-22.2805],[-55.7895,-22.3845],[-55.749,-22.3835],[-55.735,-22.459],[-55.753,-22.4755],[-55.7235,-22.552],[-55.6255,-22.628],[-55.6135,-22.694],[-55.62,-22.765],[-55.651,-22.7925],[-55.6655,-22.852],[-55.634,-22.992],[-55.596,-23.1195],[-55.542,-23.156],[-55.5235,-23.1985],[-55.542,-23.227],[-55.556,-23.3165],[-55.5045,-23.379],[-55.5445,-23.4485],[-55.5465,-23.5225],[-55.527,-23.5675],[-55.5305,-23.628],[-55.475,-23.6505],[-55.464,-23.712],[-55.43,-23.79],[-55.4465,-23.917],[-55.4195,-23.9575],[-55.336,-23.993],[-55.3175,-23.963],[-55.229,-24.014],[-55.1295,-23.984],[-55.0625,-23.993],[-54.999,-23.9575],[-54.9275,-23.9615],[-54.927,-23.9245],[-54.8595,-23.905],[-54.7935,-23.868],[-54.7055,-23.8645],[-54.6685,-23.8175],[-54.589,-23.839],[-54.515,-23.885],[-54.44,-23.9045],[-54.415,-23.958],[-54.325,-24.021],[-54.282,-24.0735],[-54.3455,-24.147],[-54.3155,-24.258],[-54.277,-24.2965],[-54.2605,-24.3705],[-54.3215,-24.4545],[-54.336,-24.515],[-54.3175,-24.5815],[-54.3245,-24.6605],[-54.3955,-24.803],[-54.4355,-24.9475],[-54.4555,-25.0975],[-54.4275,-25.1505],[-54.499,-25.313],[-54.5835,-25.3955],[-54.6185,-25.4515],[-54.602,-25.4855],[-54.5935,-25.593],[-54.5545,-25.5875],[-54.4255,-25.663],[-54.3745,-25.594],[-54.28,-25.556],[-54.255,-25.596],[-54.1905,-25.5345],[-54.166,-25.543],[-54.1045,-25.4945],[-54.0835,-25.561],[-54.0285,-25.566],[-53.862,-25.659],[-53.8645,-25.7455],[-53.8225,-25.793],[-53.849,-25.835],[-53.825,-25.871],[-53.8425,-25.9325],[-53.772,-26.0315],[-53.7355,-26.042],[-53.742,-26.1155],[-53.711,-26.1305],[-53.6435,-26.2165],[-53.646,-26.2865],[-53.6855,-26.335],[-53.7075,-26.3955],[-53.689,-26.4435],[-53.698,-26.4865],[-53.731,-26.5095],[-53.7225,-26.5815],[-53.742,-26.648],[-53.717,-26.6815],[-53.749,-26.7415],[-53.7145,-26.7535],[-53.682,-26.9165],[-53.726,-26.9585],[-53.718,-26.9845],[-53.7665,-27.044],[-53.7965,-27.0395],[-53.806,-27.1125],[-53.8455,-27.161],[-53.9,-27.173],[-53.959,-27.158],[-54.0055,-27.197],[-54.021,-27.2485],[-54.08,-27.2985],[-54.1585,-27.2935],[-54.192,-27.3075],[-54.2175,-27.386],[-54.3085,-27.423],[-54.3535,-27.4665],[-54.3965,-27.4125],[-54.4645,-27.424],[-54.4495,-27.476],[-54.52,-27.5025],[-54.5715,-27.4545],[-54.614,-27.5315],[-54.689,-27.574],[-54.742,-27.561],[-54.786,-27.576],[-54.814,-27.533],[-54.858,-27.629],[-54.907,-27.6465],[-54.903,-27.721],[-54.9345,-27.769],[-55.043,-27.775],[-55.0325,-27.825],[-55.1255,-27.859],[-55.197,-27.857],[-55.269,-27.931],[-55.31,-27.9215],[-55.3365,-27.9615],[-55.385,-27.985],[-55.3815,-28.0395],[-55.4475,-28.099],[-55.4835,-28.0775],[-55.536,-28.119],[-55.552,-28.1605],[-55.6115,-28.1185],[-55.635,-28.1785],[-55.7,-28.222],[-55.769,-28.2405],[-55.776,-28.271],[-55.67,-28.331],[-55.6915,-28.4175],[-55.739,-28.3765],[-55.8515,-28.355],[-55.9025,-28.412],[-55.881,-28.4695],[-55.986,-28.4975],[-56.022,-28.52],[-56.002,-28.596],[-56.1185,-28.6815],[-56.192,-28.7735],[-56.259,-28.7785],[-56.301,-28.8155],[-56.302,-28.901],[-56.3905,-28.955],[-56.3985,-29.021],[-56.4255,-29.082],[-56.5005,-29.0925],[-56.5915,-29.124],[-56.644,-29.196],[-56.648,-29.2585],[-56.7015,-29.359],[-56.766,-29.3775],[-56.7805,-29.439],[-56.819,-29.488],[-56.899,-29.532],[-56.97,-29.604],[-56.9665,-29.6295],[-57.033,-29.696],[-57.121,-29.765],[-57.242,-29.788],[-57.326,-29.876],[-57.328,-29.972],[-57.3625,-30.013],[-57.414,-30.037],[-57.4805,-30.123],[-57.584,-30.177],[-57.649,-30.194],[-57.5665,-30.2075],[-57.5495,-30.2735],[-57.4185,-30.2775],[-57.3975,-30.3045],[-57.31,-30.259],[-57.291,-30.289],[-57.2035,-30.2855],[-57.167,-30.248],[-57.168,-30.198],[-57.115,-30.1135],[-56.994,-30.086],[-56.906,-30.1075],[-56.8505,-30.089],[-56.781,-30.126],[-56.7845,-30.1505],[-56.6625,-30.1975],[-56.6055,-30.2925],[-56.5375,-30.3355],[-56.49,-30.3985],[-56.3995,-30.447],[-56.384,-30.4975],[-56.1885,-30.6045],[-56.146,-30.706],[-56.062,-30.7745],[-56.023,-30.7855],[-55.9895,-30.8585],[-56.008,-30.8945],[-56.019,-31.0435],[-56.008,-31.0705],[-55.933,-31.087],[-55.8715,-31.071],[-55.8135,-31.0155],[-55.7805,-31.017],[-55.6485,-30.923],[-55.661,-30.8695],[-55.582,-30.834],[-55.5455,-30.8905],[-55.51,-30.902],[-55.4805,-30.9525],[-55.4415,-30.9595],[-55.4375,-31.0045],[-55.3715,-31.0235],[-55.3055,-31.141],[-55.25,-31.206],[-55.2465,-31.2525],[-55.1835,-31.2655],[-55.0745,-31.332],[-55.0025,-31.2695],[-54.9385,-31.354],[-54.858,-31.408],[-54.8365,-31.442],[-54.755,-31.4245],[-54.6705,-31.454],[-54.5865,-31.4565],[-54.5155,-31.5105],[-54.4745,-31.5695],[-54.4565,-31.6515],[-54.081,-31.9295],[-54.0315,-31.896],[-53.9725,-31.9395],[-53.8495,-32.001],[-53.8065,-32.07],[-53.746,-32.0785],[-53.711,-32.1995],[-53.685,-32.2195],[-53.6335,-32.3505],[-53.644,-32.385],[-53.5565,-32.4725],[-53.471,-32.4805],[-53.433,-32.5575],[-53.3715,-32.5695],[-53.2795,-32.6195],[-53.1935,-32.6415],[-53.0755,-32.741],[-53.086,-32.7885],[-53.1385,-32.7895],[-53.185,-32.8505],[-53.2945,-32.8995],[-53.2445,-32.9345],[-53.4445,-33.053],[-53.518,-33.1535],[-53.5215,-33.265],[-53.508,-33.353],[-53.511,-33.4595],[-53.5255,-33.5265],[-53.5325,-33.689],[-53.4395,-33.6935],[-53.4305,-33.739],[-53.3715,-33.743],[-53.181,-33.869]]],[[[-34.018,-3.85],[-34.0065,-3.918],[-33.959,-3.992],[-33.892,-4.0365],[-33.8165,-4.051],[-33.7295,-4.0315],[-33.6665,-3.984],[-33.6275,-3.9185],[-33.615,-3.85],[-33.6325,-3.7685],[-33.687,-3.6955],[-33.75,-3.66],[-33.8165,-3.649],[-33.8935,-3.664],[-33.96,-3.709],[-34.0005,-3.7665],[-34.018,-3.85]]],[[[-32.6405,-3.797],[-32.6365,-3.8885],[-32.5935,-3.9695],[-32.519,-4.0235],[-32.429,-4.04],[-32.3405,-4.016],[-32.271,-3.956],[-32.2345,-3.872],[-32.238,-3.7805],[-32.2815,-3.6995],[-32.3555,-3.6455],[-32.4455,-3.6285],[-32.5345,-3.6525],[-32.604,-3.7125],[-32.6405,-3.797]]],[[[-29.5585,-20.502],[-29.5335,-20.5985],[-29.4925,-20.653],[-29.407,-20.7115],[-29.3295,-20.728],[-29.2,-20.7035],[-29.15,-20.669],[-29.0975,-20.5925],[-29.0825,-20.527],[-29.099,-20.437],[-29.175,-20.346],[-29.249,-20.301],[-29.3175,-20.2845],[-29.4325,-20.305],[-29.508,-20.362],[-29.5485,-20.436],[-29.5585,-20.502]]],[[[-29.5475,0.9255],[-29.541,0.869],[-29.503,0.794],[-29.4355,0.7395],[-29.3635,0.719],[-29.2945,0.725],[-29.249,0.7435],[-29.172,0.8195],[-29.145,0.926],[-29.163,1.003],[-29.2215,1.0775],[-29.2925,1.114],[-29.359,1.1205],[-29.4315,1.102],[-29.4895,1.0615],[-29.5475,0.9255]]],[[[-29.064,-20.466],[-29.0385,-20.5795],[-28.965,-20.662],[-28.8835,-20.6935],[-28.804,-20.694],[-28.7375,-20.671],[-28.6565,-20.595],[-28.629,-20.4995],[-28.6385,-20.4165],[-28.66,-20.371],[-28.741,-20.295],[-28.8105,-20.2705],[-28.881,-20.269],[-28.985,-20.3125],[-29.0445,-20.385],[-29.064,-20.466]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/05/Flag_of_Brazil.svg","name:en":"Brazil","wikidata":"Q155","ISO3166-1:alpha2":"BR","ISO3166-1:alpha3":"BRA","ISO3166-1:numeric":"076"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-81.6315,19.384],[-81.622,19.314],[-81.5735,19.1675],[-81.5105,19.1],[-81.4515,19.072],[-81.3905,19.062],[-81.227,19.0675],[-81.1565,19.093],[-81.0955,19.0935],[-81.0145,19.1165],[-80.962,19.148],[-80.913,19.2],[-80.8805,19.2715],[-80.8755,19.3655],[-80.8945,19.428],[-80.967,19.512],[-81.04,19.5465],[-81.176,19.552],[-81.3635,19.5965],[-81.449,19.589],[-81.5615,19.5305],[-81.602,19.4805],[-81.6315,19.384]]],[[[-80.3225,19.656],[-80.31,19.592],[-80.2795,19.5385],[-80.196,19.4755],[-80.1525,19.462],[-80.0655,19.4585],[-79.9545,19.491],[-79.896,19.482],[-79.804,19.4935],[-79.6425,19.5505],[-79.571,19.6065],[-79.523,19.683],[-79.511,19.7425],[-79.521,19.813],[-79.574,19.895],[-79.61,19.922],[-79.7,19.9545],[-79.81,19.948],[-79.9125,19.905],[-80.0165,19.9105],[-80.1555,19.8755],[-80.217,19.84],[-80.2765,19.787],[-80.312,19.724],[-80.3225,19.656]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/0f/Flag_of_the_Cayman_Islands.svg","name:en":"Cayman Islands","wikidata":"Q5785","ISO3166-1:alpha2":"KY","ISO3166-1:alpha3":"CYM","ISO3166-1:numeric":"136"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-178.3875,-14.257],[-178.3725,-14.3375],[-178.3445,-14.394],[-178.301,-14.4465],[-178.166,-14.5275],[-178.094,-14.5565],[-178.043,-14.563],[-177.953,-14.5435],[-177.842,-14.464],[-177.8035,-14.3925],[-177.799,-14.2975],[-177.8195,-14.2415],[-177.883,-14.171],[-178.058,-14.069],[-178.1515,-14.0415],[-178.2625,-14.058],[-178.3265,-14.1025],[-178.3795,-14.195],[-178.3875,-14.257]]],[[[-176.4555,-13.293],[-176.4435,-13.374],[-176.397,-13.482],[-176.3405,-13.549],[-176.257,-13.587],[-176.141,-13.5875],[-176.049,-13.551],[-176.0005,-13.5025],[-175.9235,-13.3305],[-175.92,-13.271],[-175.961,-13.1565],[-176.0335,-13.0605],[-176.128,-12.9955],[-176.247,-12.987],[-176.3245,-13.021],[-176.39,-13.098],[-176.444,-13.2235],[-176.4555,-13.293]]],[[[-154.936,-21.8145],[-154.919,-21.892],[-154.8755,-21.953],[-154.7765,-22.0095],[-154.7215,-22.022],[-154.6345,-22.0145],[-154.5765,-21.988],[-154.5215,-21.937],[-154.4815,-21.847],[-154.483,-21.761],[-154.502,-21.712],[-154.5455,-21.6565],[-154.6435,-21.603],[-154.714,-21.596],[-154.79,-21.614],[-154.8805,-21.6795],[-154.919,-21.7355],[-154.936,-21.8145]]],[[[-154.935,-16.521],[-154.924,-16.634],[-154.8585,-16.735],[-154.753,-16.7865],[-154.6335,-16.7895],[-154.5335,-16.762],[-154.4545,-16.69],[-154.4195,-16.6235],[-154.41,-16.542],[-154.428,-16.471],[-154.47,-16.395],[-154.5295,-16.337],[-154.5945,-16.3015],[-154.672,-16.281],[-154.744,-16.2875],[-154.8545,-16.3495],[-154.915,-16.433],[-154.935,-16.521]]],[[[-154.7515,-15.816],[-154.7395,-15.889],[-154.6965,-15.9595],[-154.616,-16.014],[-154.5255,-16.033],[-154.432,-16.0135],[-154.3675,-15.969],[-154.33,-15.9215],[-154.3045,-15.861],[-154.305,-15.761],[-154.3405,-15.689],[-154.411,-15.624],[-154.4825,-15.5995],[-154.591,-15.607],[-154.6615,-15.644],[-154.7295,-15.7265],[-154.7515,-15.816]]],[[[-154.201,-16.804],[-154.1915,-16.8695],[-154.166,-16.92],[-154.1085,-16.992],[-154.0635,-17.025],[-153.959,-17.0515],[-153.8565,-17.032],[-153.794,-16.999],[-153.7305,-16.9195],[-153.712,-16.859],[-153.7125,-16.7935],[-153.7535,-16.6885],[-153.826,-16.609],[-153.9105,-16.574],[-154.019,-16.579],[-154.0785,-16.6045],[-154.1375,-16.658],[-154.191,-16.7455],[-154.201,-16.804]]],[[[-153.0385,-22.645],[-153.0295,-22.7085],[-152.995,-22.774],[-152.955,-22.8125],[-152.8845,-22.852],[-152.7815,-22.8645],[-152.695,-22.839],[-152.6105,-22.765],[-152.574,-22.6575],[-152.581,-22.594],[-152.6185,-22.52],[-152.6655,-22.476],[-152.7675,-22.4375],[-152.828,-22.436],[-152.9135,-22.4595],[-152.999,-22.5305],[-153.0335,-22.6035],[-153.0385,-22.645]]],[[[-152.5045,-16.4505],[-152.498,-16.508],[-152.463,-16.581],[-152.4275,-16.6175],[-152.3535,-16.662],[-152.261,-16.686],[-152.2055,-16.684],[-152.1285,-16.655],[-152.066,-16.596],[-152.0355,-16.5305],[-152.0225,-16.427],[-152.041,-16.352],[-152.109,-16.267],[-152.1765,-16.228],[-152.226,-16.216],[-152.3035,-16.22],[-152.3705,-16.24],[-152.434,-16.282],[-152.489,-16.37],[-152.5045,-16.4505]]],[[[-152.0475,-16.247],[-152.0205,-16.3615],[-151.988,-16.419],[-151.9955,-16.581],[-151.968,-16.6515],[-151.926,-16.7],[-151.8145,-16.7515],[-151.7505,-16.7575],[-151.712,-16.801],[-151.6955,-16.944],[-151.68,-16.9915],[-151.6255,-17.068],[-151.525,-17.1235],[-151.4635,-17.133],[-151.38,-17.1235],[-151.318,-17.099],[-151.2065,-17.027],[-151.1495,-16.9505],[-151.0755,-17.0025],[-150.9675,-17.0215],[-150.8845,-17.0015],[-150.802,-16.9345],[-150.7425,-16.8165],[-150.7385,-16.753],[-150.754,-16.6945],[-150.809,-16.582],[-150.8645,-16.5265],[-150.918,-16.497],[-151.0195,-16.483],[-151.1215,-16.508],[-151.206,-16.5795],[-151.2315,-16.505],[-151.3095,-16.4135],[-151.374,-16.375],[-151.428,-16.3605],[-151.5275,-16.3535],[-151.5815,-16.3065],[-151.5745,-16.2625],[-151.5865,-16.1875],[-151.6225,-16.126],[-151.718,-16.054],[-151.779,-16.0345],[-151.885,-16.0365],[-151.944,-16.0625],[-152.0255,-16.153],[-152.0475,-16.247]]],[[[-151.5925,-22.453],[-151.574,-22.537],[-151.52,-22.6435],[-151.4715,-22.6875],[-151.396,-22.7195],[-151.301,-22.7215],[-151.2225,-22.692],[-151.166,-22.644],[-151.111,-22.534],[-151.112,-22.415],[-151.1595,-22.325],[-151.232,-22.27],[-151.282,-22.2455],[-151.3655,-22.2305],[-151.466,-22.2525],[-151.548,-22.3185],[-151.5765,-22.367],[-151.5925,-22.453]]],[[[-150.861,-17.66],[-150.845,-17.7415],[-150.814,-17.792],[-150.738,-17.8515],[-150.6805,-17.8745],[-150.596,-17.879],[-150.5335,-17.861],[-150.468,-17.8185],[-150.423,-17.7605],[-150.4,-17.666],[-150.4145,-17.585],[-150.4835,-17.489],[-150.5285,-17.4575],[-150.615,-17.433],[-150.672,-17.436],[-150.7425,-17.462],[-150.7955,-17.506],[-150.852,-17.6045],[-150.861,-17.66]]],[[[-150.1295,-17.4905],[-150.1135,-17.5765],[-150.078,-17.6475],[-149.9865,-17.7385],[-149.8875,-17.787],[-149.781,-17.791],[-149.7205,-17.888],[-149.649,-17.932],[-149.536,-17.9705],[-149.429,-17.9795],[-149.328,-18.048],[-149.2395,-18.073],[-149.148,-18.0805],[-149.091,-18.0695],[-149.002,-18.0185],[-148.9405,-17.9325],[-148.915,-17.809],[-148.929,-17.718],[-148.961,-17.6595],[-149.0245,-17.591],[-149.098,-17.553],[-149.1175,-17.4975],[-149.1845,-17.408],[-149.244,-17.366],[-149.3635,-17.3165],[-149.462,-17.296],[-149.531,-17.2965],[-149.623,-17.3325],[-149.698,-17.2865],[-149.785,-17.273],[-149.935,-17.2865],[-150.0,-17.303],[-150.0755,-17.355],[-150.107,-17.399],[-150.1295,-17.4905]]],[[[-149.805,-17.018],[-149.7955,-17.083],[-149.7535,-17.1555],[-149.6875,-17.207],[-149.6225,-17.2365],[-149.5365,-17.25],[-149.464,-17.2345],[-149.3855,-17.1795],[-149.3495,-17.1215],[-149.3275,-17.031],[-149.331,-16.957],[-149.3525,-16.898],[-149.435,-16.8165],[-149.5105,-16.7895],[-149.628,-16.79],[-149.7045,-16.8255],[-149.746,-16.864],[-149.794,-16.9565],[-149.805,-17.018]]],[[[-149.748,-23.368],[-149.739,-23.427],[-149.702,-23.499],[-149.652,-23.5525],[-149.558,-23.598],[-149.5015,-23.6035],[-149.4,-23.588],[-149.2795,-23.5405],[-149.214,-23.482],[-149.185,-23.4245],[-149.182,-23.321],[-149.2025,-23.2715],[-149.264,-23.194],[-149.3575,-23.145],[-149.45,-23.127],[-149.5675,-23.1475],[-149.6385,-23.19],[-149.714,-23.262],[-149.748,-23.368]]],[[[-148.924,-14.8775],[-148.914,-14.945],[-148.8715,-15.0245],[-148.803,-15.077],[-148.6855,-15.111],[-148.6115,-15.1055],[-148.497,-15.055],[-148.466,-15.1575],[-148.4305,-15.2175],[-148.357,-15.289],[-148.294,-15.318],[-148.241,-15.327],[-148.163,-15.3165],[-148.0815,-15.2825],[-147.9785,-15.366],[-147.871,-15.4015],[-147.8245,-15.4275],[-147.7415,-15.4465],[-147.664,-15.44],[-147.5715,-15.498],[-147.4845,-15.5175],[-147.283,-15.522],[-147.135,-15.4685],[-147.065,-15.423],[-147.0145,-15.5025],[-147.0525,-15.5425],[-147.088,-15.615],[-147.0955,-15.701],[-147.0625,-15.8025],[-147.017,-15.8645],[-146.959,-15.9165],[-146.8705,-15.9675],[-146.77,-16.012],[-146.5835,-16.054],[-146.6055,-16.1415],[-146.5915,-16.221],[-146.5435,-16.3015],[-146.497,-16.347],[-146.4115,-16.3855],[-146.269,-16.38],[-146.196,-16.347],[-146.15,-16.303],[-146.1075,-16.209],[-146.021,-16.2385],[-145.9465,-16.3945],[-145.802,-16.5605],[-145.6265,-16.71],[-145.5655,-16.7385],[-145.527,-16.853],[-145.4345,-16.9445],[-145.2765,-17.0115],[-145.2275,-17.018],[-145.1155,-17.011],[-144.993,-17.0815],[-144.906,-17.104],[-144.8185,-17.155],[-144.707,-17.1925],[-144.6205,-17.197],[-144.5645,-17.2875],[-144.457,-17.3575],[-144.3665,-17.3785],[-144.3065,-17.3745],[-144.2365,-17.3495],[-144.188,-17.313],[-144.1505,-17.2605],[-144.109,-17.1655],[-144.0935,-17.0985],[-144.1,-17.021],[-144.1215,-16.9675],[-143.9865,-16.93],[-143.923,-16.884],[-143.8735,-16.8025],[-143.76,-16.858],[-143.5395,-16.9475],[-143.518,-17.0435],[-143.4575,-17.146],[-143.3925,-17.207],[-143.3125,-17.2555],[-143.2855,-17.302],[-143.2245,-17.357],[-143.1585,-17.394],[-143.0605,-17.413],[-142.9455,-17.3855],[-142.8745,-17.3305],[-142.8435,-17.283],[-142.822,-17.2015],[-142.8285,-17.134],[-142.8555,-17.0675],[-142.8515,-16.959],[-142.7055,-16.9255],[-142.6585,-16.899],[-142.606,-16.8445],[-142.569,-16.746],[-142.5795,-16.653],[-142.653,-16.517],[-142.738,-16.4535],[-142.8035,-16.4315],[-142.802,-16.3585],[-142.823,-16.294],[-142.8605,-16.2425],[-142.9545,-16.173],[-143.1145,-16.0895],[-143.211,-16.0775],[-143.3085,-16.0985],[-143.361,-16.1295],[-143.4145,-16.192],[-143.4355,-16.2465],[-143.4355,-16.33],[-143.405,-16.41],[-143.4625,-16.4045],[-143.5425,-16.416],[-143.6565,-16.3645],[-143.722,-16.296],[-143.7815,-16.2645],[-143.9105,-16.236],[-144.0365,-16.254],[-144.1155,-16.305],[-144.2075,-16.2145],[-144.3195,-16.154],[-144.3725,-16.1405],[-144.457,-16.1425],[-144.568,-16.196],[-144.5845,-16.103],[-144.669,-15.9605],[-144.5785,-15.9585],[-144.4685,-15.9035],[-144.4195,-15.839],[-144.4015,-15.779],[-144.406,-15.7025],[-144.446,-15.6215],[-144.4785,-15.5865],[-144.557,-15.5365],[-144.615,-15.5215],[-144.728,-15.5355],[-144.786,-15.5715],[-144.8335,-15.6315],[-144.859,-15.712],[-144.858,-15.8415],[-144.9265,-15.6865],[-144.966,-15.639],[-145.046,-15.593],[-145.125,-15.575],[-145.2435,-15.571],[-145.2295,-15.507],[-145.2435,-15.422],[-145.304,-15.336],[-145.3875,-15.2925],[-145.5435,-15.2555],[-145.607,-15.261],[-145.6895,-15.2995],[-145.755,-15.3715],[-145.7865,-15.44],[-145.798,-15.5285],[-145.772,-15.649],[-145.739,-15.7],[-145.667,-15.7705],[-145.5815,-15.817],[-145.466,-15.8255],[-145.4575,-15.9255],[-145.521,-15.8745],[-145.564,-15.857],[-145.6845,-15.852],[-145.737,-15.7635],[-145.782,-15.715],[-145.932,-15.6225],[-146.03,-15.599],[-146.0155,-15.4935],[-145.9915,-15.427],[-145.98,-15.3305],[-145.987,-15.2845],[-146.0215,-15.2165],[-146.072,-15.17],[-146.146,-15.1385],[-146.2565,-15.1135],[-146.393,-15.0985],[-146.461,-15.1105],[-146.5745,-15.045],[-146.7585,-15.0015],[-146.834,-14.9975],[-146.9315,-15.0355],[-147.0485,-15.151],[-147.0825,-15.0965],[-147.176,-15.0345],[-147.255,-15.0235],[-147.3725,-14.931],[-147.4335,-14.861],[-147.519,-14.7955],[-147.65,-14.744],[-147.845,-14.716],[-147.9265,-14.731],[-147.9745,-14.757],[-148.034,-14.7295],[-148.124,-14.705],[-148.265,-14.72],[-148.3185,-14.746],[-148.424,-14.835],[-148.4555,-14.7655],[-148.5145,-14.7075],[-148.63,-14.6635],[-148.7015,-14.6555],[-148.7665,-14.666],[-148.8395,-14.7055],[-148.9085,-14.798],[-148.924,-14.8775]],[[-145.4135,-16.013],[-145.2825,-16.128],[-145.218,-16.1615],[-145.1895,-16.2615],[-145.105,-16.3775],[-145.0605,-16.4135],[-144.9645,-16.4645],[-144.9055,-16.481],[-144.8285,-16.484],[-144.7745,-16.472],[-144.6965,-16.428],[-144.6675,-16.431],[-144.647,-16.4915],[-144.603,-16.5585],[-144.537,-16.6225],[-144.454,-16.675],[-144.4675,-16.7115],[-144.5785,-16.6685],[-144.652,-16.606],[-144.7455,-16.5745],[-144.8225,-16.573],[-144.8975,-16.588],[-144.9865,-16.586],[-145.0665,-16.537],[-145.1605,-16.509],[-145.1525,-16.4515],[-145.168,-16.3735],[-145.2435,-16.266],[-145.3115,-16.2115],[-145.361,-16.144],[-145.4135,-16.013]],[[-143.18,-16.6095],[-143.082,-16.613],[-143.0845,-16.7455],[-143.1785,-16.725],[-143.18,-16.6095]]],[[[-148.491,-15.844],[-148.4845,-15.898],[-148.4565,-15.96],[-148.374,-16.0375],[-148.3165,-16.0635],[-148.2275,-16.0725],[-148.1285,-16.04],[-148.068,-15.991],[-148.0385,-15.9475],[-148.0145,-15.876],[-148.016,-15.8075],[-148.042,-15.7425],[-148.1235,-15.6555],[-148.1865,-15.6185],[-148.2555,-15.6055],[-148.3085,-15.6095],[-148.3835,-15.639],[-148.428,-15.6755],[-148.4715,-15.7465],[-148.491,-15.844]]],[[[-148.285,-17.877],[-148.273,-17.942],[-148.2085,-18.0355],[-148.1325,-18.076],[-148.025,-18.0805],[-147.975,-18.065],[-147.9125,-18.0225],[-147.8815,-17.9835],[-147.851,-17.8945],[-147.8545,-17.83],[-147.894,-17.748],[-147.9645,-17.691],[-148.025,-17.6705],[-148.098,-17.668],[-148.1835,-17.699],[-148.227,-17.7335],[-148.2715,-17.8015],[-148.285,-17.877]]],[[[-147.919,-23.8985],[-147.8995,-23.9835],[-147.8665,-24.031],[-147.811,-24.0735],[-147.7305,-24.1025],[-147.679,-24.107],[-147.586,-24.094],[-147.5115,-24.064],[-147.4435,-24.0195],[-147.3855,-23.942],[-147.3675,-23.8355],[-147.3875,-23.7555],[-147.4325,-23.696],[-147.4945,-23.655],[-147.5475,-23.6385],[-147.6265,-23.6335],[-147.762,-23.6655],[-147.867,-23.7285],[-147.9065,-23.7915],[-147.919,-23.8985]]],[[[-146.61,-14.552],[-146.605,-14.602],[-146.5565,-14.6965],[-146.5055,-14.738],[-146.4245,-14.766],[-146.3145,-14.754],[-146.2545,-14.721],[-146.195,-14.7065],[-146.114,-14.656],[-146.0565,-14.6675],[-145.958,-14.6585],[-145.8785,-14.6375],[-145.744,-14.563],[-145.662,-14.477],[-145.6385,-14.427],[-145.6285,-14.3645],[-145.645,-14.2855],[-145.6825,-14.219],[-145.7485,-14.159],[-145.8385,-14.129],[-145.9225,-14.1345],[-146.0155,-14.1655],[-146.1585,-14.2445],[-146.27,-14.225],[-146.37,-14.24],[-146.488,-14.2975],[-146.5565,-14.3805],[-146.5965,-14.483],[-146.61,-14.552]]],[[[-145.816,-17.3505],[-145.7945,-17.444],[-145.745,-17.5215],[-145.6485,-17.585],[-145.6045,-17.6375],[-145.559,-17.668],[-145.4575,-17.697],[-145.346,-17.6915],[-145.2685,-17.6545],[-145.224,-17.613],[-145.1855,-17.5335],[-145.1885,-17.4275],[-145.234,-17.3435],[-145.3595,-17.2055],[-145.4395,-17.1515],[-145.5375,-17.127],[-145.6285,-17.128],[-145.7365,-17.182],[-145.795,-17.2605],[-145.816,-17.3505]]],[[[-145.4695,-14.693],[-145.4455,-14.792],[-145.3755,-14.871],[-145.3165,-14.9025],[-145.21,-14.911],[-145.1465,-14.8895],[-145.0695,-14.829],[-144.9735,-14.7045],[-144.8375,-14.642],[-144.7065,-14.5455],[-144.6635,-14.481],[-144.649,-14.3895],[-144.668,-14.318],[-144.7215,-14.246],[-144.787,-14.2055],[-144.9135,-14.188],[-145.0555,-14.232],[-145.1795,-14.316],[-145.2135,-14.357],[-145.329,-14.413],[-145.388,-14.474],[-145.4335,-14.55],[-145.4695,-14.693]]],[[[-145.2135,-19.867],[-145.1975,-19.9455],[-145.163,-20.0],[-145.072,-20.0775],[-144.971,-20.1145],[-144.896,-20.109],[-144.809,-20.07],[-144.766,-20.0275],[-144.736,-19.972],[-144.725,-19.865],[-144.752,-19.7595],[-144.792,-19.703],[-144.88,-19.6485],[-144.939,-19.6355],[-145.034,-19.644],[-145.108,-19.6835],[-145.174,-19.751],[-145.208,-19.8235],[-145.2135,-19.867]]],[[[-144.7705,-14.96],[-144.7585,-15.028],[-144.7125,-15.099],[-144.6575,-15.138],[-144.56,-15.16],[-144.493,-15.152],[-144.4085,-15.1105],[-144.341,-15.028],[-144.3245,-14.935],[-144.3415,-14.8665],[-144.4165,-14.78],[-144.465,-14.756],[-144.5385,-14.7425],[-144.6455,-14.7665],[-144.708,-14.8125],[-144.759,-14.893],[-144.7705,-14.96]]],[[[-144.6065,-27.596],[-144.588,-27.6895],[-144.542,-27.759],[-144.423,-27.8345],[-144.3295,-27.8525],[-144.2315,-27.835],[-144.1605,-27.7985],[-144.1025,-27.732],[-144.0765,-27.6305],[-144.082,-27.5625],[-144.112,-27.4875],[-144.17,-27.4235],[-144.2885,-27.3715],[-144.4255,-27.3765],[-144.5095,-27.415],[-144.587,-27.5015],[-144.6065,-27.596]]],[[[-143.77,-20.4405],[-143.7405,-20.5435],[-143.6905,-20.5985],[-143.5745,-20.6555],[-143.5015,-20.6575],[-143.4795,-20.7165],[-143.4205,-20.7855],[-143.315,-20.828],[-143.2305,-20.823],[-143.1285,-20.898],[-143.0545,-20.9145],[-142.9595,-20.898],[-142.866,-20.822],[-142.8365,-20.75],[-142.8335,-20.698],[-142.8715,-20.586],[-142.929,-20.5285],[-142.989,-20.4995],[-143.1025,-20.4945],[-143.1495,-20.4455],[-143.2165,-20.4115],[-143.312,-20.404],[-143.3435,-20.3205],[-143.3875,-20.271],[-143.466,-20.2225],[-143.5765,-20.2145],[-143.6755,-20.2625],[-143.736,-20.3285],[-143.77,-20.4405]]],[[[-143.7365,-27.8855],[-143.7225,-27.9635],[-143.6485,-28.0525],[-143.559,-28.09],[-143.4425,-28.095],[-143.3625,-28.0645],[-143.293,-27.9985],[-143.2675,-27.939],[-143.2585,-27.8695],[-143.2725,-27.8095],[-143.313,-27.748],[-143.354,-27.7155],[-143.4355,-27.6815],[-143.5205,-27.675],[-143.5745,-27.685],[-143.646,-27.72],[-143.705,-27.7865],[-143.7365,-27.8855]]],[[[-143.6895,-17.4455],[-143.678,-17.5155],[-143.6305,-17.6055],[-143.5895,-17.645],[-143.479,-17.689],[-143.401,-17.6845],[-143.312,-17.646],[-143.2615,-17.6045],[-143.229,-17.5575],[-143.209,-17.4905],[-143.219,-17.4],[-143.2485,-17.3455],[-143.297,-17.2975],[-143.409,-17.25],[-143.5265,-17.2505],[-143.591,-17.2765],[-143.667,-17.358],[-143.6895,-17.4455]]],[[[-143.2985,-17.865],[-143.288,-17.9255],[-143.251,-17.993],[-143.192,-18.0465],[-143.1035,-18.0785],[-143.0565,-18.0805],[-142.9745,-18.059],[-142.9285,-18.0305],[-142.88,-17.9745],[-142.852,-17.8685],[-142.8775,-17.755],[-142.9185,-17.6955],[-142.9905,-17.6485],[-143.0755,-17.6335],[-143.1545,-17.65],[-143.236,-17.7045],[-143.29,-17.797],[-143.2985,-17.865]]],[[[-142.8805,-17.553],[-142.873,-17.634],[-142.8155,-17.7425],[-142.718,-17.812],[-142.5965,-17.837],[-142.5125,-17.8245],[-142.4335,-17.785],[-142.3585,-17.6995],[-142.3385,-17.635],[-142.3525,-17.524],[-142.4005,-17.455],[-142.361,-17.392],[-142.347,-17.312],[-142.359,-17.2505],[-142.4255,-17.1565],[-142.5225,-17.1085],[-142.598,-17.101],[-142.6815,-17.1235],[-142.733,-17.1585],[-142.784,-17.233],[-142.7975,-17.3095],[-142.784,-17.3755],[-142.8365,-17.423],[-142.8635,-17.469],[-142.8805,-17.553]]],[[[-142.7395,-16.164],[-142.698,-16.3055],[-142.658,-16.3725],[-142.617,-16.4085],[-142.508,-16.447],[-142.413,-16.439],[-142.327,-16.3975],[-142.283,-16.353],[-142.1835,-16.195],[-142.1135,-16.0275],[-141.9685,-15.8725],[-141.921,-15.7785],[-141.925,-15.6735],[-141.9765,-15.5905],[-142.049,-15.5445],[-142.112,-15.5305],[-142.2015,-15.5395],[-142.294,-15.5845],[-142.398,-15.6785],[-142.4515,-15.753],[-142.523,-15.797],[-142.64,-15.914],[-142.727,-16.0905],[-142.7395,-16.164]]],[[[-142.605,-18.0455],[-142.5875,-18.129],[-142.509,-18.2305],[-142.393,-18.302],[-142.3615,-18.4155],[-142.3035,-18.4805],[-142.2125,-18.519],[-142.143,-18.5195],[-142.0665,-18.492],[-141.9875,-18.4355],[-141.9275,-18.3685],[-141.902,-18.292],[-141.919,-18.18],[-141.981,-18.077],[-141.9775,-18.0115],[-142.0035,-17.884],[-142.074,-17.795],[-142.146,-17.7615],[-142.2685,-17.7635],[-142.3595,-17.8075],[-142.445,-17.818],[-142.533,-17.8685],[-142.5885,-17.957],[-142.605,-18.0455]]],[[[-142.1415,-16.8295],[-142.1195,-16.9235],[-142.07,-16.989],[-142.0085,-17.027],[-141.8985,-17.0465],[-141.827,-17.03],[-141.7435,-16.967],[-141.71,-16.9055],[-141.7005,-16.837],[-141.7205,-16.757],[-141.7525,-16.706],[-141.8065,-16.657],[-141.92,-16.6235],[-142.03,-16.65],[-142.0785,-16.684],[-142.1285,-16.7585],[-142.1415,-16.8295]]],[[[-142.077,-18.7225],[-142.0685,-18.783],[-142.038,-18.849],[-141.9625,-18.9465],[-141.8885,-18.994],[-141.7765,-19.007],[-141.707,-18.989],[-141.6265,-18.938],[-141.583,-18.8825],[-141.545,-18.7655],[-141.5575,-18.6875],[-141.586,-18.6355],[-141.677,-18.553],[-141.766,-18.516],[-141.8565,-18.505],[-141.9175,-18.5135],[-142.007,-18.562],[-142.0445,-18.606],[-142.077,-18.7225]]],[[[-141.7375,-17.377],[-141.6995,-17.4945],[-141.6345,-17.5635],[-141.5655,-17.5945],[-141.4715,-17.5975],[-141.367,-17.556],[-141.3265,-17.5205],[-141.2795,-17.425],[-141.2825,-17.334],[-141.3055,-17.2785],[-141.3875,-17.192],[-141.468,-17.163],[-141.57,-17.1685],[-141.64,-17.1995],[-141.6815,-17.2345],[-141.7285,-17.3135],[-141.7375,-17.377]]],[[[-141.643,-14.105],[-141.6085,-14.211],[-141.552,-14.269],[-141.509,-14.293],[-141.424,-14.3115],[-141.3325,-14.377],[-141.275,-14.393],[-141.1845,-14.4],[-141.1125,-14.3885],[-141.016,-14.323],[-140.973,-14.239],[-140.968,-14.1745],[-140.988,-14.1035],[-141.046,-14.029],[-141.1165,-13.9785],[-141.217,-13.948],[-141.2855,-13.95],[-141.3355,-13.9145],[-141.4395,-13.8905],[-141.5045,-13.9025],[-141.575,-13.944],[-141.611,-13.9855],[-141.6375,-14.049],[-141.643,-14.105]]],[[[-141.4695,-19.1895],[-141.459,-19.2585],[-141.4345,-19.3125],[-141.3865,-19.37],[-141.3225,-19.4085],[-141.258,-19.423],[-141.1615,-19.4105],[-141.074,-19.353],[-141.0305,-19.2835],[-141.018,-19.2225],[-141.0305,-19.138],[-141.0855,-19.052],[-141.189,-18.988],[-141.2735,-18.98],[-141.336,-18.995],[-141.409,-19.043],[-141.4575,-19.1215],[-141.4695,-19.1895]]],[[[-141.2795,-18.1165],[-141.2605,-18.211],[-141.2275,-18.271],[-141.182,-18.3235],[-141.0895,-18.398],[-141.0685,-18.4315],[-140.962,-18.5455],[-140.8765,-18.593],[-140.792,-18.618],[-140.676,-18.631],[-140.5715,-18.61],[-140.527,-18.581],[-140.4755,-18.5155],[-140.455,-18.4225],[-140.476,-18.3395],[-140.5455,-18.249],[-140.6065,-18.2065],[-140.625,-18.1295],[-140.6745,-18.0565],[-140.6405,-18.0335],[-140.504,-17.8985],[-140.445,-17.7805],[-140.44,-17.6975],[-140.478,-17.6045],[-140.5295,-17.554],[-140.6095,-17.5155],[-140.696,-17.506],[-140.7785,-17.524],[-140.9235,-17.6145],[-140.965,-17.6495],[-141.048,-17.7675],[-141.081,-17.8715],[-141.1425,-17.889],[-141.231,-17.959],[-141.272,-18.0495],[-141.2795,-18.1165]]],[[[-141.1095,-15.822],[-141.098,-15.8915],[-141.048,-15.971],[-140.98,-16.018],[-140.864,-16.042],[-140.785,-16.027],[-140.6885,-15.9675],[-140.6295,-15.862],[-140.6245,-15.7865],[-140.654,-15.7025],[-140.7305,-15.63],[-140.8175,-15.6025],[-140.8855,-15.606],[-140.9755,-15.634],[-141.039,-15.6715],[-141.0995,-15.7635],[-141.1095,-15.822]]],[[[-140.9285,-19.125],[-140.9145,-19.2015],[-140.8635,-19.2795],[-140.796,-19.3295],[-140.709,-19.3565],[-140.654,-19.356],[-140.5775,-19.3325],[-140.5315,-19.3015],[-140.4745,-19.23],[-140.4525,-19.161],[-140.4575,-19.0815],[-140.4795,-19.029],[-140.544,-18.9605],[-140.587,-18.938],[-140.677,-18.9175],[-140.769,-18.922],[-140.809,-18.9365],[-140.877,-18.988],[-140.9115,-19.0435],[-140.9285,-19.125]]],[[[-140.9255,-8.0145],[-140.907,-8.1145],[-140.875,-8.1695],[-140.7855,-8.234],[-140.676,-8.2495],[-140.589,-8.219],[-140.545,-8.182],[-140.495,-8.1145],[-140.418,-8.06],[-140.345,-8.0615],[-140.2635,-8.0295],[-140.1985,-7.957],[-140.1765,-7.887],[-140.18,-7.8175],[-140.2225,-7.7315],[-140.2825,-7.683],[-140.332,-7.6645],[-140.4025,-7.6605],[-140.5045,-7.704],[-140.584,-7.6985],[-140.638,-7.713],[-140.7125,-7.761],[-140.831,-7.815],[-140.902,-7.9095],[-140.9255,-8.0145]]],[[[-140.893,-21.684],[-140.8825,-21.7495],[-140.8455,-21.8185],[-140.755,-21.889],[-140.6535,-21.925],[-140.5825,-21.9255],[-140.481,-21.8935],[-140.4215,-21.85],[-140.375,-21.7785],[-140.362,-21.705],[-140.3705,-21.652],[-140.4435,-21.5115],[-140.488,-21.4675],[-140.5625,-21.4325],[-140.631,-21.424],[-140.7375,-21.4465],[-140.7835,-21.477],[-140.871,-21.5945],[-140.893,-21.684]]],[[[-140.8225,-8.676],[-140.8075,-8.7525],[-140.776,-8.8055],[-140.706,-8.8595],[-140.6635,-8.874],[-140.5545,-8.8725],[-140.4995,-8.849],[-140.4535,-8.8105],[-140.4505,-8.871],[-140.405,-9.027],[-140.3685,-9.076],[-140.303,-9.131],[-140.209,-9.176],[-140.2845,-9.2445],[-140.329,-9.32],[-140.3345,-9.418],[-140.3125,-9.4905],[-140.234,-9.5915],[-140.192,-9.6265],[-140.124,-9.658],[-140.0615,-9.668],[-139.972,-9.651],[-139.9135,-9.6125],[-139.8725,-9.5655],[-139.8395,-9.493],[-139.819,-9.3895],[-139.8205,-9.3295],[-139.8395,-9.272],[-139.9075,-9.196],[-140.0265,-9.1385],[-139.9195,-9.1045],[-139.8605,-9.058],[-139.8155,-8.973],[-139.775,-9.0565],[-139.7215,-9.117],[-139.6105,-9.1575],[-139.4585,-9.1405],[-139.366,-9.1005],[-139.2985,-9.008],[-139.2825,-8.9365],[-139.2985,-8.8375],[-139.339,-8.766],[-139.411,-8.704],[-139.463,-8.68],[-139.546,-8.6645],[-139.6135,-8.67],[-139.6905,-8.697],[-139.7435,-8.742],[-139.7935,-8.8195],[-139.848,-8.6755],[-139.908,-8.6225],[-139.9765,-8.596],[-140.087,-8.5975],[-140.149,-8.5815],[-140.2685,-8.592],[-140.3385,-8.6185],[-140.4035,-8.672],[-140.434,-8.5715],[-140.517,-8.4965],[-140.601,-8.474],[-140.6645,-8.478],[-140.717,-8.4985],[-140.7865,-8.562],[-140.8095,-8.605],[-140.8225,-8.676]]],[[[-140.645,-19.634],[-140.6255,-19.7245],[-140.5915,-19.7785],[-140.5085,-19.84],[-140.4335,-19.861],[-140.349,-19.856],[-140.2945,-19.836],[-140.2225,-19.7775],[-140.178,-19.691],[-140.172,-19.6335],[-140.192,-19.553],[-140.263,-19.465],[-140.319,-19.429],[-140.4175,-19.4085],[-140.529,-19.4385],[-140.5995,-19.504],[-140.6315,-19.564],[-140.645,-19.634]]],[[[-140.377,-15.985],[-140.3475,-16.096],[-140.319,-16.134],[-140.2505,-16.1845],[-140.16,-16.209],[-140.099,-16.207],[-140.0105,-16.177],[-139.956,-16.136],[-139.913,-16.082],[-139.8855,-15.9965],[-139.9,-15.8975],[-139.975,-15.805],[-140.0695,-15.768],[-140.1515,-15.765],[-140.211,-15.7765],[-140.317,-15.8375],[-140.3665,-15.919],[-140.377,-15.985]]],[[[-139.4675,-19.3555],[-139.4345,-19.4625],[-139.396,-19.5055],[-139.316,-19.551],[-139.256,-19.566],[-139.18,-19.567],[-139.101,-19.538],[-139.02,-19.4565],[-138.991,-19.38],[-138.947,-19.433],[-138.8765,-19.4735],[-138.8305,-19.542],[-138.761,-19.588],[-138.704,-19.603],[-138.5965,-19.5915],[-138.532,-19.554],[-138.4975,-19.5175],[-138.4665,-19.4535],[-138.4595,-19.386],[-138.4715,-19.328],[-138.512,-19.2595],[-138.5635,-19.217],[-138.6025,-19.1465],[-138.6735,-19.0935],[-138.7245,-19.077],[-138.837,-19.078],[-138.942,-19.125],[-139.0285,-19.2185],[-139.0935,-19.16],[-139.154,-19.135],[-139.2465,-19.13],[-139.3095,-19.1495],[-139.412,-19.217],[-139.4485,-19.27],[-139.4675,-19.3555]]],[[[-139.4275,-18.557],[-139.4195,-18.6145],[-139.364,-18.7035],[-139.2965,-18.746],[-139.2245,-18.7615],[-139.1635,-18.7565],[-139.054,-18.7055],[-139.069,-18.793],[-139.06,-18.841],[-139.027,-18.9035],[-138.973,-18.9525],[-138.9275,-18.9745],[-138.8425,-18.9885],[-138.7285,-18.972],[-138.6555,-18.932],[-138.6105,-18.883],[-138.579,-18.7995],[-138.5825,-18.7235],[-138.6005,-18.677],[-138.6445,-18.6205],[-138.7385,-18.5695],[-138.8335,-18.562],[-138.889,-18.572],[-138.998,-18.628],[-139.0015,-18.506],[-139.041,-18.431],[-139.125,-18.3705],[-139.19,-18.3545],[-139.2575,-18.3585],[-139.3495,-18.4005],[-139.3995,-18.4565],[-139.4275,-18.557]]],[[[-139.366,-9.781],[-139.3515,-9.857],[-139.324,-9.908],[-139.3315,-10.0275],[-139.312,-10.091],[-139.2775,-10.1485],[-139.211,-10.204],[-139.1365,-10.2265],[-139.037,-10.212],[-138.962,-10.1585],[-138.903,-10.197],[-138.836,-10.221],[-138.7625,-10.222],[-138.7115,-10.206],[-138.6465,-10.1585],[-138.612,-10.1055],[-138.5945,-10.0205],[-138.6055,-9.958],[-138.6385,-9.8765],[-138.6095,-9.8185],[-138.5955,-9.749],[-138.6085,-9.674],[-138.669,-9.5895],[-138.7495,-9.548],[-138.7175,-9.453],[-138.7305,-9.367],[-138.759,-9.3155],[-138.8385,-9.251],[-138.919,-9.232],[-139.0215,-9.2555],[-139.0715,-9.2885],[-139.13,-9.3795],[-139.138,-9.4455],[-139.1205,-9.52],[-139.257,-9.5875],[-139.3435,-9.682],[-139.366,-9.781]]],[[[-139.3595,-20.7805],[-139.346,-20.857],[-139.279,-20.949],[-139.174,-20.9945],[-139.0805,-20.988],[-138.9875,-20.934],[-138.9485,-20.882],[-138.926,-20.82],[-138.925,-20.7375],[-138.974,-20.64],[-139.042,-20.5895],[-139.099,-20.5715],[-139.165,-20.569],[-139.257,-20.6],[-139.3345,-20.682],[-139.3595,-20.7805]]],[[[-139.249,-21.883],[-139.2265,-21.9735],[-139.1405,-22.06],[-139.0835,-22.082],[-139.01,-22.0925],[-138.9775,-22.1335],[-138.9985,-22.214],[-138.993,-22.27],[-138.933,-22.394],[-138.9,-22.431],[-138.833,-22.4705],[-138.77,-22.483],[-138.6845,-22.4705],[-138.563,-22.406],[-138.5335,-22.372],[-138.4995,-22.285],[-138.496,-22.2205],[-138.513,-22.156],[-138.5435,-22.1085],[-138.6175,-22.038],[-138.6745,-22.009],[-138.6065,-21.945],[-138.5765,-21.876],[-138.5795,-21.777],[-138.6215,-21.6995],[-138.699,-21.6355],[-138.853,-21.5815],[-138.9445,-21.582],[-139.0205,-21.6115],[-139.0885,-21.6715],[-139.183,-21.7335],[-139.2275,-21.7915],[-139.249,-21.883]]],[[[-139.055,-14.813],[-139.0485,-14.8655],[-139.004,-14.949],[-138.953,-14.992],[-138.864,-15.0335],[-138.7615,-15.039],[-138.6595,-14.994],[-138.615,-14.943],[-138.5825,-14.843],[-138.598,-14.752],[-138.638,-14.689],[-138.711,-14.6255],[-138.822,-14.596],[-138.942,-14.6305],[-139.028,-14.7165],[-139.055,-14.813]]],[[[-138.895,-10.521],[-138.873,-10.6125],[-138.815,-10.687],[-138.7145,-10.7465],[-138.622,-10.7515],[-138.5655,-10.7335],[-138.494,-10.6845],[-138.455,-10.6365],[-138.418,-10.5645],[-138.407,-10.51],[-138.4105,-10.4245],[-138.465,-10.319],[-138.536,-10.2595],[-138.593,-10.2305],[-138.6505,-10.2185],[-138.73,-10.225],[-138.823,-10.2725],[-138.872,-10.3365],[-138.887,-10.38],[-138.895,-10.521]]],[[[-138.7855,-20.8245],[-138.78,-20.883],[-138.7485,-20.9585],[-138.691,-21.018],[-138.6275,-21.061],[-138.529,-21.088],[-138.4505,-21.0775],[-138.3775,-21.0375],[-138.34,-20.9975],[-138.3055,-20.918],[-138.2945,-20.839],[-138.3095,-20.739],[-138.346,-20.6765],[-138.397,-20.632],[-138.5095,-20.5755],[-138.6045,-20.5725],[-138.672,-20.597],[-138.7365,-20.6545],[-138.762,-20.702],[-138.7855,-20.8245]]],[[[-138.6635,-17.3465],[-138.648,-17.432],[-138.61,-17.49],[-138.514,-17.549],[-138.4315,-17.56],[-138.343,-17.5565],[-138.2685,-17.5355],[-138.205,-17.497],[-138.1395,-17.4165],[-138.1235,-17.3665],[-138.123,-17.2995],[-138.149,-17.2295],[-138.2335,-17.151],[-138.3355,-17.126],[-138.441,-17.13],[-138.5195,-17.1515],[-138.6,-17.204],[-138.643,-17.263],[-138.6635,-17.3465]]],[[[-137.359,-23.167],[-137.3365,-23.261],[-137.3005,-23.3125],[-137.208,-23.3715],[-137.1505,-23.383],[-137.0555,-23.37],[-136.9865,-23.329],[-136.9265,-23.2445],[-136.905,-23.176],[-136.9215,-23.0645],[-136.9735,-22.991],[-137.034,-22.95],[-137.1545,-22.933],[-137.225,-22.9555],[-137.294,-23.0055],[-137.335,-23.0635],[-137.359,-23.167]]],[[[-137.28,-18.264],[-137.259,-18.3545],[-137.2105,-18.436],[-137.1295,-18.5125],[-137.025,-18.5585],[-136.9025,-18.555],[-136.848,-18.529],[-136.789,-18.471],[-136.7555,-18.383],[-136.7605,-18.3075],[-136.808,-18.2155],[-136.895,-18.13],[-136.9505,-18.093],[-137.019,-18.066],[-137.1005,-18.0635],[-137.1765,-18.0905],[-137.234,-18.1375],[-137.273,-18.211],[-137.28,-18.264]]],[[[-136.9665,-21.297],[-136.9545,-21.37],[-136.9165,-21.4355],[-136.8635,-21.4825],[-136.7295,-21.5345],[-136.609,-21.554],[-136.5505,-21.6345],[-136.4665,-21.686],[-136.397,-21.7],[-136.291,-21.681],[-136.2165,-21.6275],[-136.18,-21.573],[-136.161,-21.4705],[-136.1845,-21.385],[-136.23,-21.315],[-136.3305,-21.255],[-136.3895,-21.176],[-136.447,-21.142],[-136.568,-21.131],[-136.711,-21.0955],[-136.754,-21.0935],[-136.8425,-21.1145],[-136.916,-21.1695],[-136.9465,-21.216],[-136.9665,-21.297]]],[[[-136.6745,-18.471],[-136.657,-18.553],[-136.5805,-18.6465],[-136.528,-18.6745],[-136.4525,-18.7405],[-136.376,-18.772],[-136.305,-18.7805],[-136.191,-18.7515],[-136.1115,-18.674],[-136.083,-18.565],[-136.098,-18.491],[-136.131,-18.4345],[-136.1815,-18.3855],[-136.2635,-18.327],[-136.372,-18.2745],[-136.46,-18.262],[-136.5735,-18.2945],[-136.633,-18.3475],[-136.661,-18.3985],[-136.6745,-18.471]]],[[[-136.414,-22.0275],[-136.3985,-22.102],[-136.3365,-22.1865],[-136.2565,-22.229],[-136.1385,-22.232],[-136.0665,-22.2],[-136.001,-22.1275],[-135.967,-22.025],[-135.99,-21.905],[-136.0405,-21.8415],[-136.1365,-21.795],[-136.2035,-21.7915],[-136.3135,-21.8345],[-136.3755,-21.898],[-136.403,-21.956],[-136.414,-22.0275]]],[[[-135.8525,-21.4795],[-135.838,-21.5695],[-135.8,-21.661],[-135.7295,-21.731],[-135.6465,-21.7645],[-135.549,-21.774],[-135.4565,-21.754],[-135.3435,-21.701],[-135.2715,-21.6265],[-135.2465,-21.539],[-135.2555,-21.4685],[-135.2785,-21.418],[-135.3625,-21.332],[-135.4805,-21.279],[-135.6265,-21.2755],[-135.7065,-21.2875],[-135.7525,-21.3085],[-135.815,-21.366],[-135.8525,-21.4795]]],[[[-135.328,-23.175],[-135.297,-23.2805],[-135.2495,-23.3335],[-135.031,-23.435],[-134.942,-23.4465],[-134.799,-23.406],[-134.7085,-23.353],[-134.6545,-23.4885],[-134.599,-23.5405],[-134.554,-23.563],[-134.4745,-23.5755],[-134.4005,-23.561],[-134.3195,-23.5135],[-134.2775,-23.4645],[-134.247,-23.383],[-134.2485,-23.3135],[-134.2785,-23.2415],[-134.3185,-23.191],[-134.396,-23.134],[-134.4715,-23.1165],[-134.592,-23.1385],[-134.629,-23.1255],[-134.643,-23.044],[-134.6675,-22.9995],[-134.7215,-22.948],[-134.783,-22.863],[-134.894,-22.81],[-134.9515,-22.802],[-135.0405,-22.818],[-135.13,-22.8855],[-135.17,-22.9425],[-135.291,-23.0605],[-135.3195,-23.1175],[-135.328,-23.175]]],[[[-109.4385,10.3085],[-109.43,10.2495],[-109.4035,10.1915],[-109.367,10.148],[-109.3035,10.104],[-109.2545,10.088],[-109.1715,10.0885],[-109.0825,10.1295],[-109.03,10.1875],[-109.002,10.2585],[-108.997,10.31],[-109.015,10.3875],[-109.0725,10.4625],[-109.1495,10.5055],[-109.2195,10.52],[-109.304,10.505],[-109.3895,10.4415],[-109.4275,10.375],[-109.4385,10.3085]]],[[[-62.847,17.674],[-62.729,17.6825],[-62.6575,17.7235],[-62.6055,17.794],[-62.588,17.853],[-62.588,17.948],[-62.611,18.017],[-62.642,18.059],[-62.7115,18.116],[-62.7645,18.1375],[-62.7675,18.163],[-62.8765,18.1905],[-62.9485,18.181],[-63.2095,18.098],[-63.3605,18.0615],[-63.3275,17.954],[-63.268,17.8965],[-63.139,18.053],[-63.063,18.064],[-62.9495,18.017],[-62.9695,17.9865],[-63.0545,17.806],[-63.0655,17.783],[-63.0195,17.736],[-62.847,17.674]]],[[[-61.1385,15.7015],[-61.036,15.784],[-60.996,15.854],[-60.912,16.093],[-60.814,16.245],[-60.7965,16.293],[-60.8045,16.403],[-60.852,16.482],[-60.9255,16.533],[-60.989,16.549],[-61.071,16.542],[-61.121,16.522],[-61.2105,16.525],[-61.2385,16.581],[-61.309,16.653],[-61.369,16.692],[-61.443,16.713],[-61.508,16.71],[-61.5625,16.692],[-61.8075,16.578],[-61.8585,16.561],[-61.9555,16.48],[-62.0035,16.367],[-62.015,16.238],[-61.975,16.025],[-61.925,15.908],[-61.8325,15.751],[-61.791,15.7],[-61.68,15.6445],[-61.4425,15.787],[-61.3235,15.734],[-61.1385,15.7015]]],[[[-60.753,14.2215],[-60.6905,14.2645],[-60.645,14.326],[-60.6055,14.4245],[-60.6055,14.516],[-60.6235,14.6105],[-60.6865,14.878],[-60.718,14.932],[-60.771,14.978],[-60.844,15.005],[-60.937,15.01],[-61.052,15.062],[-61.159,15.079],[-61.349,15.0075],[-61.399,14.944],[-61.4315,14.862],[-61.435,14.804],[-61.419,14.7275],[-61.3405,14.565],[-61.2955,14.4415],[-61.2575,14.365],[-61.1715,14.285],[-61.097,14.2485],[-61.004,14.2725],[-60.892,14.2435],[-60.753,14.2215]]],[[[-56.4945,47.365],[-56.5065,47.313],[-56.608,47.2615],[-56.659,47.2085],[-56.693,47.13],[-56.692,47.079],[-56.6535,46.9425],[-56.697,46.8295],[-56.6835,46.7675],[-56.64,46.7055],[-56.581,46.654],[-56.5,46.6115],[-56.264,46.5525],[-56.1675,46.5545],[-56.0135,46.5875],[-55.9035,46.646],[-55.9825,46.803],[-56.0995,46.86],[-56.1295,46.931],[-56.105,47.1005],[-56.255,47.3055],[-56.4945,47.365]]],[[[-54.601,2.3375],[-54.5365,2.325],[-54.478,2.2155],[-54.398,2.2035],[-54.363,2.18],[-54.2675,2.1435],[-54.204,2.1625],[-54.1395,2.118],[-54.1065,2.121],[-54.081,2.1745],[-54.015,2.183],[-53.987,2.2115],[-53.9395,2.2215],[-53.9375,2.283],[-53.892,2.277],[-53.883,2.3085],[-53.81,2.34],[-53.768,2.3755],[-53.723,2.349],[-53.659,2.2795],[-53.4615,2.253],[-53.432,2.2925],[-53.371,2.313],[-53.344,2.351],[-53.2255,2.2625],[-53.273,2.222],[-53.2225,2.201],[-53.105,2.2225],[-53.052,2.19],[-52.9565,2.173],[-52.9045,2.189],[-52.8425,2.2915],[-52.6605,2.374],[-52.6165,2.4755],[-52.5515,2.5205],[-52.5605,2.551],[-52.5275,2.581],[-52.533,2.65],[-52.4755,2.7635],[-52.478,2.7805],[-52.3955,2.931],[-52.326,3.079],[-52.349,3.1355],[-52.3,3.175],[-52.2925,3.2275],[-52.2365,3.2395],[-52.161,3.3525],[-52.087,3.4885],[-51.9885,3.63],[-51.971,3.707],[-51.9215,3.724],[-51.927,3.768],[-51.8705,3.807],[-51.7975,3.889],[-51.778,3.974],[-51.6585,4.053],[-51.6445,4.083],[-51.6335,4.176],[-51.6145,4.2355],[-51.6285,4.2685],[-51.6455,4.395],[-51.637,4.5085],[-51.5695,4.585],[-51.602,4.6765],[-51.769,4.9285],[-51.7985,4.964],[-51.864,5.0045],[-52.101,5.094],[-52.2255,5.1955],[-52.441,5.421],[-52.5085,5.468],[-52.857,5.618],[-53.206,5.7545],[-53.507,5.813],[-53.8435,5.978],[-53.967,5.761],[-54.0205,5.6585],[-54.008,5.551],[-54.0485,5.4915],[-54.128,5.4065],[-54.193,5.315],[-54.2965,5.2405],[-54.374,5.109],[-54.4055,5.084],[-54.4375,5.017],[-54.44,4.949],[-54.4865,4.8985],[-54.4735,4.857],[-54.4775,4.748],[-54.433,4.7035],[-54.4335,4.634],[-54.414,4.597],[-54.452,4.509],[-54.4405,4.4195],[-54.386,4.349],[-54.382,4.269],[-54.396,4.2025],[-54.324,4.149],[-54.358,4.0515],[-54.3185,4.015],[-54.2955,3.9475],[-54.2485,3.9055],[-54.2345,3.858],[-54.1995,3.8525],[-54.183,3.7995],[-54.127,3.798],[-54.0785,3.708],[-54.0835,3.6755],[-54.051,3.635],[-54.003,3.645],[-53.98,3.6055],[-54.004,3.563],[-54.009,3.466],[-54.023,3.4105],[-54.048,3.3875],[-54.064,3.3135],[-54.13,3.2845],[-54.174,3.2075],[-54.1935,3.1975],[-54.1945,3.0655],[-54.1715,2.9415],[-54.2075,2.829],[-54.206,2.7765],[-54.263,2.7225],[-54.2805,2.6625],[-54.3175,2.6285],[-54.349,2.5215],[-54.42,2.436],[-54.4735,2.4365],[-54.5045,2.3925],[-54.521,2.337],[-54.572,2.3545],[-54.601,2.3375]]],[[[1.442,42.6035],[1.479,42.6515],[1.5495,42.656],[1.6005,42.626],[1.7135,42.615],[1.7505,42.564],[1.726,42.5025],[1.8045,42.4905],[1.9585,42.424],[1.9645,42.3825],[2.012,42.353],[2.083,42.3635],[2.129,42.4125],[2.2,42.4165],[2.257,42.4385],[2.4145,42.392],[2.481,42.341],[2.542,42.334],[2.5795,42.3585],[2.661,42.367],[2.6795,42.407],[2.8005,42.4195],[2.84,42.459],[2.9185,42.456],[2.9455,42.48],[3.11,42.4355],[3.448,42.4355],[3.4375,42.4965],[3.394,42.588],[3.3105,42.672],[3.3135,42.814],[3.339,42.9795],[3.435,43.0685],[3.509,43.063],[3.599,43.0755],[3.6775,43.109],[3.7845,43.202],[3.8685,43.2255],[3.9965,43.303],[4.127,43.2715],[4.3,43.2565],[4.456,43.1845],[4.644,43.1455],[4.7115,43.147],[4.7925,43.123],[4.9375,43.127],[5.064,43.0375],[5.1535,43.007],[5.3425,42.973],[5.51,42.964],[5.705,42.871],[6.1375,42.787],[6.1835,42.7825],[6.411,42.787],[6.4775,42.7965],[6.615,42.847],[6.7455,42.926],[6.9225,43.095],[6.9735,43.226],[7.2015,43.332],[7.296,43.3815],[7.5,43.5165],[7.4185,43.725],[7.4525,43.742],[7.533,43.537],[7.631,43.5785],[7.6105,43.656],[7.539,43.744],[7.5075,43.842],[7.5115,43.885],[7.5615,43.899],[7.585,43.954],[7.652,43.9735],[7.7185,44.0825],[7.667,44.1335],[7.575,44.153],[7.357,44.1165],[7.2805,44.141],[7.194,44.19],[7.07,44.233],[7.0085,44.235],[6.897,44.3755],[6.9365,44.44],[6.876,44.4825],[6.8545,44.529],[6.9155,44.56],[6.969,44.626],[6.948,44.654],[7.041,44.7195],[7.0,44.789],[7.0065,44.8395],[6.932,44.8635],[6.8645,44.8515],[6.75,44.9075],[6.7635,44.971],[6.7485,45.016],[6.6735,45.02],[6.6275,45.107],[6.6805,45.1405],[6.768,45.16],[6.85,45.1275],[6.9555,45.1805],[6.966,45.208],[7.081,45.2245],[7.136,45.28],[7.1095,45.3185],[7.1595,45.3595],[7.1825,45.408],[7.0485,45.4725],[6.9905,45.531],[6.9955,45.5755],[6.97,45.654],[6.8465,45.6905],[6.808,45.7275],[6.818,45.836],[6.939,45.847],[6.99,45.868],[7.0455,45.9225],[7.0105,45.997],[6.8825,46.095],[6.898,46.122],[6.798,46.136],[6.804,46.2025],[6.8645,46.283],[6.7705,46.3555],[6.821,46.427],[6.682,46.4545],[6.519,46.4565],[6.4255,46.416],[6.335,46.4035],[6.253,46.3605],[6.2195,46.312],[6.2945,46.225],[6.2325,46.206],[6.1365,46.1415],[6.0525,46.1515],[5.995,46.183],[5.979,46.217],[6.1245,46.2515],[6.1025,46.285],[6.17,46.366],[6.0975,46.409],[6.073,46.4655],[6.1565,46.5455],[6.1105,46.5765],[6.283,46.6915],[6.438,46.7615],[6.435,46.8015],[6.4645,46.8905],[6.4325,46.9285],[6.505,46.9655],[6.6335,46.998],[6.7565,47.117],[6.85,47.1565],[6.8805,47.2005],[6.9555,47.244],[6.9405,47.286],[7.0455,47.3265],[7.05,47.3615],[6.924,47.355],[6.9405,47.4335],[7.002,47.454],[6.986,47.4935],[7.139,47.502],[7.2015,47.4925],[7.17,47.443],[7.255,47.4245],[7.3385,47.441],[7.3865,47.4325],[7.511,47.497],[7.5055,47.5445],[7.589,47.59],[7.5195,47.6675],[7.548,47.74],[7.5315,47.7865],[7.5595,47.8825],[7.622,47.9725],[7.5705,48.0315],[7.577,48.118],[7.668,48.2245],[7.6945,48.3025],[7.7455,48.3325],[7.737,48.4065],[7.7705,48.4915],[7.8045,48.5125],[7.803,48.5905],[7.8415,48.6435],[7.894,48.6655],[8.106,48.8245],[8.129,48.8795],[8.216,48.9755],[8.07,48.9965],[7.9315,49.058],[7.868,49.0335],[7.57,49.0795],[7.5285,49.097],[7.44,49.1835],[7.3655,49.172],[7.2985,49.1175],[7.245,49.13],[7.067,49.1145],[7.035,49.1915],[6.9185,49.2225],[6.834,49.1515],[6.7115,49.1885],[6.6615,49.282],[6.589,49.322],[6.588,49.3835],[6.552,49.425],[6.428,49.477],[6.3675,49.4695],[6.257,49.51],[6.196,49.5055],[6.042,49.448],[5.94,49.501],[5.8665,49.5],[5.8185,49.5465],[5.7005,49.54],[5.6645,49.553],[5.5935,49.522],[5.482,49.506],[5.449,49.517],[5.425,49.5985],[5.3535,49.6305],[5.2685,49.6965],[5.206,49.696],[5.125,49.727],[5.095,49.762],[5.0085,49.782],[4.856,49.792],[4.851,49.8655],[4.89,49.909],[4.849,49.948],[4.791,49.958],[4.827,50.05],[4.8675,50.0965],[4.8755,50.154],[4.824,50.161],[4.704,50.0965],[4.6845,49.997],[4.5975,49.9865],[4.51,49.947],[4.4455,49.937],[4.31,49.9695],[4.1965,49.9555],[4.136,50.021],[4.231,50.0735],[4.197,50.135],[4.1555,50.163],[4.201,50.275],[4.1225,50.298],[4.036,50.3435],[3.9675,50.35],[3.9005,50.327],[3.838,50.3545],[3.6655,50.347],[3.663,50.456],[3.6075,50.497],[3.3905,50.498],[3.2885,50.526],[3.241,50.669],[3.245,50.713],[3.1485,50.79],[3.019,50.7735],[2.9385,50.745],[2.899,50.694],[2.7895,50.729],[2.718,50.8135],[2.635,50.813],[2.604,50.9165],[2.633,50.946],[2.574,51.0035],[2.546,51.0885],[2.389,51.2685],[2.3695,51.305],[2.1935,51.281],[2.115,51.2425],[1.969,51.203],[1.8875,51.1995],[1.724,51.0985],[1.5465,51.038],[1.3555,50.949],[1.2815,50.8955],[1.2635,50.8245],[1.242,50.7075],[1.252,50.498],[1.2195,50.4095],[1.219,50.346],[1.1865,50.3015],[1.1645,50.2215],[0.986,50.126],[0.888,50.1195],[0.7835,50.0895],[0.4865,50.046],[0.2995,49.973],[0.0135,49.873],[-0.1055,49.8045],[-0.1665,49.742],[-0.1895,49.6505],[-0.2435,49.5365],[-0.347,49.5545],[-0.6075,49.562],[-0.7045,49.5505],[-0.8595,49.589],[-0.913,49.6465],[-0.9725,49.773],[-1.0425,49.8395],[-1.103,49.871],[-1.2205,49.9065],[-1.4145,49.92],[-1.552,49.896],[-1.6725,49.8875],[-1.7335,49.9085],[-1.8915,49.9325],[-2.0545,49.92],[-2.0505,49.8085],[-2.066,49.681],[-2.139,49.54],[-2.099,49.4595],[-1.994,49.3635],[-1.893,49.3155],[-1.8345,49.25],[-1.8345,49.1825],[-1.8605,49.065],[-1.944,48.9635],[-1.9845,48.9405],[-1.9845,48.8825],[-2.0845,48.871],[-2.243,48.871],[-2.5265,48.927],[-2.5265,49.0585],[-2.5605,49.22],[-2.7785,49.279],[-2.933,49.3035],[-3.008,49.28],[-3.096,49.223],[-3.1575,49.1165],[-3.212,49.1105],[-3.4935,49.1065],[-3.723,49.074],[-4.119,48.954],[-4.643,48.8475],[-4.9055,48.7575],[-5.17,48.6815],[-5.2465,48.667],[-5.3635,48.6055],[-5.4265,48.5315],[-5.451,48.458],[-5.447,48.4085],[-5.319,48.084],[-5.3115,48.0045],[-5.2655,47.9375],[-5.1765,47.8805],[-5.1025,47.858],[-4.479,47.592],[-4.261,47.5455],[-4.1775,47.4925],[-4.038,47.4625],[-3.9115,47.4765],[-3.6675,47.444],[-3.5155,47.222],[-3.4305,47.146],[-3.297,47.0975],[-3.1345,47.0795],[-3.037,47.081],[-2.879,47.108],[-2.6885,47.0335],[-2.67,47.009],[-2.702,46.7045],[-2.6855,46.656],[-2.626,46.586],[-2.557,46.539],[-2.0445,46.3445],[-1.781,46.0845],[-1.701,45.9955],[-1.6615,45.872],[-1.59,45.7955],[-1.558,45.7275],[-1.578,45.6505],[-1.5285,45.541],[-1.467,45.495],[-1.444,45.4495],[-1.448,45.315],[-1.4715,45.1695],[-1.5085,44.8675],[-1.547,44.6745],[-1.562,44.548],[-1.5385,44.4695],[-1.5895,44.172],[-1.656,43.924],[-1.714,43.7375],[-1.72,43.6915],[-1.804,43.595],[-1.772,43.488],[-1.772,43.388],[-1.79,43.3535],[-1.73,43.296],[-1.6695,43.315],[-1.609,43.252],[-1.533,43.2935],[-1.379,43.25],[-1.383,43.19],[-1.4145,43.128],[-1.471,43.079],[-1.441,43.0465],[-1.354,43.0285],[-1.346,43.0905],[-1.2475,43.0425],[-1.111,43.0205],[-1.0065,42.989],[-0.9455,42.9535],[-0.8105,42.9515],[-0.7515,42.967],[-0.718,42.8865],[-0.602,42.8315],[-0.5675,42.781],[-0.5055,42.8275],[-0.4435,42.796],[-0.394,42.799],[-0.308,42.8425],[-0.189,42.787],[-0.156,42.785],[-0.062,42.695],[0.0015,42.686],[0.1845,42.7355],[0.2595,42.7165],[0.2955,42.674],[0.3585,42.7195],[0.4225,42.6905],[0.527,42.7025],[0.675,42.6915],[0.646,42.7825],[0.7085,42.8615],[0.8585,42.8255],[0.9265,42.7895],[0.9605,42.806],[1.109,42.7715],[1.166,42.709],[1.3575,42.7195],[1.442,42.6035]]],[[[9.559,43.193],[9.4815,43.2195],[9.3575,43.225],[9.2715,43.203],[9.141,43.1335],[9.0755,43.0405],[9.053,42.9315],[8.958,42.8925],[8.8705,42.841],[8.56,42.744],[8.4815,42.685],[8.394,42.549],[8.3285,42.5015],[8.283,42.4445],[8.265,42.368],[8.269,42.227],[8.321,41.9555],[8.315,41.864],[8.3365,41.795],[8.4335,41.63],[8.5755,41.4305],[8.7155,41.3145],[8.831,41.261],[9.135,41.318],[9.27,41.292],[9.3165,41.336],[9.45,41.4065],[9.631,41.4335],[9.6495,41.49],[9.6745,41.7045],[9.6795,41.89],[9.79,42.0145],[9.82,42.087],[9.828,42.261],[9.801,42.3815],[9.815,42.435],[9.7825,42.6325],[9.733,42.7035],[9.7645,42.8085],[9.7165,42.8485],[9.6415,42.955],[9.589,43.1755],[9.559,43.193]]],[[[39.414,-21.471],[39.4495,-21.602],[39.482,-21.645],[39.55,-21.6965],[39.6555,-21.726],[39.7575,-21.716],[39.826,-21.6885],[39.881,-21.6505],[39.934,-21.5845],[39.9595,-21.5115],[39.953,-21.4245],[39.907,-21.3435],[39.814,-21.266],[39.731,-21.238],[39.658,-21.236],[39.539,-21.2735],[39.4905,-21.3075],[39.4435,-21.3655],[39.414,-21.471]]],[[[40.112,-22.3525],[40.1215,-22.433],[40.1585,-22.5],[40.241,-22.569],[40.3115,-22.5965],[40.3885,-22.6015],[40.477,-22.5775],[40.534,-22.5365],[40.5785,-22.4865],[40.6085,-22.422],[40.615,-22.3395],[40.585,-22.2505],[40.5425,-22.1955],[40.447,-22.14],[40.339,-22.13],[40.2585,-22.1525],[40.161,-22.2225],[40.124,-22.2865],[40.112,-22.3525]]],[[[42.491,-17.0635],[42.5255,-17.158],[42.574,-17.2095],[42.6475,-17.2515],[42.7015,-17.264],[42.8025,-17.255],[42.8615,-17.2305],[42.9115,-17.188],[42.945,-17.132],[42.959,-17.0705],[42.9515,-17.007],[42.918,-16.94],[42.8625,-16.8895],[42.7635,-16.8515],[42.674,-16.8465],[42.616,-16.8615],[42.554,-16.9015],[42.5105,-16.959],[42.491,-17.0635]]],[[[44.7435,-12.6615],[44.77,-12.7605],[44.747,-12.86],[44.755,-12.938],[44.813,-13.0935],[44.862,-13.1565],[44.9405,-13.217],[44.9935,-13.246],[45.0785,-13.2695],[45.198,-13.2665],[45.281,-13.2355],[45.395,-13.1505],[45.43,-13.091],[45.446,-12.9905],[45.4865,-12.9095],[45.506,-12.8115],[45.4945,-12.7015],[45.4705,-12.6385],[45.435,-12.591],[45.315,-12.48],[45.2035,-12.4105],[45.135,-12.3835],[45.08,-12.379],[44.99,-12.396],[44.9085,-12.4545],[44.838,-12.4845],[44.7935,-12.524],[44.7605,-12.5775],[44.7435,-12.6615]]],[[[47.0815,-11.5955],[47.097,-11.6625],[47.134,-11.7205],[47.214,-11.777],[47.3235,-11.793],[47.3915,-11.7725],[47.4645,-11.7245],[47.5345,-11.6545],[47.57,-11.5945],[47.585,-11.527],[47.5655,-11.4285],[47.5225,-11.37],[47.4195,-11.3175],[47.3335,-11.3185],[47.2675,-11.345],[47.18,-11.4015],[47.128,-11.448],[47.0855,-11.536],[47.0815,-11.5955]]],[[[49.8785,-46.088],[49.8895,-46.153],[49.94,-46.226],[50.0015,-46.2735],[50.1195,-46.3245],[50.096,-46.395],[50.119,-46.4875],[50.149,-46.53],[50.226,-46.5855],[50.2945,-46.6125],[50.3875,-46.6285],[50.5255,-46.616],[50.654,-46.549],[50.705,-46.4765],[50.703,-46.3685],[50.6285,-46.278],[50.536,-46.23],[50.584,-46.1315],[50.684,-46.072],[50.7195,-46.0285],[50.7415,-45.9575],[50.734,-45.905],[50.655,-45.806],[50.5575,-45.7565],[50.462,-45.738],[50.3575,-45.745],[50.211,-45.8035],[50.135,-45.868],[50.029,-45.9],[49.9605,-45.9425],[49.893,-46.0265],[49.8785,-46.088]]],[[[51.352,-46.387],[51.396,-46.5005],[51.5265,-46.615],[51.6605,-46.664],[51.8145,-46.6745],[51.879,-46.667],[51.9895,-46.6315],[52.104,-46.6685],[52.245,-46.68],[52.3865,-46.658],[52.5215,-46.5975],[52.5775,-46.544],[52.6155,-46.465],[52.61,-46.3955],[52.552,-46.302],[52.4135,-46.2185],[52.2605,-46.1845],[52.107,-46.19],[52.0025,-46.217],[51.8885,-46.164],[51.771,-46.143],[51.6135,-46.1595],[51.496,-46.199],[51.392,-46.2725],[51.352,-46.387]]],[[[54.3085,-15.884],[54.3235,-15.9595],[54.3725,-16.0345],[54.457,-16.0875],[54.5535,-16.0965],[54.6155,-16.078],[54.688,-16.021],[54.7185,-15.9725],[54.736,-15.901],[54.7135,-15.7995],[54.6755,-15.749],[54.6075,-15.7035],[54.4985,-15.684],[54.4125,-15.71],[54.3645,-15.747],[54.3295,-15.796],[54.3085,-15.884]]],[[[55.0035,-21.036],[55.019,-21.1495],[55.067,-21.2205],[55.1185,-21.3455],[55.171,-21.4085],[55.222,-21.4495],[55.3585,-21.5205],[55.4805,-21.564],[55.6505,-21.5905],[55.8405,-21.5565],[55.9125,-21.5215],[55.9875,-21.4455],[56.018,-21.3745],[56.0205,-21.286],[56.05,-21.2005],[56.025,-21.069],[55.967,-20.993],[55.9275,-20.9675],[55.858,-20.8295],[55.7625,-20.747],[55.6445,-20.702],[55.476,-20.674],[55.407,-20.674],[55.3325,-20.69],[55.2145,-20.736],[55.147,-20.772],[55.094,-20.8345],[55.075,-20.8845],[55.034,-20.9325],[55.0035,-21.036]]],[[[67.814,-49.953],[67.84,-50.038],[67.8995,-50.095],[67.986,-50.136],[68.0895,-50.1555],[68.1965,-50.151],[68.295,-50.1235],[68.373,-50.076],[68.4205,-50.014],[68.4325,-49.9455],[68.407,-49.878],[68.3475,-49.8205],[68.261,-49.779],[68.104,-49.759],[67.975,-49.783],[67.891,-49.826],[67.835,-49.8855],[67.814,-49.953]]],[[[68.114,-49.516],[68.1615,-49.6215],[68.282,-49.693],[68.432,-49.7145],[68.465,-49.848],[68.5445,-49.939],[68.53,-50.0355],[68.5735,-50.112],[68.6325,-50.157],[68.7425,-50.2035],[68.8755,-50.2185],[69.025,-50.19],[69.123,-50.1305],[69.175,-50.036],[69.1695,-49.977],[69.1295,-49.9125],[69.1965,-49.8975],[69.1915,-49.986],[69.2265,-50.048],[69.354,-50.1265],[69.4625,-50.1475],[69.545,-50.147],[69.6885,-50.1085],[69.7535,-50.0655],[69.8015,-49.9995],[69.8075,-49.9185],[69.936,-49.914],[70.028,-49.932],[70.125,-49.932],[70.369,-49.8765],[70.477,-49.8235],[70.588,-49.6945],[70.6755,-49.666],[70.7725,-49.592],[70.8025,-49.5255],[70.78,-49.425],[70.8175,-49.38],[70.8605,-49.2625],[70.855,-49.167],[70.8085,-49.031],[70.7355,-48.9555],[70.586,-48.8875],[70.445,-48.858],[70.1345,-48.8175],[70.0005,-48.787],[69.874,-48.64],[69.7885,-48.5945],[69.7425,-48.543],[69.672,-48.501],[69.523,-48.4695],[69.4415,-48.4705],[69.221,-48.4955],[69.1035,-48.4545],[69.0965,-48.4095],[69.0385,-48.3285],[68.9455,-48.2765],[68.8155,-48.252],[68.7325,-48.257],[68.6415,-48.2825],[68.5275,-48.367],[68.498,-48.441],[68.413,-48.478],[68.333,-48.5455],[68.302,-48.6155],[68.302,-48.6645],[68.336,-48.734],[68.482,-48.8925],[68.491,-48.9235],[68.4325,-49.0285],[68.4315,-49.1255],[68.46,-49.174],[68.394,-49.225],[68.34,-49.321],[68.2015,-49.374],[68.162,-49.4065],[68.114,-49.516]]],[[[77.249,-38.715],[77.271,-38.805],[77.3175,-38.8665],[77.3725,-38.904],[77.4715,-38.934],[77.5655,-38.938],[77.683,-38.9055],[77.7435,-38.8645],[77.7935,-38.791],[77.8,-38.7],[77.7755,-38.64],[77.7325,-38.588],[77.651,-38.532],[77.5195,-38.5025],[77.4165,-38.516],[77.308,-38.5775],[77.2635,-38.642],[77.249,-38.715]]],[[[77.2605,-37.841],[77.302,-37.9585],[77.3585,-38.015],[77.414,-38.049],[77.5215,-38.0785],[77.648,-38.0685],[77.7735,-38.0],[77.8285,-37.935],[77.8475,-37.8795],[77.837,-37.7735],[77.7705,-37.6685],[77.7075,-37.625],[77.602,-37.595],[77.511,-37.596],[77.369,-37.6455],[77.303,-37.7115],[77.272,-37.7765],[77.2605,-37.841]]],[[[157.985,-19.6495],[157.9945,-19.7075],[158.0495,-19.799],[158.0835,-19.914],[158.1385,-19.994],[158.1925,-20.0465],[158.3025,-20.106],[158.371,-20.1525],[158.4225,-20.172],[158.499,-20.1785],[158.6015,-20.145],[158.641,-20.113],[159.093,-19.5555],[159.1355,-19.4775],[159.185,-19.3605],[159.243,-19.1855],[159.2525,-19.113],[159.2235,-19.0235],[159.1245,-18.905],[159.069,-18.8065],[159.0035,-18.762],[158.9045,-18.743],[158.864,-18.7465],[158.4015,-18.83],[158.3015,-18.8785],[158.1805,-19.0055],[158.154,-19.059],[158.146,-19.148],[158.1085,-19.227],[158.0755,-19.368],[157.9985,-19.555],[157.985,-19.6495]]],[[[158.6325,-21.415],[158.667,-21.522],[158.6995,-21.559],[158.7835,-21.607],[158.8505,-21.6165],[158.965,-21.587],[159.013,-21.551],[159.0605,-21.473],[159.07,-21.4135],[159.056,-21.3435],[159.024,-21.29],[158.963,-21.241],[158.846,-21.2125],[158.7855,-21.2215],[158.691,-21.276],[158.6455,-21.344],[158.6325,-21.415]]],[[[162.6035,-18.1035],[162.6285,-18.4775],[162.653,-18.5675],[162.7035,-18.642],[162.861,-19.1225],[162.893,-19.2705],[162.95,-19.3975],[162.981,-19.5055],[163.0615,-19.632],[163.1505,-19.7285],[163.238,-19.852],[163.352,-19.979],[163.443,-20.0595],[163.4995,-20.132],[163.5325,-20.1925],[163.6065,-20.259],[163.6415,-20.321],[163.6915,-20.378],[163.768,-20.517],[163.8615,-20.66],[163.9545,-20.747],[164.008,-20.785],[164.0965,-20.947],[164.1315,-20.9875],[164.2295,-21.0605],[164.2655,-21.118],[164.31,-21.16],[164.4265,-21.2185],[164.515,-21.249],[164.635,-21.438],[164.6765,-21.482],[164.798,-21.564],[164.9245,-21.6655],[164.973,-21.6945],[165.168,-21.776],[165.2915,-21.8185],[165.3525,-21.897],[165.3985,-21.9325],[165.506,-21.974],[165.615,-22.07],[165.6705,-22.1075],[165.729,-22.1275],[165.7935,-22.1835],[165.8295,-22.2385],[165.9235,-22.2995],[166.0065,-22.415],[166.1885,-22.591],[166.2305,-22.6195],[166.3065,-22.717],[166.4275,-22.782],[166.5395,-22.9045],[166.688,-23.057],[166.7695,-23.111],[166.8475,-23.1755],[166.8955,-23.1985],[166.993,-23.2215],[167.1055,-23.2005],[167.6685,-22.943],[167.7385,-22.895],[167.7855,-22.8315],[167.8105,-22.749],[167.8065,-22.6935],[167.761,-22.541],[167.686,-22.417],[167.586,-22.345],[167.437,-22.2915],[167.2965,-22.216],[167.2255,-22.1505],[167.1605,-22.065],[167.1325,-21.9445],[167.0915,-21.8605],[167.0325,-21.803],[166.773,-21.637],[166.5745,-21.4525],[166.4595,-21.3825],[166.392,-21.3135],[166.2595,-21.2435],[166.1685,-21.1595],[165.892,-20.986],[165.759,-20.8745],[165.6535,-20.7645],[165.4845,-20.6265],[165.464,-20.5575],[165.4235,-20.492],[165.335,-20.428],[165.1955,-20.382],[165.058,-20.3655],[164.96,-20.334],[164.8405,-20.228],[164.724,-20.1325],[164.6865,-20.0785],[164.5555,-19.9355],[164.4925,-19.892],[164.445,-19.8245],[164.386,-19.7665],[164.2935,-19.7215],[164.1835,-19.5775],[163.9835,-19.258],[163.9915,-19.182],[163.9785,-19.1],[163.9525,-19.0545],[163.711,-18.7385],[163.6855,-18.711],[163.463,-18.5115],[163.446,-18.4235],[163.418,-18.337],[163.38,-18.2715],[163.335,-17.978],[163.3195,-17.9245],[163.2695,-17.8565],[163.2195,-17.822],[163.001,-17.7105],[162.898,-17.687],[162.8145,-17.7045],[162.731,-17.7695],[162.7025,-17.8205],[162.6315,-17.993],[162.6035,-18.1035]]],[[[164.139,-18.542],[164.1715,-18.6485],[164.2225,-18.7165],[164.2625,-18.747],[164.37,-18.795],[164.468,-18.802],[164.57,-18.757],[164.6165,-18.7035],[164.644,-18.607],[164.63,-18.5315],[164.5555,-18.377],[164.498,-18.3285],[164.448,-18.308],[164.3645,-18.3015],[164.2665,-18.338],[164.1935,-18.407],[164.156,-18.4635],[164.139,-18.542]]],[[[165.3045,-19.883],[165.3155,-19.945],[165.384,-20.0375],[165.4965,-20.0815],[165.572,-20.079],[165.812,-20.088],[165.899,-20.0735],[165.987,-20.011],[166.0415,-19.909],[166.0565,-19.8025],[166.022,-19.718],[165.9485,-19.6545],[165.6965,-19.5245],[165.5815,-19.502],[165.494,-19.525],[165.4385,-19.5595],[165.379,-19.646],[165.32,-19.799],[165.3045,-19.883]]],[[[165.8965,-20.317],[165.913,-20.4225],[165.988,-20.6355],[166.0315,-20.6905],[166.2465,-20.8735],[166.346,-20.924],[166.408,-20.9355],[166.4685,-20.9295],[166.865,-21.0805],[166.941,-21.181],[167.012,-21.241],[167.14,-21.2865],[167.244,-21.363],[167.2925,-21.3795],[167.428,-21.471],[167.689,-21.737],[167.77,-21.788],[167.8165,-21.7995],[167.9155,-21.852],[168.0475,-21.86],[168.118,-21.8455],[168.2095,-21.8085],[168.266,-21.771],[168.315,-21.703],[168.33,-21.649],[168.3265,-21.5755],[168.349,-21.477],[168.349,-21.4245],[168.326,-21.355],[168.2805,-21.2995],[168.143,-21.201],[168.0235,-21.0105],[167.987,-20.964],[167.908,-20.905],[167.4255,-20.566],[167.299,-20.495],[167.0885,-20.456],[166.8005,-20.296],[166.7125,-20.2185],[166.6675,-20.199],[166.3295,-20.139],[166.247,-20.1035],[166.124,-20.088],[166.054,-20.107],[165.997,-20.143],[165.9285,-20.2135],[165.8965,-20.317]]],[[[168.726,-22.607],[168.738,-22.6735],[168.7815,-22.7445],[168.842,-22.789],[168.9265,-22.8115],[169.016,-22.801],[169.117,-22.7345],[169.167,-22.6455],[169.1685,-22.5355],[169.132,-22.4635],[169.048,-22.3995],[168.974,-22.381],[168.8645,-22.3985],[168.7905,-22.4495],[168.7355,-22.5475],[168.726,-22.607]]],[[[171.135,-22.343],[171.145,-22.404],[171.1795,-22.4665],[171.2655,-22.5295],[171.381,-22.5465],[171.462,-22.523],[171.5475,-22.447],[171.5785,-22.3435],[171.572,-22.295],[171.53,-22.217],[171.4385,-22.154],[171.332,-22.14],[171.263,-22.157],[171.1925,-22.204],[171.152,-22.263],[171.135,-22.343]]],[[[171.8685,-22.389],[171.8795,-22.458],[171.9075,-22.511],[171.9805,-22.573],[172.0725,-22.598],[172.149,-22.5905],[172.2405,-22.5405],[172.2935,-22.4635],[172.303,-22.365],[172.284,-22.3055],[172.2505,-22.2585],[172.172,-22.2055],[172.0995,-22.19],[171.98,-22.2165],[171.903,-22.284],[171.8685,-22.389]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/c/c3/Flag_of_France.svg","name:en":"France","wikidata":"Q142","ISO3166-1:alpha2":"FR","ISO3166-1:alpha3":"FRA","ISO3166-1:numeric":"250"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-0.136,11.1395],[-0.2755,11.175],[-0.2735,11.1235],[-0.336,11.107],[-0.381,11.126],[-0.4275,11.1145],[-0.4335,11.037],[-0.5005,11.0185],[-0.5105,10.993],[-0.5685,10.9925],[-0.5935,10.9235],[-0.6715,10.961],[-0.684,10.9975],[-0.855,10.998],[-0.87,10.9665],[-0.9165,11.002],[-1.0005,11.008],[-1.115,11.004],[-1.115,10.9855],[-1.3825,10.997],[-1.425,11.021],[-1.6155,11.021],[-1.6165,10.985],[-2.1315,10.986],[-2.2245,10.994],[-2.5005,10.99],[-2.6895,11.0045],[-2.8335,11.0065],[-2.841,10.971],[-2.8175,10.9255],[-2.8635,10.8645],[-2.8955,10.764],[-2.9405,10.7105],[-2.91,10.6985],[-2.941,10.6365],[-2.863,10.452],[-2.7775,10.4255],[-2.8315,10.388],[-2.8505,10.319],[-2.832,10.295],[-2.766,10.2655],[-2.761,10.2265],[-2.7965,10.1985],[-2.7905,10.0695],[-2.775,10.01],[-2.743,9.9565],[-2.7625,9.8885],[-2.73,9.8385],[-2.7905,9.7435],[-2.7835,9.6895],[-2.7475,9.634],[-2.772,9.6],[-2.7605,9.555],[-2.687,9.4915],[-2.68,9.3625],[-2.7205,9.333],[-2.664,9.24],[-2.7175,9.2015],[-2.7255,9.17],[-2.773,9.1435],[-2.762,9.0375],[-2.664,9.019],[-2.652,8.9445],[-2.618,8.9215],[-2.623,8.8785],[-2.5845,8.7865],[-2.4955,8.3025],[-2.493,8.2065],[-2.5515,8.1455],[-2.6085,8.152],[-2.626,8.102],[-2.6045,8.0305],[-2.6795,8.0205],[-2.7375,7.9475],[-2.7815,7.952],[-2.791,7.8625],[-2.831,7.82],[-2.922,7.612],[-2.931,7.4945],[-2.97,7.3315],[-2.9755,7.27],[-2.953,7.2375],[-3.024,7.142],[-3.032,7.072],[-3.0875,7.0525],[-3.2305,6.8235],[-3.216,6.7255],[-3.259,6.6165],[-3.2335,6.6015],[-3.236,6.5415],[-3.17,6.283],[-3.1025,6.153],[-3.074,5.985],[-3.02,5.857],[-3.0265,5.709],[-2.954,5.716],[-2.9665,5.6415],[-2.9365,5.6215],[-2.864,5.6525],[-2.7615,5.6],[-2.768,5.5555],[-2.749,5.4375],[-2.724,5.388],[-2.724,5.343],[-2.766,5.317],[-2.7745,5.261],[-2.7275,5.1325],[-2.8075,5.1065],[-2.9485,5.127],[-2.97,5.1035],[-3.0685,5.1345],[-3.1105,5.1185],[-3.1035,5.086],[-3.1485,4.896],[-2.526,4.7705],[-2.409,4.7405],[-2.299,4.6395],[-2.1965,4.5685],[-2.1285,4.5425],[-2.038,4.5415],[-1.9225,4.5645],[-1.8665,4.591],[-1.7965,4.651],[-1.626,4.714],[-1.5585,4.7875],[-1.486,4.8445],[-1.1565,4.922],[-1.0185,5.0015],[-0.8075,5.0125],[-0.7565,5.0225],[-0.682,5.058],[-0.5995,5.132],[-0.4,5.204],[-0.274,5.314],[-0.1435,5.344],[0.0885,5.438],[0.1605,5.4855],[0.1865,5.5175],[0.335,5.581],[0.4385,5.5895],[0.6875,5.567],[0.8315,5.5735],[0.996,5.5995],[1.094,5.664],[1.152,5.736],[1.1775,5.791],[1.1955,5.867],[1.2185,5.8985],[1.2735,5.9265],[1.1995,6.1125],[1.1995,6.169],[1.0835,6.169],[1.054,6.2345],[1.008,6.294],[1.0035,6.3355],[0.893,6.3335],[0.857,6.384],[0.789,6.4085],[0.7495,6.444],[0.7125,6.529],[0.7475,6.563],[0.657,6.6075],[0.6355,6.6415],[0.6485,6.74],[0.5815,6.7615],[0.5675,6.822],[0.532,6.829],[0.535,6.8755],[0.5655,6.923],[0.5265,6.941],[0.5435,6.9885],[0.59,6.999],[0.6115,7.1055],[0.595,7.12],[0.634,7.207],[0.6585,7.3175],[0.644,7.401],[0.5685,7.392],[0.536,7.4275],[0.52,7.514],[0.524,7.594],[0.586,7.622],[0.5905,7.703],[0.6115,7.753],[0.6275,7.8675],[0.5965,7.987],[0.6025,8.022],[0.5845,8.093],[0.6385,8.2605],[0.731,8.2875],[0.729,8.343],[0.6865,8.4115],[0.654,8.42],[0.647,8.491],[0.511,8.564],[0.4715,8.5995],[0.3745,8.754],[0.3795,8.792],[0.441,8.7895],[0.5005,8.8145],[0.5275,8.878],[0.5165,8.9375],[0.494,8.954],[0.457,9.0355],[0.5335,9.2095],[0.506,9.255],[0.5385,9.306],[0.567,9.406],[0.4955,9.446],[0.489,9.487],[0.4485,9.4995],[0.3595,9.499],[0.337,9.452],[0.2655,9.4305],[0.23,9.4625],[0.309,9.502],[0.2475,9.5215],[0.239,9.5715],[0.366,9.576],[0.3565,9.625],[0.303,9.5965],[0.264,9.6635],[0.3175,9.652],[0.351,9.675],[0.3245,9.7225],[0.325,9.772],[0.3575,9.8465],[0.3605,10.0275],[0.4095,10.0205],[0.4175,10.0575],[0.3565,10.091],[0.351,10.1925],[0.3615,10.258],[0.334,10.3365],[0.2645,10.409],[0.1735,10.426],[0.1425,10.5265],[0.061,10.561],[0.0415,10.6015],[-0.059,10.6325],[-0.0785,10.6905],[-0.074,10.7575],[-0.022,10.8185],[-0.0255,10.8805],[-0.005,10.9645],[0.032,10.9805],[0.024,11.0625],[-0.005,11.108],[-0.0545,11.086],[-0.1265,11.106],[-0.136,11.1395]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/19/Flag_of_Ghana.svg","name:en":"Ghana","wikidata":"Q117","ISO3166-1:alpha2":"GH","ISO3166-1:alpha3":"GHA","ISO3166-1:numeric":"288"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-16.8945,12.2375],[-16.837,11.9175],[-16.7855,11.594],[-16.6775,11.2175],[-16.6525,11.1635],[-16.6125,11.119],[-16.302,10.863],[-16.256,10.8395],[-15.798,10.67],[-15.7515,10.659],[-15.5935,10.6515],[-15.562,10.6665],[-15.3415,10.6665],[-15.1015,10.8845],[-15.074,10.892],[-15.002,10.9555],[-14.9575,10.977],[-14.936,11.0265],[-14.8375,11.2025],[-14.7725,11.3645],[-14.69,11.483],[-14.6635,11.505],[-14.5115,11.4965],[-14.4505,11.537],[-14.3195,11.6025],[-14.267,11.677],[-14.135,11.6645],[-14.081,11.6275],[-14.039,11.655],[-13.9885,11.6415],[-13.918,11.68],[-13.872,11.6605],[-13.856,11.705],[-13.772,11.682],[-13.708,11.7075],[-13.7055,11.8325],[-13.7125,11.8805],[-13.6965,12.008],[-13.762,12.03],[-13.8065,12.0885],[-13.868,12.108],[-13.876,12.137],[-13.9495,12.1595],[-13.916,12.2355],[-13.857,12.2425],[-13.851,12.2825],[-13.7645,12.275],[-13.718,12.249],[-13.687,12.308],[-13.6565,12.324],[-13.666,12.3905],[-13.6365,12.433],[-13.6415,12.49],[-13.6715,12.52],[-13.7095,12.608],[-13.708,12.6755],[-14.3235,12.6765],[-14.913,12.6785],[-15.1225,12.686],[-15.184,12.682],[-15.3375,12.6135],[-15.429,12.537],[-15.683,12.425],[-15.89,12.4505],[-15.9535,12.443],[-16.039,12.4725],[-16.1625,12.4495],[-16.203,12.4615],[-16.3755,12.376],[-16.5145,12.3505],[-16.545,12.3605],[-16.609,12.3475],[-16.6895,12.359],[-16.8945,12.2375]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/01/Flag_of_Guinea-Bissau.svg","name:en":"Guinea-Bissau","wikidata":"Q1007","ISO3166-1:alpha2":"GW","ISO3166-1:alpha3":"GNB","ISO3166-1:numeric":"624"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[32.1345,-26.8405],[32.135,-26.527],[32.0775,-26.4055],[32.0745,-26.3055],[32.1055,-26.1615],[32.0875,-26.0085],[32.003,-25.997],[31.975,-25.9525],[31.868,-25.9995],[31.4165,-25.719],[31.316,-25.743],[31.2635,-25.8095],[31.1265,-25.921],[31.113,-25.9825],[31.071,-26.0675],[30.9615,-26.2615],[30.892,-26.321],[30.805,-26.4665],[30.792,-26.569],[30.791,-26.7125],[30.813,-26.8445],[30.8875,-26.8035],[30.9075,-26.86],[30.9735,-26.908],[30.9585,-26.992],[30.985,-27.037],[31.0615,-27.0965],[31.1505,-27.202],[31.4935,-27.315],[31.978,-27.3175],[31.9745,-27.1165],[31.996,-26.924],[32.016,-26.82],[32.0925,-26.805],[32.1345,-26.8405]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/1/1e/Flag_of_Swaziland.svg","name:en":"Eswatini","wikidata":"Q1050","ISO3166-1:alpha2":"SZ","ISO3166-1:alpha3":"SWZ","ISO3166-1:numeric":"748"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[30.796,-8.2765],[30.7595,-8.145],[30.697,-7.9705],[30.6495,-7.878],[30.5095,-7.6795],[30.456,-7.5805],[30.4355,-7.5095],[30.426,-7.428],[30.382,-7.284],[30.3115,-7.137],[30.1885,-6.9625],[30.0375,-6.822],[29.8915,-6.7555],[29.746,-6.6415],[29.704,-6.5865],[29.656,-6.4465],[29.571,-6.338],[29.537,-6.239],[29.533,-6.167],[29.4985,-6.0565],[29.5045,-5.946],[29.5355,-5.894],[29.614,-5.799],[29.632,-5.7485],[29.626,-5.6855],[29.5975,-5.579],[29.5345,-5.448],[29.485,-5.289],[29.4585,-5.1845],[29.4245,-5.1425],[29.3825,-5.052],[29.3515,-4.9515],[29.3285,-4.775],[29.336,-4.652],[29.3715,-4.5695],[29.436,-4.449],[29.419,-4.2495],[29.3825,-4.117],[29.313,-3.9855],[29.244,-3.928],[29.2255,-3.843],[29.2495,-3.7165],[29.245,-3.5155],[29.2135,-3.295],[29.238,-3.279],[29.2225,-3.198],[29.2475,-3.1255],[29.2545,-3.0525],[29.203,-3.0225],[29.158,-2.9505],[29.0965,-2.9205],[29.094,-2.882],[29.0515,-2.821],[29.006,-2.8165],[29.001,-2.7855],[29.0405,-2.7435],[29.004,-2.7045],[28.918,-2.681],[28.891,-2.5575],[28.8625,-2.5325],[28.892,-2.476],[28.8665,-2.4365],[28.909,-2.3725],[28.948,-2.3765],[28.9985,-2.2975],[29.092,-2.2765],[29.1155,-2.256],[29.1735,-2.129],[29.156,-2.0175],[29.129,-1.9415],[29.136,-1.861],[29.156,-1.82],[29.245,-1.698],[29.2685,-1.6295],[29.3105,-1.5995],[29.362,-1.509],[29.4505,-1.506],[29.526,-1.4115],[29.591,-1.3875],[29.608,-1.318],[29.595,-1.2805],[29.607,-1.219],[29.5735,-1.186],[29.591,-1.0535],[29.589,-0.9005],[29.631,-0.8905],[29.641,-0.831],[29.6255,-0.725],[29.6745,-0.5385],[29.7115,-0.22],[29.7325,-0.0805],[29.7185,0.075],[29.7375,0.126],[29.7895,0.1695],[29.82,0.1655],[29.8725,0.388],[29.979,0.5155],[29.973,0.597],[29.9545,0.639],[29.977,0.745],[29.9635,0.7785],[29.989,0.8455],[30.1115,0.895],[30.169,0.905],[30.1765,0.9445],[30.2285,0.9885],[30.235,1.125],[30.29,1.1725],[30.336,1.1505],[30.36,1.2015],[30.391,1.1875],[30.4955,1.206],[30.5195,1.266],[30.5495,1.266],[31.011,1.785],[31.3055,2.1165],[31.3055,2.157],[31.203,2.2215],[31.203,2.2895],[31.131,2.262],[31.077,2.2955],[31.0745,2.3435],[30.9825,2.405],[30.9595,2.395],[30.934,2.335],[30.882,2.341],[30.834,2.427],[30.7415,2.4485],[30.762,2.51],[30.753,2.591],[30.779,2.606],[30.8265,2.729],[30.884,2.814],[30.888,2.8785],[30.8565,2.962],[30.769,3.045],[30.791,3.0635],[30.8025,3.1365],[30.8415,3.243],[30.8875,3.338],[30.937,3.399],[30.942,3.506],[30.8505,3.5005],[30.864,3.5475],[30.8505,3.5825],[30.8045,3.5955],[30.798,3.6555],[30.777,3.681],[30.7275,3.6235],[30.688,3.643],[30.6195,3.6055],[30.5565,3.6275],[30.574,3.671],[30.578,3.754],[30.5485,3.844],[30.47,3.8315],[30.411,3.8655],[30.3465,3.924],[30.277,3.9565],[30.2115,3.94],[30.195,4.024],[30.161,4.049],[30.1635,4.1155],[30.1225,4.112],[30.0595,4.133],[30.028,4.2055],[29.958,4.224],[29.9445,4.248],[29.9655,4.2955],[29.903,4.3475],[29.838,4.3375],[29.8,4.373],[29.797,4.4375],[29.826,4.56],[29.7495,4.5695],[29.62,4.6595],[29.4905,4.6945],[29.473,4.6485],[29.4745,4.5925],[29.4485,4.5665],[29.446,4.52],[29.404,4.473],[29.3695,4.471],[29.347,4.3975],[29.269,4.3975],[29.25,4.352],[29.2215,4.341],[29.138,4.429],[29.08,4.4345],[29.035,4.4915],[28.91,4.476],[28.8095,4.5005],[28.8025,4.5565],[28.7305,4.545],[28.6955,4.5025],[28.668,4.4285],[28.6015,4.4185],[28.584,4.384],[28.5175,4.3795],[28.4645,4.295],[28.3825,4.2855],[28.356,4.353],[28.3015,4.361],[28.2065,4.352],[28.15,4.426],[28.089,4.4345],[28.0375,4.4795],[28.0415,4.531],[28.0175,4.555],[27.9435,4.5735],[27.9225,4.5355],[27.886,4.5425],[27.842,4.5895],[27.7925,4.6065],[27.766,4.69],[27.7825,4.7275],[27.7715,4.7915],[27.707,4.817],[27.6485,4.896],[27.559,4.892],[27.5065,4.9395],[27.512,4.9615],[27.449,5.0195],[27.4415,5.074],[27.2625,5.151],[27.2295,5.153],[27.1355,5.2075],[27.086,5.2085],[27.022,5.1865],[26.9295,5.1375],[26.88,5.0485],[26.862,5.038],[26.747,5.109],[26.6595,5.08],[26.6385,5.0935],[26.571,5.083],[26.515,5.0485],[26.451,5.068],[26.4535,5.0945],[26.386,5.1515],[26.2935,5.1635],[26.244,5.2025],[26.214,5.204],[26.1575,5.2615],[26.093,5.246],[26.0615,5.1995],[25.9805,5.247],[25.9485,5.2045],[25.8265,5.2015],[25.781,5.259],[25.752,5.244],[25.7075,5.301],[25.604,5.339],[25.579,5.3785],[25.5,5.344],[25.4425,5.337],[25.335,5.261],[25.308,5.191],[25.344,5.11],[25.312,5.031],[25.2485,5.026],[25.086,4.9775],[25.0645,4.928],[24.996,4.9585],[24.962,4.9875],[24.9115,4.952],[24.8,4.9265],[24.783,4.8965],[24.6785,4.908],[24.672,4.9675],[24.6305,4.977],[24.625,5.013],[24.5685,5.033],[24.534,5.083],[24.4695,5.1045],[24.432,5.0635],[24.4095,5.102],[24.363,5.0725],[24.393,5.0295],[24.3555,5.006],[24.282,4.9965],[24.2665,4.969],[24.2155,4.954],[24.136,4.895],[24.111,4.9185],[24.0485,4.867],[23.962,4.875],[23.956,4.81],[23.891,4.833],[23.828,4.8325],[23.7665,4.7835],[23.7125,4.7865],[23.676,4.765],[23.5945,4.762],[23.5025,4.6775],[23.4615,4.6785],[23.427,4.6495],[23.429,4.599],[23.3285,4.608],[23.266,4.6665],[23.2305,4.6755],[23.196,4.723],[23.157,4.7395],[23.1105,4.7115],[23.01,4.757],[22.977,4.834],[22.8995,4.815],[22.893,4.75],[22.842,4.6995],[22.816,4.727],[22.782,4.7145],[22.7355,4.6305],[22.731,4.5565],[22.6895,4.4695],[22.62,4.4915],[22.596,4.48],[22.575,4.344],[22.544,4.288],[22.546,4.214],[22.4525,4.1345],[22.3815,4.121],[22.2585,4.115],[22.165,4.155],[22.0885,4.218],[21.9965,4.2455],[21.949,4.2335],[21.859,4.2465],[21.8095,4.287],[21.737,4.312],[21.6485,4.3125],[21.5585,4.253],[21.478,4.2645],[21.429,4.293],[21.387,4.281],[21.3415,4.2935],[21.282,4.345],[21.2265,4.293],[21.116,4.34],[21.0765,4.4],[20.9485,4.446],[20.8415,4.4545],[20.797,4.423],[20.7095,4.433],[20.5805,4.4125],[20.5215,4.4565],[20.4655,4.524],[20.447,4.567],[20.464,4.596],[20.4415,4.646],[20.4075,4.663],[20.3855,4.7335],[20.352,4.7665],[20.254,4.789],[20.173,4.892],[20.1065,4.9025],[20.0075,4.9715],[19.9135,4.9825],[19.863,5.033],[19.8195,5.1075],[19.7215,5.1265],[19.6795,5.1465],[19.613,5.13],[19.5285,5.1495],[19.4805,5.122],[19.437,5.1325],[19.3705,5.0965],[19.362,5.0735],[19.223,4.995],[19.197,4.9415],[19.107,4.941],[19.0195,4.767],[18.9385,4.701],[18.9215,4.6525],[18.8365,4.587],[18.8195,4.5545],[18.806,4.468],[18.786,4.4275],[18.7175,4.369],[18.621,4.3565],[18.603,4.371],[18.545,4.321],[18.6465,4.0615],[18.659,3.986],[18.6475,3.95],[18.6095,3.925],[18.5935,3.818],[18.5785,3.777],[18.6045,3.617],[18.608,3.518],[18.626,3.471],[18.6435,3.3115],[18.633,3.2745],[18.6415,3.2045],[18.62,3.144],[18.539,3.0755],[18.491,2.9495],[18.4445,2.8665],[18.402,2.73],[18.3205,2.581],[18.243,2.5295],[18.2265,2.4845],[18.2245,2.4185],[18.1685,2.3315],[18.1105,2.2805],[18.091,2.218],[18.0945,2.139],[18.072,2.0425],[18.084,1.938],[18.0655,1.875],[18.078,1.729],[18.0705,1.5285],[17.962,1.3195],[17.926,1.1455],[17.845,1.021],[17.8405,0.9245],[17.8775,0.8525],[17.889,0.776],[17.869,0.702],[17.868,0.614],[17.8855,0.571],[17.932,0.534],[17.9685,0.453],[17.954,0.3615],[17.92,0.3145],[17.8215,0.2335],[17.774,0.1225],[17.734,-0.064],[17.6765,-0.168],[17.6635,-0.231],[17.677,-0.3005],[17.7135,-0.356],[17.7165,-0.4145],[17.694,-0.5575],[17.6755,-0.6105],[17.6355,-0.6695],[17.582,-0.6945],[17.5295,-0.7445],[17.344,-0.99],[17.2485,-1.036],[17.1495,-1.0565],[17.0455,-1.109],[16.9535,-1.167],[16.807,-1.3165],[16.74,-1.428],[16.681,-1.562],[16.663,-1.62],[16.576,-1.7865],[16.508,-1.8905],[16.441,-1.96],[16.4065,-1.98],[16.3665,-2.0365],[16.2655,-2.0935],[16.1935,-2.1805],[16.1745,-2.3445],[16.236,-2.602],[16.2355,-2.6545],[16.208,-2.7235],[16.179,-2.904],[16.192,-3.0],[16.1715,-3.118],[16.187,-3.192],[16.1815,-3.2565],[16.2135,-3.287],[16.2015,-3.352],[16.164,-3.423],[16.1245,-3.472],[16.0915,-3.5565],[16.039,-3.627],[15.9995,-3.725],[15.9675,-3.7685],[15.927,-3.8595],[15.906,-3.9335],[15.8865,-3.9545],[15.803,-3.9815],[15.7195,-3.989],[15.5645,-4.0355],[15.517,-4.078],[15.4795,-4.229],[15.404,-4.2855],[15.3285,-4.269],[15.2565,-4.316],[15.1965,-4.325],[15.1755,-4.391],[15.144,-4.4185],[15.1,-4.489],[15.0315,-4.539],[14.9865,-4.603],[14.8985,-4.688],[14.848,-4.807],[14.7535,-4.862],[14.7005,-4.9065],[14.651,-4.922],[14.591,-4.9035],[14.5615,-4.865],[14.472,-4.8505],[14.415,-4.896],[14.3975,-4.8515],[14.407,-4.7035],[14.4195,-4.6515],[14.3905,-4.6335],[14.3595,-4.561],[14.4625,-4.465],[14.4805,-4.432],[14.4255,-4.3975],[14.393,-4.3555],[14.4015,-4.2785],[14.2885,-4.3195],[14.2525,-4.3575],[14.176,-4.398],[14.099,-4.415],[14.07,-4.4045],[13.9445,-4.5155],[13.88,-4.509],[13.8445,-4.4435],[13.7795,-4.424],[13.7235,-4.4555],[13.734,-4.506],[13.7175,-4.6375],[13.7255,-4.6895],[13.6995,-4.745],[13.5775,-4.803],[13.515,-4.7475],[13.495,-4.802],[13.441,-4.863],[13.4285,-4.913],[13.373,-4.8585],[13.3735,-4.8],[13.2815,-4.7655],[13.1855,-4.6665],[13.1405,-4.594],[13.105,-4.6025],[13.1025,-4.684],[13.0515,-4.675],[13.03,-4.7155],[12.9805,-4.7055],[12.923,-4.7475],[12.8965,-4.735],[12.828,-4.782],[12.8155,-4.839],[12.7285,-4.911],[12.72,-4.95],[12.626,-4.9505],[12.6055,-5.0265],[12.518,-5.0465],[12.4695,-5.0825],[12.494,-5.14],[12.528,-5.167],[12.5285,-5.742],[12.2785,-5.742],[12.219,-5.7525],[12.202,-5.774],[12.039,-5.8895],[12.1145,-5.99],[12.3125,-6.006],[12.3895,-6.068],[12.4445,-6.078],[12.625,-6.0195],[12.785,-5.986],[12.856,-5.9245],[12.951,-5.9125],[13.0425,-5.8635],[13.084,-5.871],[13.1135,-5.9035],[13.2225,-5.862],[13.365,-5.902],[13.3775,-5.8725],[13.431,-5.86],[13.5145,-5.8625],[13.572,-5.883],[13.617,-5.873],[13.774,-5.8695],[14.013,-5.839],[14.146,-5.8495],[14.1795,-5.8395],[14.239,-5.881],[14.2775,-5.8605],[14.3105,-5.883],[14.4125,-5.897],[14.5055,-5.8875],[14.596,-5.9045],[14.6995,-5.878],[14.9445,-5.8655],[15.4195,-5.87],[15.68,-5.862],[15.8145,-5.878],[16.153,-5.864],[16.1745,-5.86],[16.4345,-5.866],[16.492,-5.844],[16.5835,-5.878],[16.577,-5.937],[16.6005,-5.9785],[16.596,-6.033],[16.624,-6.0395],[16.663,-6.091],[16.7135,-6.1295],[16.7305,-6.167],[16.704,-6.203],[16.7035,-6.2655],[16.7195,-6.331],[16.678,-6.382],[16.718,-6.451],[16.7055,-6.4765],[16.7445,-6.511],[16.7325,-6.586],[16.754,-6.6315],[16.7655,-6.713],[16.7975,-6.7265],[16.802,-6.7935],[16.8365,-6.8145],[16.846,-6.884],[16.8735,-6.8755],[16.9105,-6.912],[16.9,-6.983],[16.9665,-7.0525],[16.9555,-7.1045],[16.925,-7.141],[16.955,-7.2115],[16.941,-7.236],[17.0205,-7.2925],[17.0365,-7.317],[17.1085,-7.362],[17.135,-7.4055],[17.1735,-7.424],[17.15,-7.471],[17.195,-7.496],[17.2125,-7.572],[17.302,-7.6335],[17.2905,-7.687],[17.316,-7.7155],[17.335,-7.782],[17.377,-7.7765],[17.42,-7.8555],[17.45,-7.942],[17.484,-7.9615],[17.4805,-8.0015],[17.514,-8.0165],[17.5035,-8.066],[17.539,-8.1115],[17.614,-8.131],[17.6795,-8.101],[17.7625,-8.1005],[17.7965,-8.1135],[17.874,-8.0805],[17.976,-8.096],[18.1035,-8.0965],[18.093,-8.056],[18.1425,-8.0355],[18.1965,-7.986],[18.2835,-7.987],[18.3115,-8.0155],[18.3715,-8.0245],[18.4075,-8.0],[18.532,-8.0],[18.5305,-7.9165],[18.762,-7.9165],[18.7965,-8.0],[19.3815,-8.0],[19.3585,-7.9495],[19.3635,-7.8075],[19.3835,-7.789],[19.406,-7.6705],[19.3695,-7.597],[19.383,-7.572],[19.4735,-7.572],[19.486,-7.5045],[19.53,-7.4625],[19.5225,-7.3935],[19.4945,-7.3635],[19.488,-7.3105],[19.505,-7.249],[19.4985,-7.168],[19.562,-7.0555],[19.548,-7.0],[20.303,-7.0],[20.322,-6.9165],[20.627,-6.917],[20.5955,-7.0605],[20.5615,-7.1175],[20.548,-7.229],[20.553,-7.2835],[21.242,-7.2835],[21.789,-7.2835],[21.8435,-7.374],[21.8345,-7.4085],[21.8605,-7.4475],[21.842,-7.5305],[21.8525,-7.6045],[21.7755,-7.7735],[21.7835,-7.829],[21.776,-7.9085],[21.7525,-7.945],[21.7745,-8.025],[21.8235,-8.0705],[21.8915,-8.253],[21.8925,-8.293],[21.925,-8.307],[21.9225,-8.397],[21.9485,-8.442],[21.9525,-8.504],[21.9245,-8.567],[21.892,-8.7],[21.8965,-8.7835],[21.8525,-8.949],[21.8345,-9.0605],[21.8395,-9.1305],[21.862,-9.1945],[21.81,-9.3295],[21.791,-9.4075],[21.8315,-9.464],[21.8335,-9.51],[21.8645,-9.5595],[21.86,-9.5945],[21.935,-9.707],[22.0015,-9.762],[22.013,-9.82],[22.0695,-9.875],[22.1455,-9.886],[22.1915,-9.933],[22.193,-9.993],[22.2155,-10.0535],[22.215,-10.1585],[22.235,-10.2165],[22.275,-10.2575],[22.324,-10.383],[22.2715,-10.5075],[22.307,-10.531],[22.323,-10.719],[22.3075,-10.7795],[22.2675,-10.785],[22.209,-10.819],[22.1675,-10.8635],[22.203,-10.9305],[22.2175,-11.1125],[22.2685,-11.1805],[22.254,-11.213],[22.271,-11.263],[22.344,-11.2025],[22.346,-11.1825],[22.4385,-11.177],[22.5025,-11.112],[22.489,-11.0645],[22.5775,-11.028],[22.6275,-11.047],[22.647,-11.079],[22.71,-11.1135],[22.7735,-11.1185],[22.8515,-11.065],[22.9065,-11.0865],[23.041,-11.0905],[23.08,-11.113],[23.1515,-11.0815],[23.196,-11.108],[23.2555,-11.0715],[23.299,-11.0215],[23.3975,-10.9605],[23.484,-10.9385],[23.5315,-10.9455],[23.597,-10.9865],[23.6685,-11.003],[23.778,-10.994],[23.863,-11.0355],[23.8975,-10.9765],[23.9615,-10.935],[24.0,-10.89],[24.065,-10.8885],[24.1325,-10.9245],[24.144,-11.031],[24.186,-11.0205],[24.256,-11.045],[24.302,-11.0415],[24.3795,-11.085],[24.4015,-11.1995],[24.4015,-11.2645],[24.3415,-11.367],[24.3645,-11.4005],[24.42,-11.427],[24.4385,-11.4665],[24.5275,-11.465],[24.5915,-11.42],[24.6415,-11.401],[24.672,-11.3525],[24.7505,-11.303],[24.7945,-11.31],[24.8635,-11.2915],[24.873,-11.2655],[25.028,-11.245],[25.06,-11.259],[25.1455,-11.2375],[25.205,-11.236],[25.318,-11.1885],[25.354,-11.201],[25.2945,-11.301],[25.2835,-11.397],[25.306,-11.4335],[25.3135,-11.518],[25.3065,-11.565],[25.332,-11.627],[25.4955,-11.7145],[25.5055,-11.7915],[25.656,-11.733],[25.671,-11.7755],[25.721,-11.817],[25.8305,-11.8295],[25.887,-11.8085],[25.891,-11.852],[25.927,-11.886],[25.968,-11.8825],[26.048,-11.9365],[26.1855,-11.938],[26.2065,-11.921],[26.303,-11.9475],[26.3125,-11.9635],[26.4235,-11.9115],[26.479,-11.9195],[26.5,-11.952],[26.576,-11.9855],[26.6905,-11.993],[26.709,-12.0065],[26.776,-11.956],[26.8195,-11.987],[26.891,-11.9775],[26.9155,-11.9375],[26.961,-11.916],[26.996,-11.8285],[27.0065,-11.745],[27.0475,-11.707],[27.016,-11.618],[27.0535,-11.593],[27.1605,-11.601],[27.2075,-11.5685],[27.2355,-11.685],[27.219,-11.746],[27.231,-11.787],[27.287,-11.8035],[27.3955,-11.8725],[27.4765,-11.9545],[27.4665,-12.044],[27.496,-12.0565],[27.5245,-12.1615],[27.615,-12.241],[27.65,-12.286],[27.7685,-12.2745],[27.823,-12.2275],[27.932,-12.2425],[27.961,-12.3145],[28.043,-12.3555],[28.083,-12.3515],[28.122,-12.438],[28.1695,-12.378],[28.194,-12.4045],[28.3395,-12.4315],[28.399,-12.5005],[28.4385,-12.514],[28.4455,-12.563],[28.4925,-12.5885],[28.534,-12.635],[28.5085,-12.6895],[28.467,-12.69],[28.4515,-12.722],[28.543,-12.8235],[28.543,-12.868],[28.5805,-12.899],[28.609,-12.8335],[28.654,-12.8145],[28.7125,-12.866],[28.754,-12.9415],[28.781,-12.9375],[28.8255,-13.005],[28.8235,-13.08],[28.8475,-13.111],[28.8475,-13.1555],[28.8905,-13.154],[28.918,-13.1905],[28.922,-13.2345],[28.959,-13.334],[28.9625,-13.3765],[29.0055,-13.423],[29.0895,-13.3795],[29.1435,-13.379],[29.183,-13.4415],[29.3615,-13.3255],[29.4345,-13.315],[29.53,-13.229],[29.576,-13.2055],[29.678,-13.262],[29.6465,-13.2945],[29.6515,-13.347],[29.613,-13.4035],[29.7265,-13.459],[29.7645,-13.4355],[29.815,-13.4445],[29.815,-13.097],[29.815,-12.4565],[29.815,-12.15],[29.7455,-12.1675],[29.721,-12.2185],[29.6475,-12.2345],[29.5985,-12.1955],[29.562,-12.2025],[29.479,-12.254],[29.459,-12.3095],[29.4815,-12.388],[29.53,-12.422],[29.4795,-12.46],[29.3825,-12.415],[29.3465,-12.421],[29.2695,-12.3605],[29.2395,-12.379],[29.177,-12.3725],[29.1205,-12.395],[29.0405,-12.3835],[29.0095,-12.2935],[28.9175,-12.1725],[28.8545,-12.135],[28.807,-12.067],[28.7775,-12.0475],[28.765,-11.9935],[28.719,-11.9905],[28.634,-11.9415],[28.61,-11.9065],[28.5755,-11.911],[28.5135,-11.877],[28.493,-11.845],[28.4355,-11.816],[28.4555,-11.775],[28.4305,-11.73],[28.434,-11.6515],[28.386,-11.5875],[28.396,-11.4755],[28.456,-11.3715],[28.457,-11.2925],[28.49,-11.238],[28.4825,-11.1975],[28.5,-11.147],[28.4765,-11.1035],[28.5125,-11.0205],[28.5105,-10.97],[28.5405,-10.9475],[28.544,-10.869],[28.562,-10.8215],[28.6255,-10.7155],[28.695,-10.6765],[28.6585,-10.6265],[28.675,-10.5685],[28.63,-10.5185],[28.644,-10.425],[28.6185,-10.3645],[28.636,-10.3105],[28.577,-10.221],[28.627,-10.161],[28.629,-10.066],[28.644,-10.02],[28.6215,-9.9935],[28.626,-9.9475],[28.6595,-9.883],[28.6755,-9.812],[28.6685,-9.751],[28.636,-9.747],[28.6055,-9.619],[28.6095,-9.589],[28.57,-9.545],[28.574,-9.493],[28.5115,-9.44],[28.518,-9.3545],[28.431,-9.324],[28.379,-9.2935],[28.3735,-9.2555],[28.3995,-9.226],[28.512,-9.1705],[28.6695,-9.058],[28.734,-9.0],[28.8865,-8.8135],[28.956,-8.686],[28.962,-8.6105],[28.9365,-8.545],[28.991,-8.462],[29.56,-8.381],[29.6255,-8.415],[29.702,-8.3695],[30.429,-8.275],[30.4985,-8.2965],[30.537,-8.2795],[30.567,-8.2975],[30.796,-8.2765]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/6f/Flag_of_the_Democratic_Republic_of_the_Congo.svg","name:en":"Democratic Republic of the Congo","wikidata":"Q974","ISO3166-1:alpha2":"CD","ISO3166-1:alpha3":"COD","ISO3166-1:numeric":"180"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[29.3755,-22.1955],[29.288,-22.1215],[29.264,-22.0715],[29.139,-22.072],[29.075,-22.042],[29.0405,-22.0035],[29.0225,-21.904],[29.065,-21.804],[28.861,-21.755],[28.7475,-21.701],[28.6645,-21.6735],[28.5825,-21.6305],[28.5035,-21.669],[28.3665,-21.603],[28.2925,-21.5895],[28.199,-21.6035],[28.0995,-21.5785],[28.022,-21.576],[27.921,-21.335],[27.8055,-21.176],[27.7515,-21.1545],[27.693,-21.0875],[27.7045,-21.037],[27.6895,-20.9335],[27.7275,-20.7975],[27.7325,-20.706],[27.7035,-20.6575],[27.7025,-20.604],[27.73,-20.523],[27.6955,-20.4905],[27.622,-20.4795],[27.5765,-20.4945],[27.4715,-20.468],[27.434,-20.4825],[27.3625,-20.4685],[27.2885,-20.499],[27.3045,-20.331],[27.285,-20.232],[27.217,-20.0935],[27.1435,-20.0795],[27.046,-20.011],[26.9805,-20.015],[26.8515,-19.962],[26.7265,-19.94],[26.703,-19.8875],[26.622,-19.883],[26.583,-19.804],[26.5335,-19.77],[26.445,-19.751],[26.388,-19.6695],[26.3285,-19.6515],[26.3585,-19.615],[26.3125,-19.576],[26.2465,-19.585],[26.1655,-19.5385],[26.142,-19.438],[26.0435,-19.2365],[26.042,-19.2105],[25.975,-19.1315],[25.967,-19.0635],[25.9965,-19.0315],[25.985,-18.9735],[25.952,-18.913],[25.8255,-18.8375],[25.7905,-18.72],[25.803,-18.687],[25.7825,-18.6245],[25.692,-18.573],[25.648,-18.4965],[25.6105,-18.4835],[25.5235,-18.384],[25.492,-18.3],[25.3935,-18.121],[25.319,-18.078],[25.2885,-18.027],[25.2655,-17.9485],[25.2375,-17.9125],[25.263,-17.7915],[25.262,-17.7905],[25.1625,-17.7785],[25.144,-17.808],[25.0835,-17.837],[25.052,-17.808],[25.0075,-17.8385],[24.954,-17.8005],[24.9105,-17.8105],[24.864,-17.8455],[24.732,-17.8925],[24.6965,-17.9445],[24.6655,-17.95],[24.6185,-17.996],[24.579,-18.07],[24.5055,-18.06],[24.482,-18.0105],[24.4265,-17.9535],[24.372,-17.949],[24.294,-18.0305],[24.1925,-18.02],[24.149,-18.075],[24.091,-18.1205],[23.9315,-18.2045],[23.925,-18.2565],[23.8965,-18.263],[23.806,-18.3415],[23.7895,-18.382],[23.722,-18.433],[23.6825,-18.436],[23.6565,-18.4785],[23.616,-18.5025],[23.5725,-18.482],[23.558,-18.336],[23.5205,-18.2695],[23.4105,-18.2035],[23.379,-18.135],[23.324,-18.082],[23.297,-18.001],[23.0985,-18.001],[22.5,-18.1175],[22.048,-18.205],[21.453,-18.3175],[20.999,-18.3175],[20.999,-19.166],[20.999,-19.866],[20.999,-20.5435],[20.999,-21.006],[20.999,-21.746],[20.9985,-22.0005],[20.58,-22.0005],[19.999,-22.0],[19.999,-22.281],[19.999,-23.309],[19.999,-23.863],[19.999,-24.434],[19.999,-24.764],[20.038,-24.8095],[20.0905,-24.833],[20.1155,-24.8805],[20.2035,-24.8965],[20.2655,-24.9285],[20.286,-24.9695],[20.384,-25.0345],[20.4125,-25.0825],[20.4465,-25.199],[20.4965,-25.269],[20.566,-25.395],[20.5985,-25.422],[20.643,-25.4985],[20.631,-25.526],[20.6975,-25.589],[20.6765,-25.6835],[20.7135,-25.7115],[20.7615,-25.7915],[20.7885,-25.803],[20.7875,-25.86],[20.807,-25.926],[20.84,-25.962],[20.817,-26.005],[20.862,-26.1355],[20.8155,-26.192],[20.7895,-26.2715],[20.761,-26.2855],[20.646,-26.435],[20.6175,-26.4445],[20.604,-26.521],[20.6315,-26.5845],[20.635,-26.665],[20.6145,-26.6865],[20.6505,-26.81],[20.676,-26.861],[20.763,-26.8665],[20.818,-26.823],[20.885,-26.7975],[20.978,-26.8115],[21.0065,-26.8445],[21.083,-26.8495],[21.1335,-26.8735],[21.264,-26.837],[21.404,-26.822],[21.486,-26.8415],[21.6045,-26.846],[21.627,-26.8665],[21.6945,-26.8615],[21.78,-26.8025],[21.7935,-26.7705],[21.7875,-26.676],[21.8235,-26.6595],[21.953,-26.6685],[22.0485,-26.635],[22.0775,-26.5815],[22.166,-26.504],[22.201,-26.4205],[22.1935,-26.395],[22.2475,-26.347],[22.367,-26.319],[22.422,-26.223],[22.4745,-26.2],[22.5495,-26.226],[22.5795,-26.157],[22.673,-26.0735],[22.669,-26.0145],[22.721,-26.0185],[22.723,-25.9085],[22.7695,-25.733],[22.8,-25.723],[22.8535,-25.6115],[22.814,-25.583],[22.8525,-25.5355],[22.864,-25.4795],[22.91,-25.4225],[22.9675,-25.3835],[22.978,-25.34],[23.1135,-25.3035],[23.1365,-25.317],[23.1925,-25.2775],[23.274,-25.2745],[23.294,-25.294],[23.3605,-25.286],[23.42,-25.3135],[23.4415,-25.2775],[23.5945,-25.397],[23.677,-25.427],[23.759,-25.469],[23.7615,-25.5005],[23.817,-25.51],[23.8435,-25.5515],[23.929,-25.638],[23.9665,-25.615],[24.003,-25.648],[24.066,-25.6505],[24.1065,-25.63],[24.1955,-25.622],[24.2885,-25.7255],[24.379,-25.765],[24.4485,-25.734],[24.606,-25.799],[24.678,-25.819],[24.713,-25.8055],[24.7935,-25.8255],[24.912,-25.798],[25.038,-25.7215],[25.1695,-25.7665],[25.199,-25.76],[25.337,-25.771],[25.514,-25.684],[25.5815,-25.638],[25.6635,-25.468],[25.6905,-25.3745],[25.7015,-25.289],[25.7315,-25.249],[25.8615,-24.9225],[25.8885,-24.882],[25.859,-24.7555],[25.956,-24.7475],[26.009,-24.7235],[26.1835,-24.688],[26.3935,-24.637],[26.4705,-24.5905],[26.5145,-24.485],[26.6015,-24.41],[26.665,-24.3795],[26.704,-24.322],[26.8,-24.3],[26.866,-24.2485],[26.8805,-24.197],[26.863,-24.0995],[26.909,-24.029],[26.928,-23.9225],[26.955,-23.874],[26.9415,-23.8435],[26.9645,-23.7775],[26.9715,-23.703],[26.9995,-23.653],[27.071,-23.67],[27.064,-23.627],[27.108,-23.5705],[27.148,-23.5515],[27.1925,-23.4985],[27.3035,-23.4625],[27.327,-23.4045],[27.4015,-23.4115],[27.462,-23.384],[27.5285,-23.3855],[27.564,-23.3485],[27.573,-23.266],[27.6045,-23.216],[27.7145,-23.2285],[27.784,-23.1735],[27.7825,-23.143],[27.9195,-23.0685],[27.944,-23.031],[27.935,-22.963],[28.0365,-22.914],[28.0395,-22.841],[28.11,-22.801],[28.163,-22.7555],[28.1585,-22.7215],[28.213,-22.6635],[28.2685,-22.6385],[28.345,-22.5715],[28.48,-22.571],[28.5195,-22.5875],[28.566,-22.5595],[28.6425,-22.5575],[28.709,-22.515],[28.8325,-22.4875],[28.869,-22.4475],[28.922,-22.458],[28.971,-22.378],[28.964,-22.319],[29.0375,-22.217],[29.1495,-22.216],[29.2175,-22.1785],[29.3755,-22.1955]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fa/Flag_of_Botswana.svg","name:en":"Botswana","wikidata":"Q963","ISO3166-1:alpha2":"BW","ISO3166-1:alpha3":"BWA","ISO3166-1:numeric":"072"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[124.273,-9.419],[124.2775,-9.5025],[124.3415,-9.4825],[124.3515,-9.426],[124.404,-9.34],[124.462,-9.2995],[124.475,-9.1655],[124.433,-9.113],[124.3295,-9.117],[124.155,-9.178],[124.048,-9.231],[124.0415,-9.34],[124.1005,-9.41],[124.133,-9.4255],[124.218,-9.3665],[124.273,-9.419]]],[[[126.7625,-8.232],[126.147,-8.2895],[125.883,-8.314],[125.8275,-8.251],[125.696,-8.101],[125.6725,-8.0895],[125.34,-8.2325],[125.3215,-8.2915],[125.24,-8.444],[125.098,-8.519],[124.936,-8.7415],[124.9525,-8.9665],[124.9345,-8.995],[124.941,-9.047],[124.9845,-9.066],[125.0895,-9.011],[125.1135,-8.9655],[125.1855,-9.0265],[125.1715,-9.126],[125.176,-9.1725],[125.098,-9.2005],[125.0745,-9.1765],[125.014,-9.171],[124.983,-9.191],[124.9825,-9.2395],[125.0065,-9.295],[125.0795,-9.3925],[125.087,-9.463],[125.24,-9.5645],[125.584,-9.4345],[126.065,-9.2445],[126.6525,-9.0545],[127.179,-8.8095],[127.4375,-8.5945],[127.5335,-8.419],[127.397,-8.2785],[127.189,-8.2235],[126.7995,-8.2285],[126.7625,-8.232]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/26/Flag_of_East_Timor.svg","name:en":"East Timor","wikidata":"Q574","ISO3166-1:alpha2":"TL","ISO3166-1:alpha3":"TLS","ISO3166-1:numeric":"626"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[8.388,4.3675],[8.428,4.3185],[8.538,4.2855],[8.6185,4.2845],[8.7475,4.178],[8.792,4.021],[8.8315,3.9605],[8.9835,3.906],[9.047,3.856],[9.1255,3.771],[9.2875,3.7155],[9.414,3.6575],[9.438,3.618],[9.434,3.5195],[9.4575,3.459],[9.498,3.4065],[9.5905,3.3375],[9.699,3.2265],[9.754,3.0955],[9.716,3.0125],[9.6905,2.905],[9.6845,2.82],[9.6535,2.7225],[9.6265,2.5995],[9.621,2.45],[9.762,2.3905],[9.8375,2.325],[9.848,2.2455],[9.906,2.2075],[10.035,2.163],[10.167,2.152],[10.19,2.175],[10.656,2.174],[11.0,2.171],[11.36,2.172],[11.352,2.2225],[11.3965,2.3025],[11.61,2.2955],[11.7015,2.319],[11.754,2.271],[11.8695,2.2845],[12.015,2.2905],[12.156,2.2765],[12.2075,2.282],[12.249,2.2565],[12.304,2.2905],[12.3665,2.2675],[12.4545,2.252],[12.528,2.253],[12.5745,2.2705],[12.6415,2.24],[12.6835,2.244],[12.7515,2.2245],[12.8185,2.2525],[12.8615,2.2275],[12.9975,2.252],[13.0535,2.2395],[13.157,2.286],[13.2195,2.261],[13.248,2.2735],[13.3055,2.207],[13.294,2.1705],[13.8195,2.172],[14.292,2.172],[14.3555,2.1855],[14.413,2.1815],[14.443,2.1575],[14.572,2.179],[14.595,2.202],[14.644,2.1325],[14.718,2.1275],[14.808,2.0595],[14.8565,2.1015],[14.911,2.064],[14.8935,2.0155],[14.9685,2.008],[14.979,2.0355],[15.045,1.9795],[15.105,1.983],[15.122,2.014],[15.2015,2.0385],[15.2495,2.028],[15.305,1.969],[15.3035,1.9425],[15.347,1.912],[15.4855,1.9835],[15.616,1.9385],[15.7475,1.92],[15.8055,1.856],[15.8685,1.8285],[15.8855,1.7905],[15.96,1.7665],[16.021,1.766],[16.019,1.6785],[16.0575,1.652],[16.0825,1.6905],[16.145,1.6905],[16.134,1.8045],[16.0835,1.8995],[16.0515,2.0045],[16.0835,2.0475],[16.0815,2.1905],[16.184,2.2055],[16.1885,2.227],[16.144,2.3525],[16.078,2.4485],[16.0655,2.5295],[16.083,2.5965],[16.066,2.641],[16.0735,2.7095],[16.056,3.0205],[15.7755,3.27],[15.7335,3.2435],[15.3655,3.6795],[15.0745,4.0205],[15.135,4.037],[15.1445,4.068],[15.108,4.121],[15.0825,4.3085],[15.028,4.367],[15.014,4.415],[14.8875,4.4695],[14.871,4.4965],[14.81,4.5275],[14.73,4.6115],[14.7185,4.6455],[14.7125,4.868],[14.687,4.9245],[14.691,4.971],[14.673,5.0865],[14.6935,5.099],[14.651,5.215],[14.5675,5.239],[14.528,5.2745],[14.572,5.3825],[14.5995,5.416],[14.6025,5.4655],[14.626,5.518],[14.591,5.608],[14.6265,5.711],[14.6195,5.888],[14.603,5.921],[14.549,5.905],[14.485,5.92],[14.4165,6.0395],[14.43,6.0885],[14.553,6.1925],[14.5805,6.189],[14.7405,6.2625],[14.805,6.3465],[14.797,6.39],[14.8255,6.431],[14.966,6.7565],[15.0425,6.7615],[15.0645,6.788],[15.1195,6.9255],[15.1585,7.0585],[15.219,7.115],[15.211,7.224],[15.243,7.2675],[15.422,7.4175],[15.4935,7.525],[15.567,7.5945],[15.5855,7.668],[15.5875,7.774],[15.5065,7.792],[15.452,7.8855],[15.394,8.0465],[15.395,8.0935],[15.2,8.5125],[15.154,8.572],[15.151,8.606],[15.109,8.6595],[15.06,8.681],[14.9825,8.689],[14.989,8.7285],[14.9225,8.7785],[14.9,8.8135],[14.844,8.812],[14.745,8.8795],[14.5665,9.014],[14.4905,9.058],[14.464,9.115],[14.407,9.148],[14.3475,9.2085],[14.373,9.294],[14.339,9.31],[14.3045,9.3715],[14.2685,9.358],[14.204,9.4465],[14.15,9.5025],[14.1045,9.518],[14.0925,9.5505],[14.04,9.602],[13.9765,9.638],[14.0225,9.728],[14.131,9.82],[14.1735,9.904],[14.2065,10.002],[14.4635,10.002],[14.8025,9.935],[14.958,9.983],[15.033,9.9655],[15.06,9.9455],[15.1485,9.9945],[15.248,9.9895],[15.3645,9.959],[15.414,9.9335],[15.5235,9.952],[15.6885,9.993],[15.617,10.0455],[15.502,10.099],[15.4405,10.1975],[15.3455,10.298],[15.304,10.312],[15.2795,10.4055],[15.216,10.498],[15.1555,10.529],[15.1425,10.592],[15.161,10.626],[15.138,10.6865],[15.096,10.748],[15.0675,10.8235],[15.095,10.876],[15.079,10.958],[15.046,11.004],[15.092,11.0335],[15.095,11.168],[15.068,11.2015],[15.0485,11.2805],[15.0735,11.3265],[15.058,11.4145],[15.1445,11.5625],[15.102,11.5845],[15.0635,11.712],[15.122,11.7885],[15.089,11.855],[15.0495,11.8585],[15.0395,11.9105],[15.085,12.002],[15.051,12.0145],[15.0605,12.06],[15.038,12.104],[14.9935,12.088],[14.956,12.0995],[14.8855,12.165],[14.9095,12.1915],[14.8935,12.3075],[14.9085,12.3805],[14.848,12.518],[14.8545,12.576],[14.823,12.64],[14.772,12.635],[14.7615,12.6775],[14.7095,12.7265],[14.669,12.7195],[14.5945,12.746],[14.547,12.7925],[14.565,12.8365],[14.5605,12.9205],[14.535,12.946],[14.4665,13.0835],[14.0835,13.0835],[14.2035,12.538],[14.1775,12.481],[14.1895,12.405],[14.2225,12.362],[14.334,12.3735],[14.3765,12.3595],[14.439,12.3675],[14.4825,12.3525],[14.5255,12.2915],[14.591,12.2295],[14.6175,12.186],[14.659,12.198],[14.656,12.1385],[14.624,12.0495],[14.6445,11.9725],[14.643,11.912],[14.6045,11.8695],[14.6065,11.799],[14.5555,11.7055],[14.6,11.698],[14.644,11.65],[14.647,11.572],[14.618,11.51],[14.5175,11.468],[14.4715,11.428],[14.4325,11.4225],[14.3645,11.3595],[14.239,11.296],[14.1785,11.24],[13.976,11.311],[13.9345,11.2065],[13.856,11.0955],[13.815,11.0645],[13.7895,11.003],[13.7355,11.006],[13.7115,10.9635],[13.73,10.924],[13.717,10.863],[13.6505,10.813],[13.6265,10.7115],[13.5885,10.698],[13.5745,10.635],[13.547,10.6125],[13.5785,10.537],[13.531,10.4575],[13.535,10.4],[13.5005,10.3905],[13.5015,10.3375],[13.466,10.246],[13.4835,10.196],[13.47,10.161],[13.412,10.1215],[13.3395,10.114],[13.299,10.087],[13.2865,10.046],[13.2475,10.0055],[13.288,9.9795],[13.265,9.927],[13.237,9.911],[13.249,9.8575],[13.302,9.827],[13.2625,9.78],[13.234,9.614],[13.2095,9.557],[13.1245,9.5265],[13.0345,9.506],[12.9755,9.4645],[12.9455,9.422],[12.8785,9.394],[12.9025,9.3545],[12.9175,9.2485],[12.888,9.1775],[12.901,9.114],[12.823,8.9715],[12.8,8.8565],[12.819,8.8305],[12.784,8.7455],[12.7205,8.763],[12.6985,8.669],[12.575,8.614],[12.4875,8.643],[12.4465,8.6025],[12.4615,8.537],[12.342,8.4585],[12.3135,8.4255],[12.2675,8.448],[12.244,8.3865],[12.249,8.216],[12.256,8.1765],[12.1925,8.108],[12.201,8.009],[12.214,7.9835],[12.136,7.862],[12.098,7.8475],[12.0525,7.7355],[11.9965,7.667],[12.0115,7.6305],[12.005,7.5805],[12.019,7.5185],[11.9315,7.4865],[11.928,7.394],[11.879,7.3475],[11.847,7.2545],[11.8565,7.1685],[11.8805,7.108],[11.863,7.0775],[11.8025,7.0825],[11.779,7.046],[11.7375,7.05],[11.631,6.9895],[11.6035,6.909],[11.573,6.8995],[11.5795,6.8415],[11.5695,6.773],[11.585,6.7375],[11.5465,6.701],[11.5175,6.613],[11.462,6.611],[11.4275,6.579],[11.422,6.532],[11.316,6.5065],[11.28,6.538],[11.222,6.5395],[11.167,6.5],[11.097,6.5205],[11.0965,6.6795],[11.0305,6.7145],[10.9925,6.687],[10.9455,6.687],[10.916,6.7095],[10.914,6.7575],[10.8395,6.811],[10.8145,6.8535],[10.8415,6.9305],[10.7985,6.9625],[10.7665,6.9555],[10.7205,6.9885],[10.6795,7.0385],[10.6265,7.049],[10.5955,7.0785],[10.595,7.1475],[10.5715,7.162],[10.56,7.032],[10.5415,6.9395],[10.4815,6.9015],[10.462,6.916],[10.2155,6.8895],[10.172,6.942],[10.181,6.968],[10.1505,7.0385],[10.0125,6.904],[9.996,6.898],[9.863,6.776],[9.774,6.7845],[9.751,6.6525],[9.706,6.512],[9.5955,6.5285],[9.587,6.4735],[9.529,6.4425],[9.4675,6.456],[9.431,6.3155],[9.347,6.3535],[9.3345,6.292],[9.307,6.263],[9.2645,6.1815],[9.2115,6.1685],[9.169,6.1335],[9.1535,6.094],[9.0545,6.0005],[8.8605,5.845],[8.8505,5.805],[8.884,5.775],[8.842,5.6795],[8.904,5.619],[8.9215,5.564],[8.8335,5.4305],[8.8415,5.3915],[8.8145,5.284],[8.821,5.1845],[8.7835,5.113],[8.745,5.098],[8.724,5.041],[8.6955,5.013],[8.653,4.9175],[8.6205,4.9055],[8.607,4.8645],[8.6255,4.821],[8.544,4.8015],[8.526,4.737],[8.539,4.702],[8.4525,4.6165],[8.4075,4.5335],[8.403,4.442],[8.388,4.3675]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/4/4f/Flag_of_Cameroon.svg","name:en":"Cameroon","wikidata":"Q1009","ISO3166-1:alpha2":"CM","ISO3166-1:alpha3":"CMR","ISO3166-1:numeric":"120"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[24.256,8.687],[24.1685,8.6985],[24.127,8.6855],[23.926,8.7185],[23.8835,8.708],[23.8305,8.727],[23.734,8.705],[23.597,8.7345],[23.5585,8.705],[23.5265,8.7085],[23.4975,8.773],[23.509,8.812],[23.5495,8.847],[23.5805,8.9015],[23.583,8.9945],[23.5335,8.9605],[23.4575,8.9905],[23.4715,9.1245],[23.4935,9.174],[23.5515,9.1805],[23.65,9.2755],[23.6405,9.346],[23.666,9.436],[23.632,9.4495],[23.6265,9.547],[23.6965,9.6715],[23.6695,9.867],[23.3065,10.459],[23.252,10.493],[23.151,10.6005],[23.013,10.6955],[22.8905,10.8825],[22.878,10.9205],[22.7855,10.931],[22.7675,10.955],[22.653,10.966],[22.6005,10.9855],[22.529,10.9765],[22.466,11.0015],[22.415,10.963],[22.323,10.9415],[22.271,10.9035],[22.2505,10.912],[22.202,10.8705],[22.176,10.814],[22.113,10.8325],[22.0475,10.8305],[22.015,10.808],[22.0085,10.7515],[21.9075,10.714],[21.8615,10.669],[21.811,10.677],[21.7165,10.6355],[21.7015,10.5145],[21.748,10.4065],[21.7245,10.366],[21.7155,10.2905],[21.688,10.2845],[21.667,10.236],[21.6145,10.2135],[21.5355,10.212],[21.4735,10.148],[21.432,10.038],[21.342,9.9585],[21.2765,9.9805],[21.225,9.9425],[21.1885,9.8805],[21.1155,9.822],[21.103,9.7725],[21.015,9.7385],[20.986,9.612],[20.9565,9.6055],[20.896,9.5215],[20.826,9.458],[20.831,9.4285],[20.7745,9.4055],[20.738,9.3705],[20.678,9.3595],[20.668,9.2985],[20.6035,9.298],[20.538,9.3185],[20.498,9.277],[20.488,9.2095],[20.434,9.137],[20.3715,9.1085],[20.2535,9.115],[20.207,9.1395],[20.147,9.1115],[20.062,9.1345],[20.0165,9.093],[19.8785,9.0525],[19.7865,9.048],[19.684,9.0135],[19.5715,9.0295],[19.51,9.0185],[19.2635,9.016],[19.1775,9.027],[19.147,9.0095],[19.0725,9.015],[18.978,8.9405],[18.9115,8.9235],[18.8615,8.878],[18.907,8.8505],[19.0335,8.7315],[19.1005,8.7005],[19.1065,8.664],[19.0465,8.5935],[19.039,8.5375],[18.9295,8.4375],[18.919,8.387],[18.8495,8.3455],[18.8335,8.29],[18.7865,8.2545],[18.6735,8.212],[18.63,8.1465],[18.637,8.091],[18.6015,8.0465],[18.192,8.018],[18.032,8.0095],[17.9215,7.9585],[17.6845,7.983],[17.606,7.9295],[17.535,7.925],[17.5105,7.8725],[17.4725,7.9],[17.36,7.856],[17.2765,7.8105],[17.258,7.753],[17.208,7.7495],[17.1675,7.7185],[17.1525,7.67],[17.106,7.695],[17.0355,7.639],[16.9525,7.645],[16.9015,7.6035],[16.86,7.545],[16.809,7.5375],[16.776,7.607],[16.73,7.639],[16.6845,7.6395],[16.66,7.672],[16.6615,7.742],[16.5895,7.774],[16.577,7.874],[16.506,7.8455],[16.479,7.7895],[16.432,7.81],[16.407,7.7705],[16.4235,7.7155],[16.398,7.683],[16.296,7.6545],[16.2475,7.612],[16.1855,7.6145],[16.1635,7.5945],[16.0565,7.5775],[16.039,7.534],[15.975,7.4835],[15.8095,7.441],[15.743,7.4785],[15.728,7.519],[15.5465,7.511],[15.4935,7.525],[15.422,7.4175],[15.243,7.2675],[15.211,7.224],[15.219,7.115],[15.1585,7.0585],[15.1195,6.9255],[15.0645,6.788],[15.0425,6.7615],[14.966,6.7565],[14.8255,6.431],[14.797,6.39],[14.805,6.3465],[14.7405,6.2625],[14.5805,6.189],[14.553,6.1925],[14.43,6.0885],[14.4165,6.0395],[14.485,5.92],[14.549,5.905],[14.603,5.921],[14.6195,5.888],[14.6265,5.711],[14.591,5.608],[14.626,5.518],[14.6025,5.4655],[14.5995,5.416],[14.572,5.3825],[14.528,5.2745],[14.5675,5.239],[14.651,5.215],[14.6935,5.099],[14.673,5.0865],[14.691,4.971],[14.687,4.9245],[14.7125,4.868],[14.7185,4.6455],[14.73,4.6115],[14.81,4.5275],[14.871,4.4965],[14.8875,4.4695],[15.014,4.415],[15.028,4.367],[15.0825,4.3085],[15.108,4.121],[15.1445,4.068],[15.135,4.037],[15.0745,4.0205],[15.3655,3.6795],[15.7335,3.2435],[15.7755,3.27],[16.056,3.0205],[16.0735,2.7095],[16.066,2.641],[16.083,2.5965],[16.0655,2.5295],[16.078,2.4485],[16.144,2.3525],[16.1885,2.227],[16.184,2.2055],[16.207,2.221],[16.499,2.843],[16.496,2.8845],[16.466,2.9225],[16.469,2.981],[16.512,3.0605],[16.4825,3.132],[16.484,3.162],[16.5355,3.2485],[16.529,3.2755],[16.561,3.339],[16.572,3.444],[16.589,3.482],[16.6595,3.5335],[16.767,3.55],[16.7975,3.526],[16.862,3.5245],[16.8765,3.566],[16.9605,3.556],[16.988,3.5355],[17.0335,3.5405],[17.0715,3.5755],[17.234,3.5845],[17.2565,3.617],[17.3035,3.6265],[17.3605,3.618],[17.414,3.6795],[17.4595,3.7085],[17.5025,3.706],[17.564,3.6535],[17.6155,3.6295],[17.723,3.6265],[17.834,3.6105],[17.854,3.5375],[17.9475,3.573],[17.995,3.5395],[18.0435,3.566],[18.1195,3.5605],[18.16,3.533],[18.178,3.4835],[18.222,3.4905],[18.265,3.577],[18.4425,3.5955],[18.4715,3.641],[18.5335,3.599],[18.594,3.485],[18.626,3.471],[18.608,3.518],[18.6045,3.617],[18.5785,3.777],[18.5935,3.818],[18.6095,3.925],[18.6475,3.95],[18.659,3.986],[18.6465,4.0615],[18.545,4.321],[18.603,4.371],[18.621,4.3565],[18.7175,4.369],[18.786,4.4275],[18.806,4.468],[18.8195,4.5545],[18.8365,4.587],[18.9215,4.6525],[18.9385,4.701],[19.0195,4.767],[19.107,4.941],[19.197,4.9415],[19.223,4.995],[19.362,5.0735],[19.3705,5.0965],[19.437,5.1325],[19.4805,5.122],[19.5285,5.1495],[19.613,5.13],[19.6795,5.1465],[19.7215,5.1265],[19.8195,5.1075],[19.863,5.033],[19.9135,4.9825],[20.0075,4.9715],[20.1065,4.9025],[20.173,4.892],[20.254,4.789],[20.352,4.7665],[20.3855,4.7335],[20.4075,4.663],[20.4415,4.646],[20.464,4.596],[20.447,4.567],[20.4655,4.524],[20.5215,4.4565],[20.5805,4.4125],[20.7095,4.433],[20.797,4.423],[20.8415,4.4545],[20.9485,4.446],[21.0765,4.4],[21.116,4.34],[21.2265,4.293],[21.282,4.345],[21.3415,4.2935],[21.387,4.281],[21.429,4.293],[21.478,4.2645],[21.5585,4.253],[21.6485,4.3125],[21.737,4.312],[21.8095,4.287],[21.859,4.2465],[21.949,4.2335],[21.9965,4.2455],[22.0885,4.218],[22.165,4.155],[22.2585,4.115],[22.3815,4.121],[22.4525,4.1345],[22.546,4.214],[22.544,4.288],[22.575,4.344],[22.596,4.48],[22.62,4.4915],[22.6895,4.4695],[22.731,4.5565],[22.7355,4.6305],[22.782,4.7145],[22.816,4.727],[22.842,4.6995],[22.893,4.75],[22.8995,4.815],[22.977,4.834],[23.01,4.757],[23.1105,4.7115],[23.157,4.7395],[23.196,4.723],[23.2305,4.6755],[23.266,4.6665],[23.3285,4.608],[23.429,4.599],[23.427,4.6495],[23.4615,4.6785],[23.5025,4.6775],[23.5945,4.762],[23.676,4.765],[23.7125,4.7865],[23.7665,4.7835],[23.828,4.8325],[23.891,4.833],[23.956,4.81],[23.962,4.875],[24.0485,4.867],[24.111,4.9185],[24.136,4.895],[24.2155,4.954],[24.2665,4.969],[24.282,4.9965],[24.3555,5.006],[24.393,5.0295],[24.363,5.0725],[24.4095,5.102],[24.432,5.0635],[24.4695,5.1045],[24.534,5.083],[24.5685,5.033],[24.625,5.013],[24.6305,4.977],[24.672,4.9675],[24.6785,4.908],[24.783,4.8965],[24.8,4.9265],[24.9115,4.952],[24.962,4.9875],[24.996,4.9585],[25.0645,4.928],[25.086,4.9775],[25.2485,5.026],[25.312,5.031],[25.344,5.11],[25.308,5.191],[25.335,5.261],[25.4425,5.337],[25.5,5.344],[25.579,5.3785],[25.604,5.339],[25.7075,5.301],[25.752,5.244],[25.781,5.259],[25.8265,5.2015],[25.9485,5.2045],[25.9805,5.247],[26.0615,5.1995],[26.093,5.246],[26.1575,5.2615],[26.214,5.204],[26.244,5.2025],[26.2935,5.1635],[26.386,5.1515],[26.4535,5.0945],[26.451,5.068],[26.515,5.0485],[26.571,5.083],[26.6385,5.0935],[26.6595,5.08],[26.747,5.109],[26.862,5.038],[26.88,5.0485],[26.9295,5.1375],[27.022,5.1865],[27.086,5.2085],[27.1355,5.2075],[27.2295,5.153],[27.2625,5.151],[27.4415,5.074],[27.449,5.0195],[27.466,5.0855],[27.4355,5.099],[27.4135,5.148],[27.318,5.2175],[27.3035,5.28],[27.254,5.3235],[27.2345,5.429],[27.2635,5.5185],[27.287,5.54],[27.2815,5.601],[27.2375,5.606],[27.244,5.651],[27.2005,5.744],[27.103,5.8105],[27.0685,5.8005],[26.9875,5.8645],[26.9335,5.865],[26.9195,5.904],[26.829,5.9015],[26.7825,6.0005],[26.7025,6.0175],[26.6615,5.997],[26.5965,6.032],[26.569,6.028],[26.514,6.1155],[26.5305,6.224],[26.4695,6.2235],[26.489,6.2795],[26.3965,6.31],[26.3635,6.352],[26.3,6.3965],[26.32,6.5125],[26.403,6.6175],[26.415,6.6535],[26.345,6.6815],[26.31,6.7105],[26.2655,6.6965],[26.2485,6.739],[26.184,6.757],[26.175,6.8125],[26.109,6.8165],[26.0785,6.85],[26.0585,6.957],[26.0395,7.003],[25.9385,7.0405],[25.895,7.099],[25.841,7.111],[25.8205,7.156],[25.6535,7.205],[25.57,7.238],[25.536,7.278],[25.484,7.27],[25.4055,7.341],[25.369,7.3445],[25.3405,7.426],[25.287,7.47],[25.1975,7.495],[25.1725,7.5745],[25.2305,7.6335],[25.2895,7.652],[25.282,7.7835],[25.2465,7.846],[25.1775,7.9105],[25.0875,7.8955],[25.047,7.9445],[24.969,7.977],[24.9125,8.0315],[24.8805,8.0935],[24.8585,8.1695],[24.8,8.1955],[24.7625,8.1865],[24.7225,8.2105],[24.6345,8.221],[24.539,8.2055],[24.483,8.2345],[24.4705,8.2685],[24.3335,8.2665],[24.2305,8.285],[24.163,8.3315],[24.141,8.3765],[24.1635,8.474],[24.268,8.592],[24.229,8.6305],[24.256,8.687]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/6/6f/Flag_of_the_Central_African_Republic.svg","name:en":"Central African Republic","wikidata":"Q929","ISO3166-1:alpha2":"CF","ISO3166-1:alpha3":"CAF","ISO3166-1:numeric":"140"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[36.5605,14.2575],[36.6335,14.3135],[36.6955,14.3015],[36.744,14.3225],[36.853,14.322],[36.965,14.295],[37.014,14.2515],[37.092,14.2715],[37.1015,14.336],[37.1355,14.4095],[37.2095,14.44],[37.312,14.4475],[37.373,14.3835],[37.404,14.3345],[37.406,14.29],[37.4715,14.251],[37.4675,14.2155],[37.5275,14.1845],[37.9125,14.894],[38.0165,14.746],[38.0565,14.7155],[38.0915,14.7165],[38.1435,14.6755],[38.2325,14.692],[38.2655,14.6705],[38.271,14.617],[38.319,14.557],[38.4295,14.453],[38.4555,14.4135],[38.525,14.4165],[38.596,14.442],[38.647,14.4425],[38.6895,14.4645],[38.7945,14.4665],[38.9005,14.505],[38.936,14.54],[38.9785,14.545],[39.0295,14.637],[39.0995,14.6355],[39.154,14.653],[39.1575,14.6085],[39.2055,14.5655],[39.241,14.563],[39.2635,14.49],[39.234,14.4515],[39.36,14.4835],[39.3705,14.537],[39.4475,14.5055],[39.4895,14.505],[39.5095,14.552],[39.5865,14.609],[39.6435,14.605],[39.713,14.5555],[39.765,14.5485],[39.821,14.4835],[39.8385,14.4905],[39.9305,14.415],[40.0325,14.4785],[40.037,14.5045],[40.0905,14.548],[40.1475,14.5395],[40.213,14.387],[40.2595,14.411],[40.365,14.3665],[40.4515,14.307],[40.5725,14.2695],[40.8105,14.172],[40.9435,14.0805],[40.995,14.0165],[41.1155,13.8275],[41.172,13.728],[41.2505,13.6105],[41.309,13.5795],[41.403,13.501],[41.449,13.4885],[41.503,13.4455],[41.6425,13.385],[41.6685,13.3235],[41.7215,13.2735],[41.738,13.2365],[41.8105,13.1665],[41.8905,13.0745],[41.9665,12.9235],[42.03,12.868],[42.0575,12.801],[42.2195,12.7625],[42.2785,12.6355],[42.404,12.4685],[42.464,12.524],[42.567,12.476],[42.688,12.3625],[42.752,12.385],[42.764,12.4115],[42.825,12.434],[42.7975,12.477],[42.8255,12.541],[42.8595,12.5495],[42.8895,12.617],[42.9195,12.613],[42.9815,12.6495],[43.151,12.7205],[43.298,12.7925],[43.249,12.8675],[43.2125,12.9695],[43.145,13.068],[43.079,13.1335],[42.989,13.2085],[42.9295,13.2365],[42.824,13.2505],[42.8545,13.3585],[42.834,13.4275],[42.7235,13.458],[42.637,13.5975],[42.6275,13.6585],[42.5965,13.741],[42.4965,13.6985],[42.464,13.6685],[42.4235,13.7155],[42.3265,13.879],[42.27,13.928],[42.191,13.9545],[42.163,14.0095],[42.1175,14.0555],[42.061,14.085],[41.9875,14.0955],[41.876,14.1365],[41.858,14.1955],[41.813,14.255],[41.749,14.2945],[41.691,14.3075],[41.6505,14.368],[41.5945,14.4255],[41.523,14.5255],[41.487,14.5995],[41.452,14.644],[41.377,14.7065],[41.3255,14.7745],[41.286,14.801],[41.163,14.848],[41.07,14.8685],[41.019,14.951],[40.969,15.003],[40.862,15.044],[40.751,15.126],[40.6985,15.1545],[40.738,15.194],[40.8645,15.2875],[40.9715,15.319],[41.0175,15.353],[41.062,15.41],[41.082,15.469],[41.079,15.551],[41.0405,15.6275],[41.052,15.692],[41.0405,15.761],[41.005,15.8215],[40.875,15.922],[40.8395,15.968],[40.717,16.064],[40.607,16.102],[40.5285,16.1045],[40.4825,16.092],[40.431,16.154],[40.4515,16.2385],[40.448,16.3825],[40.394,16.4825],[40.3945,16.544],[40.372,16.627],[40.3195,16.6985],[40.2585,16.7355],[40.151,16.752],[40.069,16.734],[40.006,16.733],[39.9015,16.683],[39.8115,16.724],[39.7365,16.725],[39.5655,16.656],[39.5335,16.664],[39.501,16.7345],[39.4125,16.804],[39.322,16.8215],[39.2655,16.959],[39.138,17.4315],[39.0765,17.573],[38.997,17.724],[38.852,17.93],[38.8355,17.983],[38.788,18.071],[38.5825,18.0205],[38.452,17.8995],[38.3445,17.6445],[38.2735,17.603],[38.262,17.5355],[38.2225,17.529],[38.19,17.559],[38.1335,17.5485],[38.137,17.504],[38.0905,17.502],[38.087,17.5495],[38.046,17.548],[37.9685,17.5015],[37.934,17.4585],[37.8955,17.4415],[37.831,17.483],[37.791,17.4685],[37.7445,17.382],[37.5835,17.3485],[37.502,17.3165],[37.502,17.197],[37.396,17.0545],[37.307,17.061],[37.252,17.025],[37.2035,17.0325],[37.158,17.0155],[37.099,17.0525],[37.026,17.077],[36.9885,16.9375],[37.0135,16.9135],[37.009,16.7825],[36.9805,16.706],[36.9165,16.6505],[36.9195,16.593],[36.8985,16.537],[36.906,16.4865],[36.9575,16.419],[36.9505,16.3475],[36.9695,16.3055],[36.9655,16.256],[36.9205,16.2245],[36.7635,15.808],[36.698,15.753],[36.6105,15.437],[36.5485,15.252],[36.4335,15.1675],[36.4605,14.983],[36.5235,14.3645],[36.5415,14.278],[36.5605,14.2575]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/2/29/Flag_of_Eritrea.svg","name:en":"Eritrea","wikidata":"Q986","ISO3166-1:alpha2":"ER","ISO3166-1:alpha3":"ERI","ISO3166-1:numeric":"232"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[33.067,34.617],[33.1515,34.6065],[33.231,34.545],[33.2675,34.4995],[33.47,34.546],[33.7045,34.633],[33.79,34.685],[33.8355,34.74],[33.7595,34.8795],[33.7155,34.9295],[33.7,35.033],[33.7645,35.0395],[33.818,35.0715],[33.86,34.974],[33.921,34.9585],[33.9445,34.9285],[33.9805,34.875],[33.9865,34.766],[34.116,34.754],[34.193,34.773],[34.28,34.831],[34.329,34.92],[34.3295,34.999],[34.306,35.066],[34.2305,35.1765],[34.288,35.238],[34.4485,35.325],[34.7235,35.488],[34.767,35.526],[34.83,35.6205],[34.8545,35.6945],[34.8375,35.786],[34.7955,35.8425],[34.7185,35.8915],[34.657,35.909],[34.5755,35.9115],[34.5135,35.8985],[34.3305,35.839],[34.0765,35.747],[33.937,35.6665],[33.6815,35.604],[33.5535,35.562],[33.302,35.554],[33.156,35.5695],[32.97,35.601],[32.869,35.6015],[32.7935,35.5775],[32.742,35.5445],[32.6955,35.4905],[32.671,35.399],[32.6085,35.397],[32.442,35.3575],[32.363,35.3055],[32.2775,35.317],[32.183,35.3005],[32.115,35.2645],[32.0575,35.2005],[32.032,35.131],[32.0265,35.0095],[32.0895,34.807],[32.1385,34.751],[32.1925,34.651],[32.265,34.5875],[32.4115,34.5125],[32.6285,34.4485],[32.744,34.439],[32.7765,34.604],[32.7545,34.6495],[32.827,34.706],[32.9005,34.665],[32.9435,34.671],[32.9695,34.639],[33.014,34.644],[33.067,34.617]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/d4/Flag_of_Cyprus.svg","name:en":"Cyprus","wikidata":"Q229","ISO3166-1:alpha2":"CY","ISO3166-1:alpha3":"CYP","ISO3166-1:numeric":"196"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[44.8095,39.6275],[44.9035,39.577],[44.91,39.525],[44.952,39.492],[44.964,39.424],[45.0175,39.406],[45.0865,39.351],[45.185,39.2205],[45.2475,39.1855],[45.3135,39.2],[45.3525,39.133],[45.4525,39.055],[45.455,38.9905],[45.6185,38.9425],[45.69,38.9515],[45.737,38.9275],[45.918,38.8735],[45.979,38.879],[46.112,38.865],[46.1405,38.8445],[46.1495,38.8895],[46.1095,38.954],[46.1015,39.016],[46.07,39.078],[46.031,39.0925],[45.9895,39.2005],[45.9985,39.2915],[45.8615,39.3565],[45.7925,39.369],[45.804,39.428],[45.838,39.4685],[45.799,39.5715],[45.6945,39.6035],[45.6235,39.5635],[45.5905,39.57],[45.492,39.5115],[45.342,39.542],[45.243,39.6115],[45.172,39.593],[45.187,39.677],[45.1345,39.7435],[45.061,39.7945],[45.007,39.7505],[44.9425,39.7265],[44.888,39.7485],[44.7635,39.7145],[44.8095,39.6275]]],[[[49.2125,38.4085],[49.3115,38.709],[49.467,39.18],[49.603,39.5885],[49.711,39.904],[49.799,40.064],[49.919,40.144],[50.058,40.1835],[50.9115,40.15],[51.009,40.404],[50.475,40.555],[49.952,40.703],[49.851,40.75],[49.717,40.8455],[49.2285,41.4455],[48.814,41.9505],[48.5925,41.843],[48.5555,41.7825],[48.491,41.7275],[48.3775,41.591],[48.2845,41.561],[48.203,41.5125],[48.065,41.4925],[48.003,41.4165],[47.9915,41.3555],[47.8905,41.2875],[47.9115,41.255],[47.8845,41.2185],[47.8205,41.222],[47.7835,41.185],[47.7265,41.1935],[47.701,41.226],[47.6185,41.234],[47.564,41.198],[47.5065,41.226],[47.4985,41.2685],[47.377,41.2725],[47.3005,41.308],[47.252,41.3985],[47.222,41.419],[47.1715,41.527],[47.11,41.575],[47.0515,41.5545],[46.9915,41.595],[47.0125,41.6275],[46.8665,41.7205],[46.7655,41.801],[46.757,41.8625],[46.6215,41.824],[46.5565,41.8205],[46.5515,41.867],[46.5085,41.8895],[46.4635,41.8655],[46.422,41.908],[46.3985,41.8445],[46.3045,41.7755],[46.216,41.7635],[46.1815,41.7345],[46.2075,41.6535],[46.1955,41.63],[46.2975,41.572],[46.331,41.5015],[46.5225,41.4115],[46.6165,41.3905],[46.697,41.3195],[46.7,41.2415],[46.6675,41.219],[46.66,41.131],[46.631,41.098],[46.55,41.1125],[46.4925,41.0555],[46.458,41.098],[46.369,41.1195],[46.2915,41.174],[46.1895,41.192],[45.953,41.178],[45.887,41.211],[45.817,41.222],[45.7415,41.2585],[45.7005,41.298],[45.7405,41.344],[45.6485,41.379],[45.601,41.3735],[45.564,41.407],[45.4545,41.455],[45.3915,41.4275],[45.309,41.473],[45.165,41.4065],[45.093,41.339],[45.014,41.2965],[45.052,41.2395],[45.0515,41.193],[45.127,41.1995],[45.202,41.138],[45.15,41.1135],[45.099,41.12],[45.0755,41.069],[45.215,41.0385],[45.251,41.0135],[45.3685,41.008],[45.454,40.969],[45.4765,40.93],[45.602,40.8825],[45.608,40.8505],[45.5455,40.7745],[45.43,40.7465],[45.3635,40.655],[45.388,40.6185],[45.4595,40.5815],[45.4385,40.52],[45.512,40.4555],[45.5895,40.4305],[45.652,40.367],[45.697,40.367],[45.7855,40.329],[45.9305,40.297],[45.9595,40.2755],[45.9785,40.1905],[45.955,40.135],[45.904,40.104],[45.912,40.0755],[45.8805,40.021],[45.834,39.99],[45.796,40.0255],[45.6215,40.021],[45.611,39.974],[45.701,39.968],[45.7805,39.947],[45.826,39.826],[45.941,39.7875],[45.969,39.792],[46.08,39.685],[46.155,39.651],[46.1715,39.615],[46.2495,39.601],[46.3345,39.636],[46.4125,39.631],[46.434,39.569],[46.489,39.587],[46.5665,39.572],[46.5735,39.541],[46.51,39.525],[46.5055,39.483],[46.4585,39.4495],[46.3855,39.433],[46.39,39.388],[46.448,39.3395],[46.494,39.336],[46.547,39.26],[46.5895,39.2355],[46.544,39.1755],[46.462,39.1995],[46.44,39.1705],[46.531,39.0945],[46.546,39.0035],[46.5125,38.936],[46.534,38.867],[46.6035,38.91],[46.6735,38.978],[46.6955,39.018],[46.762,39.037],[46.7905,39.087],[46.8665,39.1405],[46.9255,39.1645],[46.9635,39.1405],[47.054,39.194],[47.058,39.238],[47.133,39.292],[47.2155,39.319],[47.296,39.367],[47.4205,39.465],[47.753,39.606],[47.8125,39.659],[47.9145,39.6615],[47.9855,39.709],[48.032,39.6925],[48.332,39.4285],[48.361,39.394],[48.248,39.3305],[48.211,39.329],[48.13,39.271],[48.141,39.208],[48.2135,39.1665],[48.2305,39.134],[48.303,39.109],[48.339,39.034],[48.271,38.9675],[48.1455,38.946],[48.088,38.947],[48.028,38.8995],[48.0205,38.838],[48.1075,38.77],[48.192,38.756],[48.249,38.726],[48.2525,38.662],[48.295,38.6475],[48.321,38.602],[48.425,38.6135],[48.472,38.5535],[48.5915,38.453],[48.608,38.4155],[48.6595,38.3935],[48.7385,38.411],[48.7915,38.4515],[49.2125,38.4085]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/d/dd/Flag_of_Azerbaijan.svg","name:en":"Azerbaijan","wikidata":"Q227","ISO3166-1:alpha2":"AZ","ISO3166-1:alpha3":"AZE","ISO3166-1:numeric":"031"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[61.274,35.605],[61.2825,35.538],[61.2335,35.4625],[61.2265,35.4215],[61.1875,35.3995],[61.1905,35.293],[61.092,35.2635],[61.093,35.183],[61.14,35.102],[61.1085,35.03],[61.0975,34.952],[61.0575,34.9075],[61.068,34.8125],[61.003,34.7085],[60.9845,34.62],[60.922,34.623],[60.8975,34.5675],[60.8485,34.542],[60.7425,34.527],[60.8075,34.462],[60.8495,34.3685],[60.9185,34.305],[60.6865,34.3105],[60.669,34.272],[60.6205,34.244],[60.5375,34.1505],[60.5175,34.062],[60.582,33.8055],[60.547,33.7315],[60.5515,33.64],[60.5815,33.597],[60.7035,33.5455],[60.903,33.54],[60.851,33.482],[60.8665,33.4215],[60.8245,33.4],[60.634,33.228],[60.5835,33.13],[60.6295,33.021],[60.7095,32.696],[60.815,32.4815],[60.859,32.2665],[60.883,32.1975],[60.819,32.0115],[60.8325,31.98],[60.818,31.89],[60.8135,31.7085],[60.856,31.4865],[61.7065,31.376],[61.7785,31.3025],[61.777,31.214],[61.8305,31.092],[61.814,30.9555],[61.782,30.911],[61.805,30.8395],[61.5115,30.5385],[61.343,30.359],[60.873,29.8585],[61.564,29.658],[61.8975,29.56],[62.471,29.3895],[63.233,29.4575],[63.5765,29.487],[64.0545,29.4075],[64.128,29.3785],[64.166,29.408],[64.142,29.4365],[64.1795,29.4835],[64.249,29.499],[64.2635,29.522],[64.3445,29.525],[64.5715,29.5865],[65.065,29.5295],[65.493,29.6445],[65.6965,29.705],[65.8095,29.7295],[66.25,29.8495],[66.332,29.92],[66.373,29.9715],[66.232,30.063],[66.289,30.153],[66.3265,30.2475],[66.3415,30.3845],[66.3625,30.4185],[66.3565,30.4765],[66.2865,30.5255],[66.288,30.577],[66.392,30.9405],[66.5785,30.9765],[66.688,31.0825],[66.7155,31.186],[66.813,31.223],[66.834,31.265],[66.956,31.308],[67.033,31.3105],[67.024,31.2565],[67.07,31.214],[67.1695,31.2455],[67.1975,31.217],[67.279,31.1995],[67.4,31.211],[67.4855,31.2325],[67.678,31.2935],[67.722,31.327],[67.7875,31.3325],[67.796,31.383],[67.75,31.4165],[67.651,31.3925],[67.571,31.5245],[67.6465,31.5285],[67.7165,31.514],[67.8575,31.624],[67.9745,31.6405],[68.0705,31.696],[68.1465,31.8045],[68.1795,31.817],[68.2655,31.8035],[68.2795,31.757],[68.425,31.756],[68.4615,31.735],[68.523,31.7355],[68.5655,31.7145],[68.591,31.753],[68.475,31.7735],[68.5755,31.834],[68.643,31.778],[68.7085,31.771],[68.728,31.6955],[68.7875,31.6545],[68.7895,31.6245],[68.908,31.5965],[68.96,31.649],[69.02,31.6295],[69.0405,31.6615],[69.116,31.7005],[69.193,31.847],[69.33,31.9295],[69.3025,31.987],[69.292,32.095],[69.27,32.162],[69.278,32.3615],[69.2395,32.4645],[69.278,32.529],[69.371,32.5625],[69.454,32.6585],[69.438,32.731],[69.3875,32.768],[69.441,32.804],[69.5065,32.896],[69.4955,32.933],[69.4995,33.0205],[69.573,33.0985],[69.672,33.101],[69.7985,33.131],[69.879,33.0945],[69.941,33.131],[70.0205,33.1385],[70.0735,33.22],[70.096,33.205],[70.166,33.2215],[70.3285,33.343],[70.297,33.43],[70.251,33.44],[70.1995,33.4875],[70.1715,33.539],[70.1985,33.6195],[70.1435,33.658],[70.1535,33.695],[69.9715,33.7585],[69.9155,33.887],[69.856,33.949],[69.9045,34.0395],[70.0485,34.035],[70.0875,34.0085],[70.149,34.0145],[70.194,33.983],[70.2905,33.9815],[70.325,33.966],[70.4145,33.9675],[70.4595,33.945],[70.547,33.943],[70.584,33.9625],[70.643,33.9525],[70.777,33.962],[70.822,33.9785],[70.8835,33.974],[71.0305,34.054],[71.0825,34.0635],[71.071,34.1095],[71.13,34.1645],[71.13,34.2825],[71.173,34.365],[71.1155,34.377],[71.1075,34.4335],[71.0595,34.4245],[71.024,34.4505],[71.011,34.534],[71.025,34.5595],[71.1145,34.628],[71.0895,34.6785],[71.187,34.7495],[71.2775,34.7995],[71.296,34.8775],[71.351,34.9145],[71.475,34.9495],[71.507,35.006],[71.5525,35.0225],[71.532,35.091],[71.625,35.139],[71.6815,35.2095],[71.6335,35.2255],[71.564,35.2785],[71.5445,35.3125],[71.6555,35.437],[71.594,35.5],[71.6205,35.571],[71.521,35.6065],[71.4995,35.6295],[71.5365,35.6695],[71.553,35.7225],[71.494,35.7495],[71.4775,35.8215],[71.431,35.888],[71.396,35.8915],[71.368,35.968],[71.3225,35.959],[71.197,36.041],[71.214,36.0825],[71.3605,36.204],[71.436,36.2325],[71.4505,36.272],[71.583,36.345],[71.558,36.3725],[71.616,36.403],[71.657,36.4835],[71.8065,36.3995],[71.8205,36.4345],[71.8095,36.508],[71.9165,36.5085],[71.936,36.5465],[72.0035,36.5575],[72.0305,36.592],[72.088,36.5965],[72.0965,36.647],[72.2095,36.6635],[72.1845,36.7115],[72.2535,36.7525],[72.2985,36.74],[72.3355,36.7665],[72.5025,36.784],[72.5615,36.829],[72.6525,36.852],[72.7175,36.837],[72.768,36.854],[72.818,36.8415],[72.93,36.843],[72.943,36.868],[73.0295,36.8615],[73.083,36.887],[73.1775,36.879],[73.4005,36.899],[73.548,36.891],[73.6915,36.916],[73.8,36.8905],[73.836,36.9255],[73.8905,36.9125],[73.9035,36.876],[73.9575,36.8425],[74.048,36.8265],[74.121,36.838],[74.1805,36.9165],[74.237,36.895],[74.3095,36.919],[74.35,36.9625],[74.4135,36.9755],[74.4195,37.0075],[74.5435,36.967],[74.519,37.0175],[74.5645,37.031],[74.4605,37.1295],[74.49,37.1775],[74.501,37.2395],[74.582,37.2355],[74.6285,37.2615],[74.6585,37.2345],[74.7155,37.282],[74.7495,37.2845],[74.809,37.2145],[74.89,37.234],[74.887,37.258],[74.804,37.3555],[74.674,37.389],[74.566,37.3725],[74.5575,37.397],[74.482,37.413],[74.4015,37.392],[74.375,37.4245],[74.276,37.402],[74.185,37.3345],[74.0635,37.3125],[74.0165,37.2915],[73.965,37.299],[73.852,37.2575],[73.843,37.234],[73.6305,37.2495],[73.671,37.31],[73.7325,37.3155],[73.7705,37.344],[73.776,37.44],[73.7195,37.447],[73.6405,37.4335],[73.5135,37.4745],[73.452,37.474],[73.3835,37.4465],[73.3005,37.462],[73.21,37.404],[73.1495,37.4045],[73.094,37.325],[72.9525,37.2905],[72.891,37.243],[72.818,37.2325],[72.7755,37.195],[72.7395,37.119],[72.6625,37.0175],[72.622,37.029],[72.5695,37.0075],[72.4735,36.9945],[72.441,37.0065],[72.3255,36.9815],[72.209,36.9245],[72.193,36.906],[72.0185,36.811],[71.958,36.756],[71.903,36.734],[71.8435,36.6805],[71.6755,36.671],[71.635,36.69],[71.563,36.764],[71.513,36.898],[71.468,36.9515],[71.4625,37.0235],[71.432,37.0565],[71.4535,37.207],[71.485,37.245],[71.4865,37.336],[71.5035,37.451],[71.5265,37.48],[71.4955,37.5335],[71.5045,37.607],[71.526,37.6315],[71.5515,37.7365],[71.532,37.7615],[71.596,37.8075],[71.591,37.9235],[71.524,37.953],[71.3275,37.887],[71.3135,37.919],[71.261,37.9215],[71.2755,38.006],[71.3215,38.0675],[71.3745,38.2555],[71.3285,38.306],[71.252,38.3105],[71.1835,38.347],[71.163,38.388],[71.0465,38.4105],[71.0315,38.461],[70.9885,38.491],[70.956,38.439],[70.856,38.4415],[70.771,38.4565],[70.7565,38.428],[70.674,38.4045],[70.6845,38.364],[70.611,38.3465],[70.6055,38.283],[70.5455,38.2435],[70.4925,38.1215],[70.4315,38.0995],[70.3285,37.999],[70.291,37.9875],[70.262,37.9375],[70.171,37.93],[70.1855,37.85],[70.2795,37.816],[70.3005,37.7035],[70.261,37.6395],[70.266,37.6115],[70.145,37.525],[70.1125,37.5245],[69.998,37.567],[69.9405,37.608],[69.8415,37.6035],[69.802,37.569],[69.7625,37.5915],[69.7135,37.5745],[69.6225,37.5695],[69.511,37.5805],[69.457,37.494],[69.3895,37.4495],[69.371,37.379],[69.393,37.36],[69.425,37.246],[69.4035,37.168],[69.37,37.166],[69.3235,37.118],[69.2535,37.0935],[69.108,37.1865],[69.0295,37.2565],[68.9995,37.308],[68.962,37.325],[68.8085,37.3065],[68.826,37.2695],[68.6715,37.2755],[68.617,37.1985],[68.543,37.167],[68.406,37.145],[68.39,37.098],[68.3155,37.1105],[68.2905,37.09],[68.2885,37.0315],[68.253,37.005],[68.174,37.0145],[68.0515,36.9285],[68.0155,36.9255],[67.972,36.9805],[67.9105,37.0145],[67.8895,37.0565],[67.7905,37.086],[67.787,37.1855],[67.7235,37.219],[67.558,37.2225],[67.517,37.265],[67.453,37.2365],[67.27,37.182],[67.2235,37.244],[67.1065,37.2915],[67.1125,37.327],[67.045,37.3805],[66.95,37.3985],[66.85,37.354],[66.801,37.3635],[66.6925,37.357],[66.652,37.3235],[66.5935,37.3685],[66.5515,37.3545],[66.431,37.319],[66.307,37.32],[66.246,37.356],[66.167,37.3695],[66.085,37.4395],[65.974,37.467],[65.894,37.4755],[65.8185,37.5],[65.803,37.523],[65.706,37.5395],[65.646,37.4385],[65.6435,37.3295],[65.556,37.2405],[65.2835,37.228],[65.2285,37.251],[65.1905,37.236],[65.1235,37.244],[64.99,37.218],[64.7555,37.1125],[64.7975,36.922],[64.707,36.796],[64.6185,36.6365],[64.6365,36.4425],[64.58,36.3515],[64.445,36.2425],[64.3155,36.208],[64.284,36.1515],[64.17,36.163],[64.0595,36.0985],[64.0685,35.9975],[63.989,36.0365],[63.7035,35.9675],[63.5365,35.948],[63.5005,35.8985],[63.299,35.8565],[63.121,35.862],[63.109,35.827],[63.195,35.713],[63.249,35.681],[63.1115,35.6335],[63.095,35.6185],[63.13,35.544],[63.1025,35.4285],[63.016,35.4125],[62.8775,35.345],[62.824,35.3],[62.7855,35.301],[62.713,35.2395],[62.611,35.2345],[62.4775,35.279],[62.326,35.145],[62.2985,35.138],[62.291,35.256],[62.2655,35.2955],[62.1535,35.341],[62.0645,35.4345],[61.968,35.454],[61.7795,35.411],[61.588,35.437],[61.45,35.5205],[61.3855,35.569],[61.3605,35.6295],[61.274,35.605]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9a/Flag_of_Afghanistan.svg","name:en":"Afghanistan","wikidata":"Q889","ISO3166-1:alpha2":"AF","ISO3166-1:alpha3":"AFG","ISO3166-1:numeric":"004"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[12.368,56.214],[12.3415,56.184],[12.033,56.282],[11.997,56.368],[11.7495,56.404],[11.5815,56.4015],[11.5065,56.382],[11.253,56.1365],[11.1485,56.0965],[11.0,55.9415],[10.7935,55.772],[10.8,55.7085],[10.7065,55.6615],[10.709,55.7585],[10.75,55.8385],[10.8835,55.93],[11.2675,56.311],[11.3265,56.4285],[11.3195,56.4825],[11.388,56.5045],[11.507,56.4845],[11.723,56.501],[11.81,56.5285],[11.9085,56.6015],[11.846,56.7545],[11.712,56.936],[11.462,56.925],[11.3705,56.908],[11.27,56.8685],[11.1965,56.816],[11.1475,56.715],[11.0815,56.682],[10.886,56.734],[10.712,56.7375],[10.645,56.832],[10.662,56.88],[10.756,57.045],[10.927,57.0005],[11.0715,57.001],[11.222,57.0305],[11.3105,57.069],[11.507,57.1915],[11.3145,57.428],[11.224,57.5235],[11.0135,57.5615],[11.0245,57.731],[10.9755,57.7845],[10.7265,57.941],[10.516,57.9475],[10.412,57.929],[10.297,57.8915],[10.076,57.793],[9.9495,57.7965],[9.8285,57.784],[9.7405,57.7595],[9.63,57.701],[9.5645,57.639],[9.455,57.5635],[9.3905,57.4815],[9.266,57.364],[9.1865,57.343],[9.092,57.358],[8.9375,57.3585],[8.7395,57.317],[8.554,57.3265],[8.4145,57.303],[8.3335,57.2705],[8.0465,57.0625],[7.9275,56.933],[7.886,56.869],[7.843,56.757],[7.7655,56.6115],[7.749,56.546],[7.7595,56.195],[7.7405,56.099],[7.7535,55.9625],[7.786,55.908],[7.818,55.775],[7.729,55.6155],[7.716,55.549],[7.7615,55.4595],[7.8705,55.392],[7.989,55.358],[8.055,55.285],[8.0445,55.099],[8.29,55.065],[8.3955,55.0695],[8.557,54.993],[8.5555,54.921],[8.6865,54.909],[8.948,54.9025],[9.0475,54.872],[9.1555,54.8705],[9.2405,54.85],[9.25,54.8095],[9.342,54.8035],[9.383,54.839],[9.6045,54.8545],[9.74,54.8235],[9.8945,54.842],[10.0515,54.766],[10.1695,54.738],[10.2,54.8535],[10.25,54.845],[10.271,54.779],[10.42,54.6445],[10.67,54.5745],[10.715,54.5765],[10.825,54.6425],[10.754,54.6735],[10.828,54.747],[10.9,54.756],[10.905,54.7085],[11.0,54.6735],[11.1335,54.6635],[11.255,54.634],[11.2965,54.6065],[11.4465,54.554],[11.795,54.4515],[12.0635,54.455],[12.145,54.4685],[12.1615,54.525],[12.24,54.6265],[12.6915,54.8045],[12.838,54.8555],[12.891,54.901],[12.904,54.9865],[12.85,55.1115],[12.7225,55.1385],[12.6035,55.2235],[12.59,55.325],[12.642,55.3375],[12.6105,55.4315],[12.7155,55.489],[12.7295,55.541],[12.8815,55.614],[12.896,55.6435],[12.7085,55.827],[12.6525,55.9265],[12.642,56.057],[12.368,56.214]]],[[[14.3335,55.0915],[14.375,55.0],[14.4315,54.9535],[14.5575,54.891],[14.858,54.8095],[15.08,54.7835],[15.22,54.8005],[15.3185,54.8365],[15.4385,54.9255],[15.4985,55.029],[15.5095,55.1465],[15.4885,55.202],[15.5415,55.268],[15.553,55.3205],[15.5155,55.41],[15.423,55.4775],[15.2395,55.53],[15.1005,55.529],[14.9325,55.4795],[14.8505,55.4915],[14.5265,55.28],[14.3445,55.1475],[14.3335,55.0915]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9c/Flag_of_Denmark.svg","name:en":"Denmark","wikidata":"Q35","ISO3166-1:alpha2":"DK","ISO3166-1:alpha3":"DNK","ISO3166-1:numeric":"208"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[31.7815,52.112],[31.781,52.1855],[31.711,52.215],[31.6965,52.258],[31.6145,52.3275],[31.6325,52.398],[31.6025,52.4235],[31.652,52.545],[31.564,52.5945],[31.5695,52.6345],[31.504,52.6985],[31.567,52.708],[31.6005,52.746],[31.5895,52.7915],[31.441,52.881],[31.3725,52.9335],[31.3305,53.042],[31.335,53.091],[31.4185,53.1875],[31.5005,53.2045],[31.5435,53.191],[31.6455,53.2195],[31.7865,53.1795],[31.831,53.1105],[31.887,53.1145],[31.9345,53.083],[32.018,53.091],[32.095,53.076],[32.2545,53.1315],[32.302,53.1275],[32.339,53.172],[32.4145,53.186],[32.4925,53.2535],[32.5225,53.3065],[32.5735,53.298],[32.6265,53.3305],[32.7335,53.3325],[32.723,53.374],[32.757,53.458],[32.478,53.549],[32.426,53.6075],[32.499,53.681],[32.4605,53.7375],[32.358,53.7205],[32.3205,53.758],[32.22,53.7705],[32.208,53.7995],[32.1,53.8145],[31.867,53.7825],[31.769,53.8355],[31.892,53.9665],[31.854,54.0045],[31.898,54.083],[31.7745,54.113],[31.6765,54.1175],[31.4925,54.1615],[31.383,54.2325],[31.3135,54.2485],[31.3225,54.3405],[31.236,54.464],[31.1085,54.492],[31.093,54.5295],[31.2105,54.632],[31.178,54.67],[31.0955,54.6615],[31.012,54.6745],[31.0255,54.706],[30.9535,54.743],[30.7515,54.8095],[30.835,54.9075],[30.8345,54.9485],[30.9085,54.949],[31.028,55.0625],[30.9905,55.077],[30.996,55.1445],[30.9235,55.2005],[30.83,55.3345],[30.9345,55.4],[30.901,55.4735],[30.959,55.501],[30.9325,55.617],[30.872,55.63],[30.8095,55.5905],[30.757,55.5945],[30.734,55.6545],[30.6705,55.6395],[30.648,55.71],[30.595,55.747],[30.5335,55.7505],[30.4815,55.8125],[30.3695,55.8085],[30.2865,55.8315],[30.239,55.872],[30.107,55.8315],[29.976,55.8715],[29.895,55.8625],[29.8065,55.7835],[29.755,55.8005],[29.623,55.78],[29.5155,55.7015],[29.4,55.7535],[29.3605,55.7985],[29.426,55.9505],[29.3055,55.9915],[29.214,55.989],[29.156,56.0265],[29.088,56.034],[28.858,55.9885],[28.829,55.9635],[28.723,55.9785],[28.62,56.0995],[28.5485,56.116],[28.458,56.1025],[28.32,56.0595],[28.1515,56.17],[28.026,56.1195],[27.972,56.1205],[27.906,56.052],[27.833,56.0235],[27.8005,55.9845],[27.653,55.9295],[27.6155,55.787],[27.429,55.795],[27.378,55.8075],[27.2875,55.785],[27.1605,55.85],[27.015,55.8285],[26.9205,55.788],[26.8705,55.714],[26.8,55.698],[26.659,55.706],[26.6305,55.6805],[26.628,55.5725],[26.524,55.51],[26.557,55.4635],[26.554,55.389],[26.4415,55.3475],[26.6025,55.321],[26.6685,55.3385],[26.792,55.321],[26.8355,55.2855],[26.7315,55.245],[26.675,55.1975],[26.6765,55.159],[26.461,55.1285],[26.3575,55.1505],[26.2295,55.1075],[26.271,55.0775],[26.186,54.9915],[26.121,54.9895],[26.081,54.9545],[25.9555,54.951],[25.858,54.9275],[25.853,54.8965],[25.734,54.8025],[25.7495,54.7645],[25.734,54.677],[25.7625,54.577],[25.6825,54.5365],[25.626,54.4565],[25.629,54.417],[25.562,54.366],[25.5525,54.315],[25.677,54.324],[25.7855,54.2335],[25.781,54.1595],[25.7105,54.169],[25.65,54.1285],[25.5155,54.1755],[25.5845,54.2345],[25.513,54.3085],[25.3535,54.264],[25.215,54.256],[25.2025,54.221],[25.0945,54.1505],[25.016,54.1375],[24.953,54.17],[24.866,54.147],[24.779,54.1],[24.839,54.021],[24.7265,53.967],[24.626,54.0105],[24.5075,53.9605],[24.448,53.904],[24.352,53.897],[24.2015,53.97],[24.103,53.9405],[23.983,53.924],[23.908,53.967],[23.7545,53.9275],[23.6305,53.928],[23.515,53.9565],[23.549,53.768],[23.7075,53.4365],[23.819,53.244],[23.915,53.162],[23.8735,53.087],[23.9255,53.0245],[23.9165,52.939],[23.9395,52.813],[23.9385,52.713],[23.773,52.623],[23.6415,52.6075],[23.4665,52.5495],[23.3565,52.469],[23.2155,52.3295],[23.2035,52.2265],[23.3955,52.201],[23.49,52.1685],[23.5565,52.1115],[23.64,52.0855],[23.6885,51.994],[23.612,51.9175],[23.6315,51.78],[23.531,51.7435],[23.553,51.715],[23.5405,51.601],[23.5655,51.534],[23.618,51.508],[23.6645,51.577],[23.6055,51.617],[23.6785,51.653],[23.7825,51.6675],[23.867,51.643],[23.8805,51.6085],[23.9955,51.5805],[24.0755,51.619],[24.1205,51.6665],[24.2685,51.716],[24.3195,51.751],[24.3,51.814],[24.388,51.882],[24.5675,51.8895],[24.625,51.9025],[24.7495,51.881],[24.81,51.912],[24.934,51.891],[25.1005,51.9535],[25.2635,51.9685],[25.41,51.9215],[25.596,51.9235],[25.708,51.916],[25.819,51.929],[25.9205,51.916],[25.99,51.932],[26.093,51.9125],[26.1555,51.866],[26.371,51.863],[26.4665,51.7995],[26.5465,51.796],[26.6115,51.8245],[26.755,51.8035],[26.8095,51.7565],[26.8505,51.767],[26.9505,51.736],[26.9935,51.7685],[27.207,51.774],[27.213,51.6665],[27.2735,51.6465],[27.2675,51.606],[27.393,51.605],[27.55,51.637],[27.619,51.6055],[27.7275,51.6045],[27.6835,51.541],[27.742,51.4775],[27.807,51.533],[27.858,51.6305],[27.9115,51.6125],[27.946,51.558],[28.1175,51.583],[28.1735,51.644],[28.2645,51.6815],[28.287,51.6235],[28.3615,51.558],[28.3975,51.551],[28.4715,51.5935],[28.559,51.573],[28.6355,51.574],[28.688,51.4445],[28.772,51.4295],[28.7615,51.4875],[28.807,51.5485],[28.9125,51.59],[28.9995,51.5735],[29.106,51.651],[29.1635,51.6525],[29.1775,51.6075],[29.2665,51.531],[29.251,51.4955],[29.309,51.454],[29.3405,51.3855],[29.4215,51.415],[29.4955,51.397],[29.542,51.4825],[29.7165,51.53],[29.7495,51.457],[29.7975,51.4465],[29.8935,51.4855],[29.9715,51.475],[30.0355,51.504],[30.12,51.4875],[30.18,51.5125],[30.347,51.4235],[30.324,51.3605],[30.424,51.3055],[30.4625,51.3065],[30.5385,51.2625],[30.618,51.288],[30.6555,51.376],[30.5285,51.563],[30.529,51.602],[30.578,51.6805],[30.6485,51.7505],[30.707,51.8685],[30.805,51.947],[30.888,51.9685],[30.9915,52.0775],[31.1365,52.103],[31.253,52.042],[31.2975,52.0515],[31.331,52.107],[31.4025,52.1425],[31.466,52.1205],[31.6585,52.1175],[31.722,52.096],[31.7815,52.112]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/85/Flag_of_Belarus.svg","name:en":"Belarus","wikidata":"Q184","ISO3166-1:alpha2":"BY","ISO3166-1:alpha3":"BLR","ISO3166-1:numeric":"112"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[115.052,4.8795],[115.0535,4.9155],[115.0905,4.9705],[115.156,4.9825],[115.1315,5.0705],[115.0675,5.101],[114.9575,5.0665],[114.8675,5.029],[114.742,4.9635],[114.4725,4.7565],[114.388,4.7115],[114.084,4.645],[114.077,4.5835],[114.162,4.5735],[114.26,4.507],[114.307,4.4185],[114.307,4.366],[114.333,4.349],[114.3165,4.2635],[114.382,4.265],[114.454,4.242],[114.496,4.1465],[114.5455,4.125],[114.553,4.084],[114.649,4.006],[114.7185,4.0435],[114.8075,4.1495],[114.794,4.1635],[114.8575,4.2695],[114.88,4.3705],[114.8775,4.4265],[114.8295,4.4295],[114.8185,4.4765],[114.8025,4.6755],[114.775,4.696],[114.8585,4.7975],[114.8955,4.8155],[114.9715,4.808],[114.9875,4.871],[115.025,4.8965],[115.052,4.8795]]],[[[115.1425,4.9155],[115.084,4.911],[115.0495,4.8595],[115.027,4.807],[115.0225,4.75],[115.099,4.4685],[115.1125,4.3725],[115.1555,4.383],[115.246,4.341],[115.296,4.338],[115.3225,4.2965],[115.3495,4.352],[115.2725,4.4475],[115.268,4.5275],[115.289,4.604],[115.248,4.7015],[115.2375,4.796],[115.1505,4.872],[115.1425,4.9155]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/9c/Flag_of_Brunei.svg","name:en":"Brunei","wikidata":"Q921","ISO3166-1:alpha2":"BN","ISO3166-1:alpha3":"BRN","ISO3166-1:numeric":"096"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[91.5695,27.8175],[91.5585,27.8575],[91.619,27.8635],[91.6255,27.892],[91.4835,27.9445],[91.4655,28.0065],[91.4065,28.021],[91.3425,28.0615],[91.309,28.0605],[91.263,28.071],[91.2155,27.992],[91.191,27.9865],[91.1645,27.9425],[90.9935,27.831],[90.872,27.85],[90.8265,27.8855],[90.795,27.9595],[90.797,28.0455],[90.6915,28.081],[90.5835,28.023],[90.5075,28.0665],[90.372,28.0665],[90.362,28.097],[90.3065,28.156],[90.209,28.148],[90.161,28.192],[90.0895,28.1515],[90.0235,28.144],[89.977,28.1945],[89.8965,28.183],[89.8605,28.23],[89.819,28.241],[89.773,28.2225],[89.765,28.1865],[89.683,28.1845],[89.5965,28.164],[89.5735,28.127],[89.5125,28.0945],[89.46,28.0305],[89.4525,27.9765],[89.389,27.8905],[89.308,27.8575],[89.2255,27.7835],[89.228,27.734],[89.14,27.648],[89.128,27.615],[89.073,27.6095],[89.0345,27.58],[89.0105,27.503],[88.969,27.4055],[89.0045,27.327],[88.9595,27.312],[88.9215,27.3275],[88.9045,27.273],[88.8025,27.2485],[88.7995,27.2085],[88.7465,27.142],[88.87,27.11],[88.874,26.954],[88.9225,26.993],[88.9795,26.918],[89.0155,26.94],[89.0955,26.8915],[89.102,26.836],[89.141,26.812],[89.2625,26.8155],[89.3175,26.8515],[89.379,26.8625],[89.4395,26.8405],[89.4765,26.802],[89.557,26.8135],[89.649,26.7705],[89.679,26.7395],[89.745,26.73],[89.771,26.702],[89.8625,26.7015],[89.902,26.723],[90.045,26.73],[90.1905,26.768],[90.2005,26.835],[90.248,26.86],[90.3,26.8485],[90.3545,26.9015],[90.418,26.9045],[90.5475,26.8165],[90.691,26.7715],[90.822,26.7765],[90.9955,26.7905],[91.0555,26.7815],[91.0985,26.8225],[91.235,26.8125],[91.3385,26.7805],[91.378,26.796],[91.41,26.8395],[91.4945,26.7925],[91.627,26.821],[91.7245,26.814],[91.825,26.864],[91.8595,26.9135],[91.894,26.9195],[91.987,26.861],[92.0565,26.8495],[92.1035,26.8695],[92.12,26.9715],[92.0815,27.0405],[92.0445,27.052],[92.023,27.11],[92.0275,27.1625],[92.0715,27.238],[92.0465,27.2695],[92.1235,27.2865],[92.065,27.3275],[92.056,27.4],[92.017,27.4805],[91.9435,27.46],[91.925,27.473],[91.7775,27.4655],[91.6515,27.484],[91.566,27.584],[91.573,27.661],[91.6285,27.6985],[91.645,27.77],[91.6145,27.8215],[91.5695,27.8175]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/9/91/Flag_of_Bhutan.svg","name:en":"Bhutan","wikidata":"Q917","ISO3166-1:alpha2":"BT","ISO3166-1:alpha3":"BTN","ISO3166-1:numeric":"064"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[74.89,37.234],[74.809,37.2145],[74.7495,37.2845],[74.7155,37.282],[74.6585,37.2345],[74.6285,37.2615],[74.582,37.2355],[74.501,37.2395],[74.49,37.1775],[74.4605,37.1295],[74.5645,37.031],[74.702,37.0825],[74.739,37.0215],[74.8405,37.02],[74.906,36.9295],[74.919,36.979],[75.1255,37.0125],[75.2335,36.959],[75.2855,36.9745],[75.406,36.9365],[75.432,36.83],[75.4195,36.7735],[75.4695,36.716],[75.534,36.7715],[75.6375,36.77],[75.7195,36.752],[75.804,36.71],[75.9405,36.593],[75.926,36.573],[75.9875,36.5025],[76.0355,36.409],[75.985,36.3415],[75.995,36.3075],[76.0575,36.246],[76.0065,36.234],[76.014,36.1725],[75.9695,36.1635],[75.9465,36.119],[75.957,36.046],[76.0045,36.017],[76.101,36.016],[76.107,35.977],[76.1595,35.917],[76.145,35.8375],[76.2245,35.8425],[76.3565,35.8315],[76.3665,35.8635],[76.427,35.8525],[76.4685,35.891],[76.5175,35.8865],[76.556,35.927],[76.599,35.9],[76.561,35.8225],[76.6155,35.7615],[76.691,35.7485],[76.776,35.658],[76.815,35.67],[76.9575,35.5965],[77.011,35.611],[77.194,35.522],[77.3025,35.5455],[77.3815,35.4735],[77.4425,35.462],[77.501,35.4895],[77.5595,35.487],[77.6295,35.4625],[77.719,35.4615],[77.7425,35.4955],[77.812,35.523],[77.911,35.462],[77.968,35.4945],[78.025,35.4685],[78.098,35.4995],[78.101,35.4305],[78.023,35.359],[78.003,35.243],[78.0565,35.175],[78.0495,35.114],[78.1275,35.1005],[78.1145,35.032],[78.2015,34.973],[78.1785,34.9285],[78.237,34.868],[78.2085,34.7205],[78.272,34.7015],[78.2625,34.6635],[78.29,34.615],[78.3855,34.6065],[78.4825,34.5785],[78.552,34.5715],[78.5635,34.5095],[78.634,34.544],[78.7095,34.526],[78.7565,34.4845],[78.7425,34.453],[78.851,34.4155],[78.902,34.381],[78.947,34.3895],[78.982,34.357],[78.9855,34.314],[78.944,34.225],[78.9255,34.1545],[78.862,34.1655],[78.826,34.1245],[78.7425,34.0925],[78.6575,34.0745],[78.6575,34.0315],[78.7425,34.0],[78.7315,33.9275],[78.7655,33.836],[78.754,33.7845],[78.775,33.738],[78.731,33.6835],[78.6785,33.667],[78.741,33.601],[78.74,33.554],[78.8035,33.4895],[78.836,33.4265],[78.9365,33.387],[78.9625,33.3395],[79.027,33.32],[79.035,33.2725],[79.073,33.223],[79.1625,33.1675],[79.14,33.106],[79.1605,33.015],[79.3065,32.928],[79.303,32.892],[79.407,32.7855],[79.4145,32.737],[79.4605,32.71],[79.399,32.604],[79.41,32.519],[79.311,32.56],[79.248,32.5165],[79.184,32.498],[79.1175,32.4545],[79.1045,32.3745],[79.058,32.387],[78.9895,32.37],[78.9685,32.3355],[78.8685,32.4135],[78.813,32.434],[78.7575,32.567],[78.7815,32.6165],[78.723,32.6745],[78.635,32.6445],[78.611,32.6005],[78.5465,32.619],[78.502,32.5845],[78.4145,32.566],[78.3925,32.538],[78.4725,32.4435],[78.476,32.3325],[78.4965,32.276],[78.597,32.158],[78.7065,32.063],[78.74,32.0015],[78.779,31.9665],[78.7425,31.902],[78.7355,31.8325],[78.702,31.807],[78.761,31.675],[78.8465,31.607],[78.8225,31.5785],[78.7445,31.542],[78.7205,31.5075],[78.795,31.444],[78.753,31.385],[78.778,31.3125],[78.833,31.294],[78.913,31.31],[78.944,31.3655],[79.0185,31.349],[79.019,31.4265],[79.075,31.459],[79.1425,31.432],[79.2235,31.346],[79.25,31.2935],[79.2265,31.2605],[79.301,31.219],[79.321,31.1385],[79.414,31.1065],[79.427,31.0235],[79.507,31.0325],[79.553,30.9575],[79.601,30.939],[79.663,30.9645],[79.776,30.986],[79.8575,30.9755],[79.889,30.9175],[79.929,30.8825],[79.9885,30.877],[80.05,30.841],[80.1085,30.7815],[80.1795,30.806],[80.1975,30.765],[80.2385,30.7625],[80.249,30.7205],[80.1925,30.666],[80.2195,30.643],[80.2085,30.588],[80.2545,30.5645],[80.3155,30.5645],[80.3455,30.5205],[80.4195,30.515],[80.497,30.488],[80.541,30.449],[80.6065,30.4715],[80.693,30.413],[80.715,30.414],[80.7875,30.3405],[80.8335,30.313],[80.9065,30.3035],[80.927,30.267],[80.981,30.2705],[81.031,30.247],[81.0345,30.197],[81.109,30.0855],[81.092,30.0535],[81.1655,30.0105],[81.2715,30.0445],[81.2825,30.123],[81.3955,30.207],[81.39,30.2345],[81.4225,30.3055],[81.396,30.3235],[81.429,30.3835],[81.5395,30.373],[81.5645,30.4295],[81.6065,30.4115],[81.634,30.445],[81.75,30.3885],[81.9375,30.3475],[81.988,30.322],[82.1065,30.349],[82.127,30.304],[82.117,30.258],[82.141,30.197],[82.185,30.19],[82.1855,30.111],[82.1665,30.076],[82.2845,30.0585],[82.313,30.0385],[82.4215,30.0055],[82.497,29.9475],[82.5335,29.971],[82.603,29.889],[82.697,29.8565],[82.738,29.814],[82.765,29.731],[82.8185,29.6965],[82.869,29.687],[82.928,29.7],[82.9565,29.662],[83.038,29.649],[83.0695,29.61],[83.15,29.6245],[83.2175,29.605],[83.282,29.499],[83.345,29.495],[83.3715,29.4285],[83.4135,29.422],[83.4435,29.2995],[83.5145,29.2555],[83.518,29.2185],[83.5705,29.203],[83.632,29.159],[83.711,29.244],[83.7685,29.241],[83.814,29.3025],[83.903,29.327],[84.0915,29.294],[84.168,29.2005],[84.187,29.081],[84.249,29.0365],[84.2255,28.9435],[84.226,28.893],[84.3285,28.8615],[84.392,28.86],[84.435,28.821],[84.4375,28.77],[84.493,28.7365],[84.558,28.746],[84.6255,28.7365],[84.696,28.6365],[84.78,28.6105],[84.8565,28.571],[84.9205,28.595],[84.9485,28.5805],[85.058,28.6825],[85.1165,28.6855],[85.157,28.644],[85.1905,28.5695],[85.147,28.4875],[85.118,28.4855],[85.1275,28.3925],[85.1085,28.3465],[85.183,28.323],[85.204,28.3405],[85.255,28.293],[85.3375,28.3035],[85.377,28.2775],[85.4135,28.325],[85.46,28.3365],[85.5915,28.308],[85.6085,28.2565],[85.6625,28.303],[85.6835,28.3825],[85.7315,28.337],[85.7505,28.2365],[85.8495,28.1715],[85.897,28.111],[85.9,28.0545],[85.9775,27.988],[85.9515,27.945],[86.0,27.9115],[86.065,27.9],[86.1245,27.9275],[86.0805,28.022],[86.084,28.088],[86.118,28.091],[86.16,28.1385],[86.2185,28.1135],[86.2045,28.0785],[86.2265,27.9815],[86.2885,27.978],[86.3195,27.948],[86.372,27.942],[86.442,27.906],[86.468,27.946],[86.514,27.957],[86.5355,28.051],[86.575,28.1135],[86.6075,28.075],[86.6655,28.1025],[86.7485,28.0985],[86.7495,28.046],[86.9245,27.988],[86.9325,27.9615],[87.038,27.9505],[87.073,27.9205],[87.111,27.8455],[87.157,27.8245],[87.227,27.821],[87.2645,27.851],[87.3165,27.8275],[87.4255,27.8595],[87.4475,27.8225],[87.4895,27.846],[87.5785,27.862],[87.606,27.8185],[87.6645,27.8375],[87.725,27.8055],[87.7855,27.9005],[87.834,27.952],[87.866,27.911],[87.9305,27.918],[87.983,27.888],[88.027,27.9055],[88.091,27.8715],[88.135,27.8815],[88.118,27.918],[88.1425,27.9655],[88.1875,27.9425],[88.2375,27.9695],[88.2635,27.9555],[88.3225,27.98],[88.468,28.017],[88.492,28.0485],[88.546,28.034],[88.5575,28.0755],[88.64,28.116],[88.667,28.0765],[88.7545,28.0805],[88.836,28.0155],[88.843,27.956],[88.888,27.8565],[88.857,27.8155],[88.8565,27.717],[88.8445,27.662],[88.8095,27.6375],[88.8065,27.5965],[88.7715,27.558],[88.783,27.4535],[88.8075,27.4045],[88.9215,27.3275],[88.9595,27.312],[89.0045,27.327],[88.969,27.4055],[89.0105,27.503],[89.0345,27.58],[89.073,27.6095],[89.128,27.615],[89.14,27.648],[89.228,27.734],[89.2255,27.7835],[89.308,27.8575],[89.389,27.8905],[89.4525,27.9765],[89.46,28.0305],[89.5125,28.0945],[89.5735,28.127],[89.5965,28.164],[89.683,28.1845],[89.765,28.1865],[89.773,28.2225],[89.819,28.241],[89.8605,28.23],[89.8965,28.183],[89.977,28.1945],[90.0235,28.144],[90.0895,28.1515],[90.161,28.192],[90.209,28.148],[90.3065,28.156],[90.362,28.097],[90.372,28.0665],[90.5075,28.0665],[90.5835,28.023],[90.6915,28.081],[90.797,28.0455],[90.9285,28.0425],[90.981,28.002],[91.026,28.0235],[91.075,28.004],[91.191,27.9865],[91.2155,27.992],[91.263,28.071],[91.309,28.0605],[91.3425,28.0615],[91.4065,28.021],[91.4655,28.0065],[91.4835,27.9445],[91.6255,27.892],[91.619,27.8635],[91.5585,27.8575],[91.5695,27.8175],[91.6125,27.839],[91.6605,27.829],[91.722,27.783],[91.8015,27.7725],[91.829,27.788],[91.8725,27.72],[91.925,27.7165],[91.979,27.7745],[92.036,27.7755],[92.108,27.803],[92.143,27.835],[92.2445,27.888],[92.2905,27.873],[92.3205,27.8],[92.422,27.8355],[92.4775,27.8365],[92.5815,27.89],[92.6505,27.915],[92.7235,27.9735],[92.7375,28.0415],[92.6645,28.0705],[92.6775,28.151],[92.739,28.154],[92.791,28.1875],[92.8265,28.175],[92.9215,28.2005],[92.9325,28.249],[92.9915,28.273],[93.1485,28.3665],[93.197,28.545],[93.23,28.5915],[93.3075,28.527],[93.3895,28.5325],[93.4575,28.668],[93.512,28.6675],[93.623,28.6885],[93.643,28.6575],[93.707,28.6645],[93.7845,28.7135],[93.7875,28.733],[93.8965,28.7575],[93.975,28.8215],[94.0785,28.8825],[94.1315,28.889],[94.1785,28.936],[94.2605,28.9315],[94.274,28.968],[94.342,29.002],[94.3125,29.079],[94.2845,29.0865],[94.293,29.1525],[94.379,29.154],[94.3905,29.184],[94.452,29.189],[94.5105,29.231],[94.541,29.2195],[94.5905,29.272],[94.6935,29.318],[94.735,29.287],[94.7515,29.2295],[94.7945,29.2175],[94.8095,29.165],[94.8475,29.1825],[94.9945,29.144],[95.097,29.142],[95.136,29.0885],[95.179,29.1045],[95.273,29.1055],[95.2995,29.1365],[95.379,29.137],[95.419,29.1805],[95.458,29.137],[95.5095,29.1265],[95.5085,29.195],[95.589,29.188],[95.6055,29.236],[95.648,29.2105],[95.7055,29.213],[95.7525,29.276],[95.737,29.298],[95.8125,29.3475],[95.8775,29.3145],[95.9655,29.376],[96.0165,29.3625],[96.0535,29.3825],[96.139,29.343],[96.1495,29.295],[96.196,29.2635],[96.261,29.2445],[96.2995,29.191],[96.235,29.1295],[96.183,29.111],[96.23,29.047],[96.2695,29.0965],[96.358,29.094],[96.362,29.048],[96.4345,29.0065],[96.441,28.9525],[96.508,28.9465],[96.519,28.8675],[96.5765,28.8185],[96.62,28.7275],[96.597,28.6965],[96.536,28.681],[96.537,28.6545],[96.451,28.583],[96.4815,28.555],[96.412,28.517],[96.4785,28.4905],[96.538,28.572],[96.6135,28.6135],[96.655,28.609],[96.7455,28.5715],[96.767,28.515],[96.861,28.4855],[96.893,28.4185],[96.8895,28.386],[96.977,28.3295],[97.027,28.33],[97.0785,28.3715],[97.146,28.353],[97.2485,28.264],[97.346,28.2145],[97.3955,28.2415],[97.425,28.298],[97.504,28.315],[97.478,28.3795],[97.5235,28.447],[97.502,28.495],[97.566,28.5465],[97.612,28.5185],[97.6635,28.5385],[97.7375,28.466],[97.7355,28.3985],[97.7785,28.3705],[97.797,28.3295],[97.842,28.3285],[97.8705,28.3675],[97.909,28.3685],[98.0205,28.258],[98.0115,28.208],[98.0765,28.2065],[98.097,28.167],[98.1385,28.1465],[98.1605,28.0895],[98.132,27.986],[98.184,27.9435],[98.206,27.892],[98.1705,27.854],[98.227,27.7965],[98.2345,27.6925],[98.284,27.6565],[98.311,27.5865],[98.317,27.523],[98.3765,27.511],[98.4305,27.5535],[98.43,27.6585],[98.475,27.6375],[98.5645,27.6405],[98.5865,27.593],[98.647,27.5685],[98.6745,27.586],[98.7045,27.522],[98.6845,27.4885],[98.7075,27.3645],[98.741,27.332],[98.7295,27.25],[98.6915,27.2],[98.7135,27.143],[98.712,27.078],[98.7665,27.052],[98.7335,27.006],[98.758,26.8855],[98.731,26.8545],[98.7625,26.802],[98.743,26.713],[98.782,26.606],[98.754,26.5655],[98.757,26.501],[98.734,26.3545],[98.682,26.31],[98.672,26.2455],[98.7115,26.2365],[98.736,26.19],[98.7095,26.121],[98.667,26.122],[98.6305,26.151],[98.573,26.122],[98.6035,26.0535],[98.619,25.975],[98.6815,25.9415],[98.708,25.86],[98.6325,25.8],[98.5315,25.8465],[98.475,25.783],[98.4605,25.6915],[98.4175,25.673],[98.4095,25.611],[98.3025,25.552],[98.2645,25.601],[98.205,25.62],[98.155,25.5275],[98.157,25.459],[98.1365,25.386],[98.0705,25.315],[98.0065,25.2805],[97.939,25.217],[97.904,25.2185],[97.8755,25.2615],[97.8395,25.2725],[97.779,25.126],[97.72,25.0215],[97.73,24.915],[97.797,24.8515],[97.772,24.831],[97.707,24.8385],[97.6515,24.7955],[97.547,24.742],[97.568,24.7235],[97.571,24.6035],[97.556,24.4965],[97.532,24.4375],[97.6645,24.453],[97.6755,24.409],[97.7135,24.3775],[97.661,24.339],[97.6665,24.3],[97.74,24.291],[97.768,24.263],[97.7295,24.23],[97.7525,24.172],[97.7285,24.1185],[97.632,24.038],[97.6285,24.008],[97.5725,23.986],[97.528,23.9295],[97.631,23.8815],[97.7035,23.867],[97.7685,23.936],[97.84,23.9635],[97.901,24.0175],[97.985,24.035],[98.0395,24.0735],[98.109,24.095],[98.1975,24.102],[98.2225,24.117],[98.3595,24.099],[98.4175,24.122],[98.543,24.1315],[98.6175,24.091],[98.679,24.102],[98.7165,24.1305],[98.758,24.1275],[98.887,24.143],[98.897,24.1075],[98.866,24.069],[98.754,23.983],[98.6785,23.9575],[98.6785,23.913],[98.704,23.8445],[98.698,23.787],[98.789,23.784],[98.83,23.7285],[98.8515,23.6345],[98.891,23.6175],[98.8035,23.542],[98.829,23.4785],[98.8755,23.4865],[98.921,23.4205],[98.9195,23.355],[98.9385,23.314],[98.931,23.269],[98.8855,23.183],[98.9225,23.1885],[99.0025,23.163],[99.058,23.165],[99.0475,23.123],[99.103,23.0915],[99.146,23.109],[99.249,23.08],[99.3445,23.1375],[99.384,23.102],[99.522,23.077],[99.5225,22.9905],[99.56,22.9515],[99.5605,22.909],[99.496,22.912],[99.441,22.9425],[99.4515,22.861],[99.3995,22.8285],[99.3855,22.765],[99.3145,22.7395],[99.3605,22.664],[99.385,22.5755],[99.358,22.537],[99.381,22.5045],[99.296,22.4145],[99.278,22.362],[99.233,22.297],[99.2355,22.2505],[99.174,22.189],[99.1675,22.1495],[99.2725,22.101],[99.4085,22.0995],[99.4735,22.136],[99.5155,22.104],[99.642,22.106],[99.67,22.077],[99.756,22.0735],[99.8545,22.026],[99.862,22.0595],[99.904,22.0695],[99.9625,22.0525],[99.997,21.977],[99.9435,21.825],[99.991,21.7065],[100.0725,21.694],[100.1185,21.706],[100.169,21.6665],[100.1115,21.603],[100.126,21.5115],[100.1685,21.486],[100.205,21.513],[100.247,21.4665],[100.2915,21.4795],[100.3455,21.5285],[100.431,21.5415],[100.4795,21.459],[100.538,21.471],[100.572,21.451],[100.6855,21.505],[100.7225,21.5125],[100.801,21.604],[100.8865,21.688],[101.014,21.71],[101.0875,21.776],[101.11,21.751],[101.115,21.6935],[101.1525,21.6705],[101.1695,21.5955],[101.1475,21.562],[101.217,21.556],[101.217,21.4925],[101.1995,21.436],[101.141,21.411],[101.185,21.3285],[101.2505,21.293],[101.2165,21.2315],[101.292,21.1755],[101.3875,21.228],[101.446,21.2265],[101.483,21.246],[101.6075,21.238],[101.5865,21.189],[101.671,21.2035],[101.7185,21.143],[101.757,21.144],[101.7935,21.206],[101.8305,21.2055],[101.8445,21.2525],[101.799,21.2915],[101.7335,21.323],[101.7385,21.406],[101.7615,21.5015],[101.75,21.583],[101.824,21.5905],[101.8095,21.63],[101.7695,21.6595],[101.746,21.7285],[101.7725,21.817],[101.739,21.873],[101.6165,21.977],[101.632,22.013],[101.596,22.064],[101.5735,22.126],[101.6005,22.135],[101.5505,22.234],[101.5695,22.275],[101.625,22.2755],[101.67,22.372],[101.6725,22.476],[101.7605,22.5085],[101.823,22.4615],[101.8315,22.4215],[101.899,22.381],[101.9155,22.44],[101.999,22.4325],[102.05,22.4565],[102.126,22.4325],[102.1445,22.4],[102.2495,22.461],[102.346,22.583],[102.4045,22.6335],[102.384,22.6795],[102.434,22.7],[102.452,22.7525],[102.5105,22.7785],[102.5665,22.722],[102.6075,22.732],[102.656,22.6895],[102.689,22.703],[102.789,22.6255],[102.8255,22.6265],[102.8835,22.589],[102.88,22.5605],[102.9235,22.507],[103.032,22.4425],[103.0805,22.451],[103.0725,22.4915],[103.14,22.5405],[103.1855,22.645],[103.2835,22.684],[103.2875,22.7375],[103.3205,22.7805],[103.372,22.7995],[103.44,22.7535],[103.4265,22.713],[103.489,22.6185],[103.531,22.595],[103.5795,22.664],[103.564,22.706],[103.6385,22.795],[103.767,22.6905],[103.826,22.6155],[103.962,22.5065],[104.0125,22.524],[104.029,22.6875],[104.046,22.734],[104.115,22.8115],[104.256,22.845],[104.269,22.743],[104.3395,22.723],[104.353,22.6935],[104.478,22.7635],[104.5785,22.855],[104.6735,22.8185],[104.7355,22.8275],[104.771,22.8955],[104.8395,22.9215],[104.8655,22.9625],[104.8315,23.0035],[104.802,23.116],[104.876,23.1255],[104.8815,23.166],[104.9275,23.1565],[104.9625,23.1985],[105.025,23.219],[105.082,23.267],[105.107,23.246],[105.179,23.288],[105.2275,23.26],[105.2585,23.3195],[105.322,23.393],[105.3515,23.345],[105.4115,23.288],[105.445,23.2955],[105.499,23.2025],[105.543,23.1935],[105.5735,23.161],[105.5695,23.0755],[105.645,23.083],[105.825,22.9965],[105.8725,22.9335],[106.003,22.9475],[106.001,22.992],[106.2,22.9865],[106.238,22.9515],[106.2865,22.8675],[106.4405,22.8895],[106.5005,22.909],[106.513,22.9485],[106.567,22.919],[106.593,22.933],[106.6485,22.867],[106.6915,22.8915],[106.783,22.8145],[106.833,22.797],[106.7645,22.7415],[106.753,22.6925],[106.6935,22.5845],[106.6485,22.5765],[106.607,22.5985],[106.5825,22.4825],[106.5575,22.459],[106.5855,22.378],[106.5715,22.3415],[106.6595,22.334],[106.69,22.279],[106.6985,22.2095],[106.673,22.181],[106.71,22.1015],[106.6825,22.002],[106.7845,22.004],[106.8125,21.973],[106.919,21.9745],[106.9335,21.9295],[106.988,21.9485],[107.05,21.928],[107.0635,21.896],[107.0045,21.8335],[107.0855,21.808],[107.1965,21.7225],[107.249,21.7055],[107.301,21.7425],[107.36,21.6645],[107.361,21.612],[107.4825,21.657],[107.4935,21.605],[107.5435,21.5895],[107.594,21.605],[107.6885,21.6115],[107.8155,21.6585],[107.872,21.647],[107.8965,21.596],[107.9425,21.57],[107.955,21.537],[108.0335,21.5475],[108.1005,21.4735],[108.0955,21.4525],[108.1345,21.2755],[108.205,21.299],[108.2895,21.311],[108.3775,21.354],[108.485,21.349],[108.576,21.382],[108.634,21.417],[108.718,21.392],[108.8375,21.392],[108.8715,21.326],[108.935,21.269],[108.957,21.218],[108.8875,21.138],[108.8695,21.074],[108.8785,20.964],[108.908,20.904],[108.9635,20.851],[109.01,20.826],[109.0705,20.75],[109.1315,20.717],[109.234,20.704],[109.493,20.707],[109.5505,20.546],[109.6085,20.478],[109.6485,20.395],[109.656,20.351],[109.706,20.216],[109.54,20.183],[109.47,20.152],[109.401,20.1],[109.295,20.117],[109.2525,20.113],[109.137,20.0645],[109.0215,19.957],[108.9725,19.89],[108.949,19.8255],[108.887,19.747],[108.761,19.636],[108.6605,19.5735],[108.5915,19.554],[108.4785,19.4775],[108.4385,19.4035],[108.3955,19.245],[108.389,19.1795],[108.413,18.8105],[108.466,18.4905],[108.4945,18.415],[108.5655,18.3405],[108.8285,18.159],[109.0375,18.0615],[109.086,18.0465],[109.532,17.9615],[109.617,17.962],[109.776,17.9955],[109.81,18.0115],[110.2575,18.2685],[110.6055,18.4915],[110.6775,18.564],[111.396,19.781],[111.4515,19.8655],[111.761,20.2905],[112.4895,21.286],[112.8485,21.373],[114.012,21.6125],[114.0585,21.627],[115.22,22.135],[116.582,22.751],[117.311,23.018],[117.382,23.057],[117.822,23.373],[117.8585,23.4065],[118.3885,24.015],[119.6055,24.813],[119.64,24.841],[120.1,25.2935],[120.129,25.3295],[120.597,26.0555],[120.7085,26.292],[121.0335,26.859],[121.321,27.3585],[122.0925,28.271],[122.466,28.779],[122.4825,28.805],[123.1545,30.084],[123.3815,30.6605],[123.388,30.7695],[123.354,30.8405],[123.3105,30.8855],[122.4465,31.536],[122.407,31.6415],[121.8665,33.077],[121.8345,33.1305],[121.5405,33.4805],[120.5915,35.3725],[121.0525,35.7455],[122.413,36.587],[122.665,36.733],[122.7445,36.798],[122.8125,36.9145],[122.949,37.3465],[122.956,37.417],[122.9315,37.4875],[122.8375,37.596],[122.7725,37.63],[122.6645,37.648],[122.5805,37.636],[122.5055,37.651],[122.3885,37.639],[122.2925,37.717],[122.211,37.75],[122.08,37.774],[122.026,37.77],[121.93,37.737],[121.8155,37.661],[121.6845,37.754],[121.6245,37.776],[121.5275,37.785],[121.4235,37.827],[121.338,37.834],[121.277,37.879],[121.1585,37.915],[121.1985,38.017],[121.186,38.089],[121.1245,38.17],[121.0835,38.197],[121.1475,38.266],[121.178,38.348],[121.1645,38.46],[121.217,38.523],[121.2965,38.539],[121.4075,38.609],[121.4385,38.4915],[121.5085,38.4275],[121.62,38.3975],[121.7315,38.411],[121.7935,38.444],[121.838,38.4925],[121.8645,38.589],[121.8345,38.664],[121.9475,38.6875],[121.9675,38.596],[122.0375,38.532],[122.149,38.502],[122.261,38.5155],[122.3225,38.5485],[122.387,38.638],[122.388,38.7225],[122.358,38.776],[122.306,38.8185],[122.238,38.844],[122.1635,38.849],[122.1755,38.883],[122.2745,38.949],[122.363,38.945],[122.4595,38.966],[122.517,38.889],[122.6265,38.829],[122.7275,38.81],[122.8515,38.824],[122.9855,38.873],[123.0935,38.833],[123.1645,38.827],[123.294,38.846],[123.4165,38.909],[123.4515,38.951],[123.4755,39.015],[123.453,39.123],[123.395,39.206],[123.3,39.273],[123.296,39.313],[123.3415,39.377],[123.366,39.506],[123.426,39.531],[123.6045,39.562],[123.7065,39.545],[123.854,39.564],[123.9485,39.605],[124.0915,39.612],[124.1595,39.743],[124.173,39.829],[124.21,39.8575],[124.2375,39.9265],[124.284,39.9275],[124.2925,39.968],[124.354,39.9705],[124.372,40.017],[124.3375,40.0465],[124.3505,40.0805],[124.412,40.139],[124.481,40.1725],[124.5095,40.2185],[124.6425,40.301],[124.706,40.313],[124.7495,40.379],[124.817,40.4045],[124.8895,40.4755],[124.922,40.4545],[125.0285,40.492],[125.0085,40.525],[125.0955,40.5565],[125.186,40.6075],[125.2575,40.613],[125.267,40.6475],[125.329,40.637],[125.4515,40.6745],[125.486,40.7215],[125.5245,40.7165],[125.5745,40.7825],[125.6295,40.799],[125.702,40.8635],[125.7955,40.865],[125.858,40.8835],[125.958,40.8805],[126.0185,40.9015],[126.0515,40.9635],[126.1035,41.005],[126.1305,41.058],[126.2345,41.145],[126.274,41.155],[126.3095,41.215],[126.3535,41.243],[126.3625,41.281],[126.433,41.352],[126.4865,41.371],[126.505,41.435],[126.588,41.5625],[126.5655,41.598],[126.725,41.705],[126.9445,41.805],[127.054,41.7],[127.03,41.672],[127.104,41.64],[127.1615,41.54],[127.3375,41.4635],[127.4105,41.4555],[127.493,41.4755],[127.559,41.43],[127.8475,41.4195],[127.967,41.4355],[128.032,41.391],[128.0825,41.393],[128.108,41.3605],[128.201,41.4085],[128.2355,41.4985],[128.2965,41.538],[128.311,41.589],[128.242,41.679],[128.1645,41.7105],[128.1085,41.789],[128.0965,41.838],[128.0995,41.945],[128.031,41.9955],[128.0845,42.0205],[128.285,42.024],[128.4575,42.0185],[128.506,41.997],[128.5765,42.0],[128.6305,42.037],[128.6765,42.012],[128.745,42.0515],[128.915,42.01],[129.0515,42.138],[129.1145,42.139],[129.203,42.2065],[129.1765,42.2595],[129.225,42.278],[129.2235,42.3525],[129.304,42.4155],[129.421,42.433],[129.4945,42.4125],[129.5325,42.3745],[129.607,42.437],[129.704,42.427],[129.7445,42.467],[129.732,42.4985],[129.785,42.6795],[129.76,42.708],[129.808,42.789],[129.8365,42.865],[129.8515,42.9615],[129.9005,43.005],[129.954,42.9785],[130.063,42.9675],[130.0985,42.9865],[130.138,42.902],[130.1725,42.914],[130.262,42.9035],[130.2375,42.8205],[130.253,42.7065],[130.37,42.6205],[130.4145,42.5675],[130.5245,42.547],[130.5785,42.4845],[130.5785,42.43],[130.637,42.4185],[130.5925,42.446],[130.592,42.4845],[130.5535,42.5185],[130.627,42.5975],[130.588,42.665],[130.5215,42.7015],[130.4575,42.686],[130.4055,42.729],[130.4595,42.77],[130.526,42.785],[130.5565,42.813],[130.659,42.845],[130.776,42.8395],[130.828,42.8805],[130.882,42.8505],[130.9415,42.8745],[131.0355,42.8605],[131.0105,42.9105],[131.1085,42.9135],[131.14,42.943],[131.0955,43.021],[131.1145,43.067],[131.21,43.1485],[131.1985,43.234],[131.2485,43.2675],[131.273,43.3755],[131.311,43.395],[131.2695,43.493],[131.207,43.5265],[131.203,43.582],[131.233,43.6655],[131.2095,43.833],[131.261,43.9365],[131.2345,43.965],[131.2555,44.0305],[131.303,44.044],[131.1045,44.7075],[131.0085,44.8105],[130.977,44.863],[131.0945,44.8945],[131.082,44.922],[131.152,44.9445],[131.1985,44.916],[131.271,44.9215],[131.35,44.9875],[131.427,44.965],[131.5355,45.017],[131.6245,45.0725],[131.6875,45.13],[131.643,45.157],[131.6745,45.2135],[131.781,45.244],[131.828,45.31],[131.91,45.337],[131.9235,45.2875],[132.004,45.252],[132.8645,45.0575],[132.9385,45.0285],[133.0375,45.0565],[133.0615,45.0945],[133.1305,45.128],[133.1045,45.2645],[133.1115,45.3505],[133.135,45.365],[133.141,45.434],[133.213,45.5135],[133.386,45.578],[133.44,45.6485],[133.435,45.703],[133.476,45.7805],[133.4615,45.8355],[133.505,45.8885],[133.5765,45.8665],[133.61,45.9415],[133.67,45.9435],[133.6755,45.9885],[133.7355,46.054],[133.682,46.1375],[133.823,46.2235],[133.9105,46.2635],[133.8705,46.3615],[133.9375,46.3795],[133.9275,46.4195],[133.8455,46.481],[133.883,46.521],[133.913,46.596],[134.0135,46.666],[134.0265,46.7545],[134.016,46.811],[134.0355,46.8855],[134.068,46.933],[134.0555,46.975],[134.0965,47.009],[134.14,47.0925],[134.218,47.1075],[134.22,47.187],[134.15,47.253],[134.1695,47.3185],[134.2555,47.3665],[134.311,47.4335],[134.486,47.443],[134.5705,47.4895],[134.5725,47.5185],[134.6805,47.598],[134.6755,47.625],[134.7435,47.6835],[134.775,47.74],[134.6635,47.825],[134.6705,47.8775],[134.607,47.9015],[134.593,47.9475],[134.544,47.985],[134.5445,48.0245],[134.6275,48.097],[134.6675,48.152],[134.676,48.2575],[134.717,48.28],[134.768,48.3575],[134.688,48.408],[134.5805,48.407],[134.4705,48.422],[134.4085,48.388],[134.337,48.379],[134.1885,48.3835],[134.14,48.338],[134.0555,48.346],[134.0075,48.309],[133.9495,48.3045],[133.7465,48.257],[133.7115,48.1875],[133.58,48.1885],[133.5565,48.1305],[133.4645,48.109],[133.3915,48.1235],[133.266,48.093],[133.182,48.1085],[133.0495,48.105],[133.008,48.0405],[132.863,47.9915],[132.7805,47.925],[132.719,47.962],[132.652,47.9355],[132.6805,47.872],[132.5985,47.8105],[132.6035,47.742],[132.5485,47.7145],[132.485,47.716],[132.3845,47.7505],[132.309,47.757],[132.253,47.708],[131.989,47.701],[131.9425,47.6615],[131.902,47.691],[131.812,47.667],[131.7305,47.7045],[131.678,47.7025],[131.622,47.657],[131.5665,47.665],[131.5475,47.7265],[131.425,47.7435],[131.366,47.7265],[131.2545,47.735],[131.175,47.6955],[131.0895,47.683],[130.9935,47.696],[130.9575,47.7205],[130.9515,47.815],[130.864,47.936],[130.6905,48.04],[130.6535,48.1075],[130.752,48.19],[130.765,48.2475],[130.8355,48.3015],[130.7665,48.3585],[130.7305,48.444],[130.758,48.4975],[130.66,48.4905],[130.6085,48.502],[130.6065,48.5735],[130.522,48.626],[130.607,48.775],[130.682,48.8655],[130.64,48.883],[130.5375,48.8535],[130.421,48.904],[130.294,48.869],[130.2245,48.865],[130.1905,48.9],[130.0505,48.972],[129.998,49.02],[129.928,49.0325],[129.9275,49.0735],[129.8585,49.1085],[129.8425,49.177],[129.7525,49.204],[129.7535,49.252],[129.7105,49.291],[129.6,49.276],[129.548,49.309],[129.5365,49.398],[129.4305,49.4415],[129.369,49.418],[129.378,49.3755],[129.309,49.357],[129.261,49.392],[129.1745,49.3885],[129.126,49.3505],[129.0735,49.3535],[128.9835,49.457],[128.8605,49.4895],[128.757,49.476],[128.747,49.513],[128.802,49.5505],[128.6585,49.57],[128.553,49.602],[128.367,49.5835],[128.336,49.542],[128.2345,49.5615],[128.2015,49.538],[128.0345,49.5555],[127.959,49.5965],[127.885,49.5695],[127.7985,49.5935],[127.7815,49.622],[127.687,49.667],[127.673,49.748],[127.6485,49.7765],[127.5565,49.7905],[127.5255,49.819],[127.5415,49.917],[127.499,49.972],[127.5015,50.0595],[127.572,50.1285],[127.6035,50.181],[127.6015,50.2375],[127.3805,50.285],[127.3255,50.335],[127.3615,50.396],[127.353,50.4435],[127.292,50.457],[127.311,50.524],[127.367,50.574],[127.2875,50.664],[127.2915,50.7545],[127.233,50.774],[127.135,50.9125],[127.0465,50.9605],[126.986,51.021],[126.9175,51.057],[126.894,51.2035],[126.973,51.298],[126.9455,51.3335],[126.8675,51.3095],[126.9155,51.2615],[126.8435,51.253],[126.8135,51.3295],[126.924,51.363],[126.896,51.408],[126.801,51.4205],[126.776,51.444],[126.833,51.5345],[126.7085,51.5685],[126.7215,51.6245],[126.7155,51.727],[126.671,51.727],[126.6175,51.7745],[126.538,51.885],[126.465,51.9365],[126.4385,52.0255],[126.5125,52.053],[126.5565,52.12],[126.49,52.1595],[126.3005,52.206],[126.346,52.2625],[126.311,52.329],[126.347,52.383],[126.2625,52.4735],[126.181,52.4775],[126.2015,52.531],[126.068,52.603],[126.012,52.5785],[125.973,52.6085],[125.9835,52.673],[126.053,52.676],[126.0375,52.7345],[126.096,52.7755],[126.0085,52.7925],[125.9395,52.763],[125.909,52.8185],[125.8295,52.8955],[125.6555,52.869],[125.6665,52.9225],[125.7275,52.944],[125.734,52.9915],[125.6725,53.0075],[125.605,53.0815],[125.5315,53.0545],[125.498,53.0885],[125.3025,53.146],[125.158,53.204],[124.9755,53.196],[124.824,53.143],[124.7115,53.1515],[124.717,53.187],[124.637,53.207],[124.4965,53.2035],[124.37,53.2525],[124.3205,53.329],[124.2325,53.378],[124.11,53.3465],[124.0615,53.399],[123.8795,53.4805],[123.6855,53.4985],[123.622,53.5455],[123.4785,53.528],[123.3755,53.5365],[123.3045,53.5595],[123.223,53.5495],[123.1375,53.498],[123.06,53.5075],[122.8495,53.4565],[122.596,53.464],[122.4295,53.443],[122.3495,53.5005],[122.2435,53.4635],[122.168,53.471],[122.079,53.4205],[121.9485,53.429],[121.8175,53.4155],[121.6945,53.3905],[121.572,53.3475],[121.41,53.317],[121.327,53.323],[121.276,53.289],[121.1275,53.275],[120.941,53.2955],[120.8155,53.2685],[120.816,53.241],[120.6835,53.171],[120.636,53.104],[120.5555,53.0805],[120.3615,52.943],[120.281,52.861],[120.0265,52.7765],[120.062,52.707],[120.027,52.6355],[120.0785,52.583],[120.1845,52.577],[120.282,52.622],[120.391,52.615],[120.4775,52.629],[120.6185,52.5705],[120.6865,52.5175],[120.6835,52.429],[120.618,52.361],[120.6215,52.3245],[120.7515,52.2555],[120.739,52.2045],[120.7795,52.1635],[120.755,52.103],[120.679,52.036],[120.712,52.007],[120.6475,51.9225],[120.5425,51.9075],[120.544,51.883],[120.3915,51.8305],[120.356,51.7875],[120.3045,51.781],[120.1665,51.678],[120.0505,51.6305],[120.045,51.5525],[119.978,51.502],[119.9955,51.458],[119.933,51.354],[119.8755,51.333],[119.8685,51.2935],[119.803,51.278],[119.812,51.2315],[119.7525,51.2125],[119.7815,51.172],[119.754,51.0885],[119.663,51.012],[119.5835,50.975],[119.5105,50.8975],[119.5205,50.862],[119.499,50.766],[119.442,50.694],[119.3735,50.68],[119.3475,50.628],[119.274,50.6035],[119.2425,50.4465],[119.145,50.388],[119.247,50.3415],[119.351,50.3575],[119.31,50.2165],[119.3295,50.172],[119.274,50.105],[119.084,49.9845],[118.9865,49.9765],[118.9375,49.9915],[118.7345,49.945],[118.648,49.9475],[118.555,49.922],[118.372,49.786],[118.2395,49.7375],[118.2045,49.69],[118.1125,49.658],[118.074,49.614],[117.974,49.62],[117.891,49.5935],[117.881,49.5155],[117.793,49.52],[117.634,49.5645],[117.4715,49.6265],[117.2735,49.631],[117.0615,49.6855],[116.7135,49.8455],[116.609,49.707],[116.3255,49.289],[116.0365,48.8675],[116.0725,48.8165],[116.0205,48.757],[115.811,48.547],[115.7985,48.4635],[115.8135,48.259],[115.5475,48.1615],[115.5215,48.111],[115.5455,48.067],[115.5705,47.919],[115.742,47.8035],[115.9355,47.681],[115.9555,47.6875],[116.1115,47.819],[116.2535,47.8785],[116.4395,47.838],[116.6765,47.889],[116.871,47.891],[117.087,47.8205],[117.375,47.64],[117.482,47.759],[117.805,48.014],[117.9135,48.0215],[118.0345,48.0125],[118.1,48.0315],[118.231,48.041],[118.2885,48.0035],[118.4145,48.014],[118.455,47.996],[118.555,47.9935],[118.755,47.772],[119.1215,47.664],[119.143,47.5425],[119.3575,47.4795],[119.3215,47.425],[119.4635,47.358],[119.4775,47.3275],[119.5525,47.301],[119.571,47.2435],[119.6235,47.244],[119.708,47.1935],[119.799,47.059],[119.7835,47.0245],[119.855,46.9205],[119.9215,46.9025],[119.8985,46.8655],[119.932,46.7955],[119.9015,46.7555],[119.929,46.7205],[119.89,46.6635],[119.807,46.6675],[119.7745,46.6255],[119.6805,46.5905],[119.6375,46.625],[119.4305,46.6385],[119.3835,46.6085],[119.3005,46.6085],[119.245,46.643],[119.101,46.6475],[119.0,46.751],[118.916,46.724],[118.897,46.776],[118.8425,46.7705],[118.7835,46.717],[118.792,46.687],[118.6735,46.696],[118.635,46.72],[118.5755,46.6935],[118.437,46.7065],[118.4025,46.727],[118.301,46.7385],[118.184,46.6815],[118.1155,46.6785],[118.0565,46.6345],[117.889,46.5915],[117.834,46.5315],[117.702,46.5135],[117.6325,46.579],[117.494,46.5985],[117.415,46.5805],[117.4405,46.5255],[117.392,46.478],[117.3685,46.3605],[117.243,46.365],[117.0885,46.3545],[116.8315,46.3835],[116.761,46.329],[116.658,46.321],[116.5775,46.2885],[116.566,46.2535],[116.4345,46.1375],[116.398,46.126],[116.264,45.9615],[116.2375,45.8725],[116.268,45.852],[116.28,45.7705],[116.2145,45.7415],[116.17,45.6885],[116.0305,45.6825],[116.015,45.656],[115.932,45.6295],[115.691,45.458],[115.5575,45.436],[115.356,45.3895],[115.165,45.396],[114.974,45.376],[114.9155,45.3815],[114.737,45.4375],[114.541,45.3845],[114.528,45.2925],[114.456,45.206],[114.3845,45.1435],[114.177,45.034],[114.1335,44.9665],[114.056,44.9255],[113.907,44.9145],[113.877,44.88],[113.7765,44.839],[113.6255,44.7425],[113.506,44.772],[113.1275,44.7955],[112.931,44.8395],[112.8635,44.837],[112.702,44.881],[112.585,44.936],[112.519,45.0125],[112.4255,45.074],[112.3745,45.0615],[112.1115,45.0715],[112.009,45.092],[111.874,45.038],[111.7695,44.9805],[111.605,44.756],[111.5535,44.6455],[111.561,44.5715],[111.4635,44.4715],[111.4095,44.3445],[111.425,44.3175],[111.519,44.271],[111.539,44.192],[111.609,44.106],[111.693,44.0355],[111.788,43.996],[111.874,43.932],[111.9545,43.8275],[111.968,43.744],[111.946,43.69],[111.883,43.672],[111.799,43.6695],[111.573,43.491],[111.4535,43.4895],[111.4015,43.4765],[111.327,43.425],[111.213,43.3995],[111.006,43.322],[110.759,43.095],[110.6825,43.035],[110.6295,42.9395],[110.464,42.8325],[110.436,42.782],[110.334,42.736],[110.1365,42.672],[110.101,42.639],[109.9035,42.6305],[109.669,42.5515],[109.5285,42.4675],[109.469,42.4535],[109.284,42.4345],[109.0245,42.454],[108.96,42.4415],[108.8385,42.3955],[108.7955,42.414],[108.716,42.4095],[108.5275,42.441],[108.306,42.436],[108.241,42.4605],[108.0165,42.4315],[107.919,42.4025],[107.7275,42.412],[107.566,42.4115],[107.496,42.455],[107.453,42.4565],[107.293,42.408],[107.257,42.363],[107.041,42.3145],[106.786,42.2915],[106.5595,42.219],[106.507,42.194],[105.697,41.931],[105.386,41.795],[105.289,41.747],[105.217,41.7255],[105.108,41.6545],[105.013,41.581],[104.9165,41.6525],[104.682,41.644],[104.517,41.6595],[104.499,41.6795],[104.4975,41.7725],[104.5235,41.8735],[104.078,41.8005],[103.891,41.7955],[103.411,41.8815],[102.976,42.0375],[102.767,42.1255],[102.5505,42.159],[102.432,42.147],[102.1715,42.204],[102.0965,42.2],[102.0245,42.224],[102.0485,42.2525],[101.8005,42.5035],[101.567,42.5255],[100.848,42.672],[100.3185,42.687],[100.268,42.6355],[99.964,42.646],[99.5045,42.5655],[98.9005,42.61],[98.5425,42.6375],[98.1925,42.652],[97.5555,42.742],[97.1715,42.7945],[96.9775,42.7545],[96.7475,42.755],[96.3875,42.7245],[96.363,42.899],[95.909,43.2355],[95.877,43.277],[95.8575,43.4135],[95.736,43.5945],[95.6475,43.7765],[95.623,43.8515],[95.523,44.005],[95.442,44.005],[95.3225,44.0255],[95.3475,44.089],[95.3725,44.2255],[95.4065,44.2415],[95.41,44.294],[94.9985,44.251],[94.945,44.288],[94.713,44.345],[94.6105,44.4425],[94.5165,44.476],[94.4625,44.5105],[94.3605,44.515],[94.3265,44.5825],[94.276,44.605],[94.2115,44.6675],[94.15,44.6835],[94.062,44.7325],[93.7305,44.8625],[93.671,44.91],[93.5225,44.9575],[93.428,44.9565],[93.366,44.988],[93.2855,44.9825],[93.168,45.0135],[92.9295,45.0155],[92.8815,45.0465],[92.776,45.05],[92.645,45.0205],[92.5495,45.0175],[92.4905,45.0],[92.3135,45.027],[92.235,45.0135],[92.0995,45.0755],[92.0575,45.083],[91.801,45.0815],[91.691,45.062],[91.609,45.07],[91.492,45.1045],[91.43,45.1565],[91.373,45.109],[91.329,45.129],[91.2415,45.132],[91.122,45.214],[90.995,45.214],[90.931,45.192],[90.8645,45.204],[90.895,45.2505],[90.8115,45.325],[90.77,45.4285],[90.6705,45.487],[90.6785,45.6055],[90.715,45.7275],[90.848,45.8875],[91.022,46.0225],[91.0105,46.1245],[90.975,46.1625],[90.949,46.229],[90.8915,46.3125],[90.9695,46.3605],[91.0095,46.461],[91.0525,46.511],[91.066,46.5755],[91.0145,46.5805],[91.0485,46.72],[91.016,46.764],[90.937,46.826],[90.9555,46.8775],[90.902,46.9575],[90.8295,46.9895],[90.778,46.992],[90.622,47.155],[90.5945,47.168],[90.484,47.313],[90.5205,47.377],[90.458,47.407],[90.4715,47.491],[90.4,47.539],[90.298,47.6925],[90.1395,47.7225],[90.0655,47.7925],[90.0675,47.874],[89.9595,47.887],[89.954,47.8345],[89.7565,47.829],[89.727,47.895],[89.6545,47.9055],[89.5925,47.968],[89.582,48.03],[89.4015,48.0305],[89.291,48.012],[89.234,47.976],[89.156,47.995],[89.0625,47.9885],[89.0095,48.05],[88.911,48.109],[88.824,48.1],[88.715,48.174],[88.6375,48.1785],[88.566,48.276],[88.603,48.339],[88.5135,48.3795],[88.437,48.387],[88.3495,48.4355],[88.357,48.4585],[88.2425,48.501],[88.175,48.499],[88.1425,48.5275],[87.969,48.5755],[88.0675,48.6805],[87.9995,48.7605],[87.935,48.7555],[87.83,48.7985],[87.7815,48.8695],[87.734,48.881],[87.754,48.9285],[87.868,48.948],[87.8765,49.0145],[87.827,49.051],[87.866,49.1115],[87.8145,49.17],[87.695,49.1755],[87.564,49.135],[87.4265,49.0695],[87.286,49.116],[87.199,49.143],[87.104,49.151],[87.0565,49.1295],[86.996,49.1425],[86.887,49.1325],[86.8475,49.0655],[86.749,49.0145],[86.727,48.9505],[86.7565,48.894],[86.8165,48.8565],[86.761,48.804],[86.773,48.7205],[86.6355,48.627],[86.584,48.54],[86.407,48.4785],[86.3215,48.488],[86.2325,48.432],[85.9115,48.436],[85.7875,48.4175],[85.7255,48.373],[85.6595,48.242],[85.5935,48.196],[85.5595,48.149],[85.5295,48.025],[85.543,48.0095],[85.6145,47.54],[85.608,47.5095],[85.6805,47.434],[85.6745,47.312],[85.6985,47.288],[85.6825,47.223],[85.5765,47.1375],[85.5295,47.0515],[85.4125,47.06],[85.3825,47.046],[85.2995,47.065],[85.217,47.0525],[85.194,47.0125],[85.0955,46.964],[85.0785,46.931],[84.983,46.915],[84.944,46.863],[84.858,46.9285],[84.8475,46.954],[84.7095,47.0095],[84.5795,46.9995],[84.5065,46.9755],[84.436,47.009],[84.379,46.996],[84.2585,47.003],[84.1655,46.994],[84.1265,46.9675],[84.0565,46.9685],[83.9845,46.9955],[83.9175,46.973],[83.7835,47.0245],[83.701,47.0225],[83.578,47.0605],[83.4575,47.132],[83.4135,47.116],[83.354,47.1745],[83.2395,47.179],[83.151,47.2345],[83.0515,47.2255],[82.993,47.0645],[82.938,47.016],[82.9325,46.972],[82.869,46.83],[82.8295,46.7775],[82.782,46.669],[82.725,46.4915],[82.608,46.2985],[82.512,46.1545],[82.456,45.977],[82.3755,45.964],[82.3355,45.939],[82.345,45.7955],[82.288,45.709],[82.259,45.6165],[82.28,45.535],[82.4375,45.4595],[82.543,45.421],[82.5875,45.3445],[82.582,45.2215],[82.475,45.177],[82.3405,45.209],[82.2675,45.2415],[82.151,45.222],[82.088,45.243],[81.922,45.229],[81.774,45.386],[81.7135,45.359],[81.6325,45.3565],[81.452,45.262],[81.382,45.264],[81.282,45.2395],[81.2035,45.237],[81.1195,45.215],[81.0245,45.1635],[80.957,45.1675],[80.8985,45.125],[80.74,45.1615],[80.698,45.1335],[80.576,45.11],[80.4375,45.097],[80.382,45.04],[80.3255,45.0695],[80.2135,45.0235],[80.1525,45.0525],[80.062,45.036],[80.013,44.98],[79.9555,44.9615],[79.882,44.9105],[79.9535,44.882],[79.9445,44.85],[80.034,44.804],[80.128,44.823],[80.173,44.7935],[80.2195,44.7305],[80.3135,44.7035],[80.4105,44.6105],[80.347,44.4825],[80.384,44.3775],[80.4065,44.2495],[80.396,44.11],[80.4455,44.071],[80.4545,43.982],[80.506,43.906],[80.521,43.8245],[80.6855,43.5795],[80.751,43.469],[80.735,43.393],[80.6855,43.3375],[80.698,43.316],[80.7785,43.3115],[80.765,43.267],[80.8075,43.1775],[80.7815,43.138],[80.6575,43.15],[80.5865,43.1365],[80.556,43.102],[80.485,43.0705],[80.384,43.046],[80.388,43.003],[80.4855,42.945],[80.5395,42.936],[80.5985,42.8945],[80.329,42.8275],[80.2775,42.838],[80.223,42.713],[80.1765,42.6715],[80.1625,42.625],[80.2205,42.533],[80.267,42.503],[80.2075,42.4675],[80.2335,42.4045],[80.232,42.349],[80.2835,42.2355],[80.1745,42.211],[80.1655,42.101],[80.225,42.0595],[80.138,42.0315],[80.0265,42.049],[79.912,42.042],[79.8545,42.022],[79.802,41.9195],[79.765,41.893],[79.638,41.8935],[79.6075,41.857],[79.4975,41.8365],[79.4095,41.841],[79.347,41.7935],[79.323,41.811],[79.2095,41.725],[79.124,41.7225],[79.083,41.689],[78.941,41.6445],[78.815,41.5585],[78.7245,41.555],[78.6445,41.469],[78.5575,41.4765],[78.532,41.442],[78.382,41.3915],[78.1895,41.394],[78.146,41.3675],[78.1215,41.2275],[78.0215,41.193],[77.912,41.186],[77.8045,41.1225],[77.796,41.0485],[77.765,41.015],[77.687,41.0035],[77.6465,41.0145],[77.5835,40.994],[77.4675,40.9985],[77.3705,41.0405],[77.2625,41.0045],[77.242,41.0265],[77.1175,41.027],[77.0855,41.063],[76.9635,41.0585],[76.94,41.0305],[76.878,41.0275],[76.8195,40.977],[76.7625,40.953],[76.7025,40.7875],[76.64,40.762],[76.654,40.622],[76.6115,40.6105],[76.552,40.5455],[76.5385,40.474],[76.44,40.389],[76.3395,40.3495],[76.3125,40.406],[76.2515,40.418],[76.1855,40.38],[76.1315,40.397],[75.962,40.3745],[75.93,40.302],[75.8115,40.3245],[75.718,40.287],[75.6635,40.355],[75.68,40.4215],[75.7235,40.472],[75.647,40.505],[75.6235,40.5455],[75.63,40.623],[75.5955,40.6475],[75.539,40.6475],[75.462,40.603],[75.4085,40.5525],[75.3725,40.5485],[75.2385,40.461],[75.124,40.458],[75.09,40.435],[75.018,40.4665],[74.987,40.452],[74.921,40.493],[74.838,40.522],[74.796,40.435],[74.9055,40.3535],[74.862,40.324],[74.79,40.3535],[74.701,40.325],[74.6605,40.273],[74.5905,40.282],[74.5255,40.209],[74.3445,40.0935],[74.304,40.115],[74.2,40.1235],[74.066,40.0785],[74.042,40.0995],[73.983,40.0485],[73.977,40.005],[73.907,39.9205],[73.903,39.8615],[73.848,39.8345],[73.832,39.7815],[73.904,39.7445],[73.949,39.6215],[73.8785,39.5425],[73.8745,39.483],[73.7255,39.461],[73.638,39.474],[73.6005,39.4595],[73.592,39.4115],[73.526,39.3915],[73.5585,39.355],[73.535,39.323],[73.569,39.244],[73.6135,39.2475],[73.662,39.165],[73.7205,39.112],[73.751,39.0255],[73.8125,39.043],[73.854,38.9525],[73.8065,38.933],[73.7575,38.9425],[73.699,38.878],[73.768,38.778],[73.744,38.732],[73.805,38.664],[73.8035,38.612],[73.8985,38.581],[73.9235,38.5425],[73.9895,38.5245],[74.0395,38.544],[74.0745,38.611],[74.1215,38.6415],[74.187,38.6405],[74.2845,38.5985],[74.5065,38.4715],[74.6505,38.446],[74.6955,38.425],[74.6655,38.3805],[74.706,38.2825],[74.698,38.228],[74.8,38.2025],[74.8205,38.0855],[74.8685,38.0255],[74.9165,38.0245],[74.913,37.8565],[74.952,37.809],[74.967,37.752],[74.897,37.6655],[74.9395,37.603],[74.932,37.5685],[75.0315,37.501],[75.0575,37.5245],[75.1485,37.421],[75.096,37.368],[75.1285,37.323],[74.9105,37.275],[74.89,37.234]]],[[[110.9815,15.7675],[111.0035,15.6905],[111.041,15.6385],[111.1015,15.595],[111.164,15.575],[111.2435,15.5765],[112.5485,15.822],[112.656,15.862],[112.753,15.9415],[112.7985,16.026],[112.9475,16.616],[112.947,16.7125],[112.9205,16.772],[112.8585,16.838],[112.4685,17.108],[112.3855,17.1615],[112.2805,17.1965],[111.525,17.319],[111.4285,17.3015],[111.357,17.273],[111.2975,17.229],[111.243,17.128],[110.9865,15.8245],[110.9815,15.7675]]],[[[112.1905,15.7105],[112.23,15.6845],[112.29,15.72],[112.265,15.7585],[112.2075,15.7455],[112.1905,15.7105]]],[[[112.857,9.5385],[112.89,9.523],[112.954,9.5475],[113.064,9.64],[113.078,9.6755],[113.05,9.7015],[112.9435,9.6615],[112.857,9.5385]]],[[[113.5365,15.575],[113.6145,15.4875],[113.876,15.363],[114.069,15.3605],[114.1785,15.375],[114.279,15.424],[114.3545,15.544],[114.5705,15.543],[114.655,15.592],[114.7435,15.6845],[114.966,16.021],[114.993,16.136],[114.9045,16.275],[114.8205,16.335],[114.67,16.2955],[114.5125,16.195],[114.2165,16.1435],[113.977,16.03],[113.83,15.866],[113.7835,15.7035],[113.617,15.638],[113.5365,15.575]]],[[[114.041,10.9125],[114.0865,10.891],[114.1165,10.911],[114.114,10.95],[114.0715,10.9545],[114.041,10.9125]]],[[[115.4735,9.9085],[115.4975,9.8615],[115.578,9.863],[115.602,9.907],[115.5695,9.9445],[115.506,9.9565],[115.4735,9.9085]]],[[[117.6955,15.21],[117.7015,15.1325],[117.7255,15.0925],[117.826,15.086],[117.855,15.1],[117.86,15.1645],[117.804,15.19],[117.767,15.23],[117.71,15.2395],[117.6955,15.21]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/f/fa/Flag_of_the_People%27s_Republic_of_China.svg","name:en":"China","wikidata":"Q148","ISO3166-1:alpha2":"CN","ISO3166-1:alpha3":"CHN","ISO3166-1:numeric":"156"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-61.7725,-50.9875],[-61.7585,-51.0575],[-61.659,-51.1525],[-61.5995,-51.177],[-61.669,-51.8225],[-61.657,-51.8915],[-61.6065,-51.954],[-61.1995,-52.29],[-61.0895,-52.347],[-60.799,-52.4375],[-60.7315,-52.453],[-59.8035,-52.591],[-59.1715,-52.646],[-58.901,-52.65],[-58.7875,-52.6385],[-58.6465,-52.579],[-58.1785,-52.2365],[-57.528,-51.8765],[-57.3995,-51.7665],[-57.366,-51.6625],[-57.4025,-51.546],[-57.4105,-51.463],[-57.459,-51.4005],[-57.61,-51.278],[-57.7155,-51.2165],[-57.89,-51.165],[-58.905,-51.0375],[-59.0095,-51.035],[-59.482,-51.0715],[-59.848,-51.0135],[-60.996,-50.8355],[-61.2695,-50.802],[-61.4455,-50.7975],[-61.5865,-50.815],[-61.6855,-50.8595],[-61.7475,-50.919],[-61.7725,-50.9875]]],[[[-59.524,-52.905],[-59.5055,-52.981],[-59.452,-53.042],[-59.37,-53.087],[-59.2595,-53.1145],[-59.144,-53.1165],[-58.9735,-53.0705],[-58.8985,-53.015],[-58.8525,-52.938],[-58.8495,-52.866],[-58.8975,-52.7865],[-58.986,-52.7295],[-59.0895,-52.6995],[-59.242,-52.6945],[-59.3795,-52.729],[-59.4615,-52.78],[-59.5085,-52.842],[-59.524,-52.905]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/8/83/Flag_of_the_Falkland_Islands.svg","name:en":"Falkland Islands","wikidata":"Q9648","ISO3166-1:alpha2":"FK","ISO3166-1:alpha3":"FLK","ISO3166-1:numeric":"238"}}, -{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-62.7675,18.163],[-62.734,18.2035],[-62.7145,18.264],[-62.7165,18.3325],[-62.743,18.3965],[-62.79,18.448],[-62.8695,18.4875],[-62.9615,18.499],[-63.0595,18.471],[-63.242,18.487],[-63.2115,18.5805],[-63.2215,18.6555],[-63.2595,18.722],[-63.319,18.7695],[-63.3915,18.793],[-63.4675,18.7905],[-63.568,18.7395],[-63.6185,18.674],[-63.639,18.5765],[-63.6245,18.508],[-63.5605,18.425],[-63.455,18.3825],[-63.486,18.286],[-63.474,18.211],[-63.433,18.144],[-63.379,18.1025],[-63.3605,18.0615],[-63.2095,18.098],[-62.9485,18.181],[-62.8765,18.1905],[-62.7675,18.163]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/b/b4/Flag_of_Anguilla.svg","name:en":"Anguilla","wikidata":"Q25228","ISO3166-1:alpha2":"AI","ISO3166-1:alpha3":"AIA","ISO3166-1:numeric":"660"}}, -{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[34.295,31.704],[34.1655,31.5615],[34.069,31.478],[34.219,31.3235],[34.2675,31.22],[34.3665,31.2905],[34.3645,31.3615],[34.3805,31.3895],[34.478,31.476],[34.547,31.513],[34.565,31.5425],[34.488,31.597],[34.295,31.704]]],[[[35.555,32.389],[35.419,32.417],[35.403,32.501],[35.358,32.5185],[35.2955,32.5095],[35.2245,32.552],[35.0655,32.449],[35.0515,32.367],[35.0165,32.3385],[35.0305,32.266],[34.9595,32.1755],[34.9905,32.142],[34.985,32.0945],[35.005,32.022],[34.9905,31.961],[35.039,31.907],[35.0335,31.859],[35.0835,31.8525],[35.1085,31.8235],[35.178,31.808],[35.264,31.8265],[35.239,31.7095],[35.127,31.7295],[35.085,31.6905],[35.0065,31.6525],[34.9525,31.5945],[34.944,31.51],[34.894,31.415],[34.882,31.393],[34.92,31.3435],[35.006,31.3575],[35.1345,31.355],[35.2305,31.3745],[35.3945,31.4915],[35.475,31.4965],[35.4915,31.621],[35.5275,31.7215],[35.5555,31.7515],[35.549,31.8655],[35.5245,31.907],[35.5445,31.97],[35.519,32.0355],[35.533,32.108],[35.5705,32.1915],[35.5655,32.258],[35.559,32.3365],[35.5565,32.3685],[35.555,32.386],[35.555,32.389]]]]},"properties":{"flag":"http://upload.wikimedia.org/wikipedia/commons/0/00/Flag_of_Palestine.svg","name:en":"Palestinian Territories","wikidata":"Q219060","ISO3166-1:alpha2":"PS","ISO3166-1:alpha3":"PSE","ISO3166-1:numeric":"275"}}]} \ No newline at end of file +{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"id":"IN-AS"},"geometry":{"type":"Polygon","coordinates":[[[89.71298,26.26755],[89.73581,26.15818],[89.77865,26.08387],[89.77728,26.04254],[89.86592,25.93115],[89.80585,25.82489],[89.84388,25.70042],[89.86129,25.61714],[89.81208,25.37244],[89.89562,25.5635],[90.01922,25.60314],[89.89906,25.73867],[90.10642,25.96113],[90.48065,26.0031],[90.61729,25.87714],[90.95443,25.95001],[91.24969,25.71887],[91.84432,26.10982],[91.94732,25.99755],[92.30438,26.06788],[92.13958,25.69846],[92.6477,25.57465],[92.79464,25.21612],[92.39147,25.01471],[92.49887,24.88796],[92.38626,24.86055],[92.25854,24.9191],[92.15796,24.54435],[92.22953,24.50245],[92.27931,24.39213],[92.21786,24.25259],[92.29819,24.25406],[92.42317,24.25635],[92.44239,24.14299],[92.52685,24.16617],[92.76168,24.52088],[92.8379,24.39588],[93.02329,24.40026],[93.10569,24.81883],[93.17985,24.80169],[93.47236,25.3074],[93.31924,25.53872],[93.51905,25.67433],[93.69964,25.92717],[93.78341,25.97594],[93.79371,25.81472],[93.96537,25.90185],[93.99902,26.16468],[94.27093,26.5658],[94.30801,26.45459],[94.39659,26.52772],[94.40551,26.61922],[94.78248,26.79771],[94.88548,26.92451],[95.19515,27.02915],[95.46295,27.12209],[95.49041,27.2473],[95.89279,27.27294],[95.99647,27.37481],[95.86189,27.43333],[95.88523,27.52836],[95.76335,27.73519],[95.97518,27.9665],[95.63117,27.96105],[95.51994,27.88157],[95.316,27.87125],[94.86968,27.74036],[94.46937,27.5728],[94.216,27.62331],[94.26269,27.52471],[94.15489,27.4839],[93.80882,27.15142],[93.83354,27.07746],[93.68385,26.97838],[93.50257,26.93859],[93.38035,26.96308],[93.03291,26.919],[92.90313,27.00591],[92.64289,27.03894],[92.6441,26.98495],[92.11863,26.893],[92.05523,26.8692],[91.83181,26.87318],[91.50067,26.79223],[90.67715,26.77215],[90.48504,26.8594],[90.39271,26.90704],[90.30402,26.85098],[90.04535,26.72422],[89.86124,26.73307],[89.86885,26.46258],[89.84653,26.40078],[89.71298,26.26755]]]}},{"type":"Feature","properties":{"id":"IN-LD"},"geometry":{"type":"Polygon","coordinates":[[[71.96044,11.88348],[72.15131,7.6285],[74.03744,7.70688],[74.10827,11.89423],[71.96044,11.88348]]]}},{"type":"Feature","properties":{"id":"CN-HE"},"geometry":{"type":"Polygon","coordinates":[[[113.47297,36.6976],[113.59554,36.4616],[113.72909,36.36103],[114.02435,36.33172],[114.61418,36.12345],[114.91287,36.13066],[114.91493,36.04354],[115.19714,36.22544],[115.31936,36.07518],[115.48072,36.15506],[115.27336,36.47927],[115.47866,36.75759],[115.62629,36.79828],[115.83366,37.0475],[115.96755,37.33304],[116.23466,37.35978],[116.21887,37.46177],[116.31912,37.58485],[116.42143,37.46995],[116.79153,37.84015],[117.40745,37.83636],[117.55439,38.06323],[117.69378,38.07296],[117.84484,38.26514],[118.33648,38.49229],[117.59559,38.61472],[117.42736,38.61204],[117.24128,38.56373],[117.21931,38.63189],[117.09297,38.58682],[117.02567,38.69837],[116.87187,38.68658],[116.85539,38.74658],[116.75514,38.74176],[116.70364,38.93751],[116.75926,39.04372],[116.85882,39.05598],[116.91581,39.13112],[116.85539,39.16467],[116.87942,39.34385],[116.81899,39.34491],[116.87049,39.43354],[116.77848,39.46005],[116.80732,39.61415],[116.52065,39.59193],[116.46366,39.52946],[116.43722,39.44494],[116.32942,39.4566],[116.23809,39.51649],[116.22058,39.5787],[115.91365,39.57552],[115.81031,39.50933],[115.74851,39.51251],[115.66268,39.60833],[115.51574,39.6041],[115.47042,39.74177],[115.41755,39.77925],[115.56518,39.81011],[115.42098,39.96449],[115.50338,40.07649],[115.76808,40.15736],[115.8467,40.14633],[115.95382,40.27481],[115.90541,40.35544],[115.857,40.36145],[115.76568,40.44668],[115.77632,40.46196],[115.73272,40.51145],[115.77907,40.56128],[115.81615,40.5575],[115.82267,40.5871],[116.11278,40.62646],[116.24565,40.79067],[116.46537,40.7652],[116.31465,40.93037],[116.47602,40.89586],[116.44306,40.98145],[116.6209,40.97989],[116.62605,41.06485],[116.98379,40.68896],[117.21794,40.69938],[117.30789,40.65199],[117.43972,40.68532],[117.51663,40.65069],[117.47749,40.63167],[117.44419,40.65042],[117.42977,40.61851],[117.41432,40.63701],[117.40882,40.56206],[117.3072,40.57719],[117.24781,40.54902],[117.25227,40.50857],[117.20626,40.50126],[117.25913,40.43701],[117.22824,40.41245],[117.21811,40.36616],[117.29587,40.27612],[117.32814,40.2879],[117.34291,40.23131],[117.5537,40.22607],[117.75558,40.05389],[117.78648,39.96659],[117.53517,39.98711],[117.50427,39.88971],[117.54753,39.7584],[117.58872,39.73993],[117.65945,39.63848],[117.71026,39.52787],[117.76245,39.59828],[117.92861,39.57499],[117.85274,39.38685],[117.79197,39.36668],[117.84656,39.35421],[117.84072,39.32712],[117.96363,39.31331],[118.06079,39.25166],[118.02577,39.21789],[118.35983,38.73266],[119.98931,39.77661],[119.73586,40.11431],[119.58137,40.36799],[119.55253,40.54876],[119.26277,40.53154],[119.12406,40.67647],[118.90296,40.75297],[118.88992,40.9576],[119.01695,40.97523],[118.92631,41.06382],[119.07325,41.08608],[119.24972,41.27264],[119.23562,41.3149],[119.1481,41.29638],[118.79585,41.3598],[118.38729,41.31082],[118.20396,41.62314],[118.11538,41.76567],[118.34335,41.85728],[118.26232,42.08446],[118.18267,42.02838],[117.98973,42.25952],[118.01376,42.39962],[117.7851,42.61678],[117.44453,42.59151],[117.39921,42.46449],[117.00782,42.45994],[116.88697,42.37985],[116.90757,42.18477],[116.78054,42.19902],[116.86981,42.00236],[116.70158,41.93037],[116.30058,41.99675],[116.10763,41.84399],[116.06574,41.77131],[115.91194,41.93804],[115.81787,41.93191],[115.31112,41.67291],[115.33927,41.59541],[114.8545,41.59593],[114.93484,41.85882],[114.84626,42.14456],[114.50637,42.11095],[114.43702,41.9457],[114.23309,41.69547],[114.21455,41.50703],[114.01336,41.52503],[113.85612,41.41338],[113.99208,41.19054],[113.83621,41.08452],[114.06829,40.85485],[114.05834,40.79795],[114.1246,40.74569],[114.28527,40.51327],[114.30519,40.36695],[114.54482,40.3366],[113.89114,40.0192],[114.41093,39.83121],[114.39376,39.60568],[114.5613,39.55276],[114.3402,39.07997],[113.94676,39.09489],[113.75518,38.94499],[113.84101,38.76854],[113.53683,38.50787],[113.55194,38.23871],[113.82797,38.16263],[114.02984,37.72972],[114.127,37.69387],[114.09851,37.58594],[113.74488,37.0738],[113.78917,36.88071],[113.47297,36.6976]]]}},{"type":"Feature","properties":{"id":"IN-AN"},"geometry":{"type":"Polygon","coordinates":[[[91.66991,10.83331],[93.82619,5.95573],[94.98735,6.60903],[94.6395,14.00732],[93.69443,13.6468],[92.61282,13.95915],[91.66991,10.83331]]]}},{"type":"Feature","properties":{"id":"CN-SD"},"geometry":{"type":"Polygon","coordinates":[[[114.82429,34.994],[115.13122,35.00187],[115.21465,34.94814],[115.19439,34.90817],[115.42545,34.8014],[115.45394,34.63659],[115.67916,34.55577],[116.09596,34.60665],[116.19792,34.51759],[116.37405,34.64676],[116.43859,34.89944],[116.96456,34.88367],[117.15957,34.52692],[117.78991,34.51447],[117.93411,34.68404],[118.14559,34.54389],[118.17718,34.36837],[118.42849,34.44655],[118.51226,34.69194],[118.76495,34.73822],[118.88442,35.04349],[119.30259,35.07833],[123.85601,37.49093],[123.90497,38.79949],[120.97044,38.45359],[117.84484,38.26514],[117.69378,38.07296],[117.55439,38.06323],[117.40745,37.83636],[116.79153,37.84015],[116.42143,37.46995],[116.31912,37.58485],[116.21887,37.46177],[116.23466,37.35978],[115.96755,37.33304],[115.83366,37.0475],[115.62629,36.79828],[115.47866,36.75759],[115.27336,36.47927],[115.48072,36.15506],[115.43197,36.01078],[115.35026,35.95021],[115.35644,35.77771],[115.49171,35.92297],[115.64414,35.91936],[116.0939,36.11069],[116.03073,35.963],[115.87005,35.91185],[115.67985,35.76211],[115.41206,35.64111],[115.32966,35.47744],[115.07903,35.40416],[114.91218,35.19962],[114.82429,34.994]]]}},{"type":"Feature","properties":{"id":"IN-CT"},"geometry":{"type":"Polygon","coordinates":[[[80.24825,18.95565],[80.36636,18.83091],[80.27503,18.72104],[80.34782,18.59158],[80.51055,18.62802],[81.05026,17.77615],[81.39221,17.81014],[81.40165,17.88727],[81.44988,17.89707],[81.52679,18.16248],[81.50722,18.19217],[81.54275,18.26864],[81.61468,18.31052],[81.706,18.39916],[81.69158,18.42196],[81.79664,18.477],[81.84402,18.5714],[81.90101,18.64266],[82.16468,18.79094],[82.17945,18.90401],[82.2512,18.91473],[82.06375,19.78221],[81.82617,19.92558],[81.93809,20.09527],[82.34939,19.83776],[82.45513,19.91461],[82.58628,19.86263],[82.58285,19.76444],[82.72258,19.85036],[82.70439,20.00141],[82.39437,20.05302],[82.34252,20.85688],[82.4517,20.82608],[82.67555,21.16008],[83.1792,21.11044],[83.27499,21.3722],[83.40236,21.33958],[83.3385,21.50226],[83.46828,21.72569],[83.48167,21.81242],[83.58844,21.84078],[83.54347,22.05159],[83.65436,22.22967],[84.00421,22.37261],[84.04129,22.46005],[83.99425,22.53602],[84.40177,22.89325],[84.3695,22.97704],[84.14909,22.97546],[84.02961,23.16592],[84.06978,23.33059],[83.97039,23.37424],[84.02652,23.63068],[83.78929,23.58853],[83.71564,23.68587],[83.69487,23.82209],[83.55342,23.8783],[83.4185,24.08596],[83.32134,24.10131],[83.15963,23.90467],[82.95226,23.87642],[82.80876,23.96492],[82.65666,23.90216],[82.49599,23.7844],[81.90685,23.85569],[81.79218,23.80764],[81.72214,23.83999],[81.66274,23.92821],[81.59614,23.88834],[81.68712,23.71809],[81.57091,23.58113],[81.62567,23.48875],[81.75338,23.56619],[81.94942,23.49347],[82.03886,23.38433],[82.19361,23.32066],[82.13687,23.16608],[82.16245,23.15077],[82.1149,23.10247],[81.93886,23.07823],[81.91543,22.94701],[81.75991,22.85086],[81.76849,22.65108],[81.39633,22.45291],[81.35684,22.52175],[81.0918,22.43102],[80.98571,22.03441],[80.90606,22.13112],[80.80203,21.74036],[80.65784,21.32967],[80.64308,21.25418],[80.42129,21.09923],[80.46935,20.92681],[80.54214,20.92681],[80.5799,20.6694],[80.60806,20.32273],[80.38284,20.23256],[80.54488,20.07528],[80.51467,19.92687],[80.8937,19.47306],[80.72341,19.27355],[80.55587,19.40313],[80.24825,18.95565]]]}},{"type":"Feature","properties":{"id":"CN-XZ"},"geometry":{"type":"Polygon","coordinates":[[[78.38897,32.53938],[78.4645,32.45367],[78.49609,32.2762],[78.68754,32.10256],[78.74404,32.00384],[78.78036,31.99478],[78.69933,31.78723],[78.84516,31.60631],[78.71032,31.50197],[78.77898,31.31209],[79.01931,31.42817],[79.14016,31.43403],[79.22805,31.34963],[79.59884,30.93943],[79.93255,30.88288],[80.20721,30.58541],[80.54504,30.44936],[80.83343,30.32023],[81.03953,30.20059],[81.12842,30.01395],[81.24362,30.0126],[81.29032,30.08806],[81.2623,30.14596],[81.33355,30.15303],[81.39928,30.21862],[81.41018,30.42153],[81.62033,30.44703],[81.99082,30.33423],[82.10135,30.35439],[82.10757,30.23745],[82.19475,30.16884],[82.16984,30.0692],[82.38622,30.02608],[82.5341,29.9735],[82.73024,29.81695],[83.07116,29.61957],[83.28131,29.56813],[83.44787,29.30513],[83.63156,29.16249],[83.82303,29.30513],[83.97559,29.33091],[84.18107,29.23451],[84.24801,29.02783],[84.2231,28.89571],[84.47528,28.74023],[84.62317,28.73887],[84.85511,28.58041],[85.06059,28.68562],[85.19135,28.62825],[85.18668,28.54076],[85.10729,28.34092],[85.38127,28.28336],[85.4233,28.32996],[85.59765,28.30529],[85.60854,28.25045],[85.69105,28.38475],[85.71907,28.38064],[85.74864,28.23126],[85.84672,28.18187],[85.90743,28.05144],[85.97813,27.99023],[85.94946,27.9401],[86.06309,27.90021],[86.12069,27.93047],[86.08333,28.02121],[86.088,28.09264],[86.18607,28.17364],[86.22966,27.9786],[86.42736,27.91122],[86.51609,27.96623],[86.56265,28.09569],[86.74181,28.10638],[86.75582,28.04182],[87.03757,27.94835],[87.11696,27.84104],[87.56996,27.84517],[87.72718,27.80938],[87.82681,27.95248],[88.13378,27.88015],[88.1278,27.95417],[88.25332,27.9478],[88.54858,28.06057],[88.63235,28.12356],[88.83559,28.01936],[88.88091,27.85192],[88.77517,27.45415],[88.82981,27.38814],[88.91901,27.32483],[88.93678,27.33777],[88.96947,27.30319],[89.00216,27.32532],[88.95355,27.4106],[88.97213,27.51671],[89.0582,27.60985],[89.12825,27.62502],[89.59525,28.16433],[89.79762,28.23979],[90.13387,28.19178],[90.58842,28.02838],[90.69894,28.07784],[91.20019,27.98715],[91.25779,28.07509],[91.46327,28.0064],[91.48973,27.93903],[91.5629,27.84823],[91.6469,27.76358],[91.84722,27.76325],[91.87057,27.7195],[92.27432,27.89077],[92.32101,27.79363],[92.42538,27.80092],[92.7275,27.98662],[92.73025,28.05814],[92.65472,28.07632],[92.67486,28.15018],[92.93075,28.25671],[93.14635,28.37035],[93.18069,28.50319],[93.44621,28.67189],[93.72797,28.68821],[94.35897,29.01965],[94.2752,29.11687],[94.69318,29.31739],[94.81353,29.17804],[95.0978,29.14446],[95.11291,29.09527],[95.2214,29.10727],[95.26122,29.07727],[95.3038,29.13847],[95.41091,29.13007],[95.50842,29.13487],[95.72086,29.20797],[95.75149,29.32063],[95.84899,29.31464],[96.05361,29.38167],[96.31316,29.18643],[96.18682,29.11087],[96.20467,29.02325],[96.3626,29.10607],[96.61391,28.72742],[96.40929,28.51526],[96.48895,28.42955],[96.6455,28.61657],[96.85561,28.4875],[96.88445,28.39452],[96.98882,28.32564],[97.1289,28.3619],[97.34547,28.21385],[97.41729,28.29783],[97.47085,28.2688],[97.50518,28.49716],[97.56835,28.55628],[97.70705,28.5056],[97.79632,28.33168],[97.90069,28.3776],[98.15337,28.12114],[98.16696,28.21002],[98.26789,28.24421],[98.20129,28.35666],[98.28987,28.39804],[98.36952,28.26084],[98.39355,28.10953],[98.60881,28.1725],[98.69361,28.21789],[98.75884,28.33218],[98.63697,28.49072],[98.5968,28.68622],[98.63113,28.69103],[98.68125,28.73319],[98.66254,28.79549],[98.65447,28.8585],[98.62495,28.9721],[98.78974,29.01054],[98.83197,28.80406],[98.97548,28.82933],[98.97376,28.87564],[98.91815,28.88796],[98.92501,28.98111],[99.0184,29.03425],[98.96003,29.18663],[99.11487,29.22679],[99.05479,29.30586],[99.06234,29.45051],[98.98956,29.66359],[99.01119,29.8189],[99.05651,29.93708],[99.0438,30.07979],[98.99642,30.15344],[98.90613,30.68457],[98.95523,30.74862],[98.78528,30.92431],[98.80725,30.98496],[98.75404,31.03293],[98.71009,31.11997],[98.60469,31.18725],[98.63559,31.33839],[98.69052,31.33751],[98.77567,31.24949],[98.88896,31.37767],[98.84433,31.42954],[98.71971,31.50626],[98.56246,31.67705],[98.409,31.83089],[98.43475,32.00458],[98.30497,32.12619],[98.21639,32.33936],[97.99255,32.46748],[97.72544,32.52886],[97.66399,32.47704],[97.37663,32.5306],[97.30865,32.07501],[97.22557,32.10962],[97.16583,32.03049],[97.00309,32.06628],[96.9564,31.99351],[96.725,32.02612],[96.8431,31.7083],[96.5657,31.7194],[96.37069,31.85539],[96.33636,31.95682],[96.24435,31.9341],[96.13929,31.82623],[96.21963,31.76145],[96.25053,31.55396],[96.20109,31.53816],[96.14822,31.69078],[95.7891,31.75327],[95.61881,31.77896],[95.51032,31.74685],[95.22571,32.3872],[94.91981,32.413],[94.61254,32.67001],[94.14733,32.43561],[93.72161,32.57343],[93.48953,32.4947],[93.02398,32.73646],[92.22335,32.72144],[92.20275,32.88766],[91.97891,32.86113],[91.51405,33.11339],[90.70175,33.13985],[90.51567,33.2651],[90.38177,33.2605],[90.24993,33.42857],[89.99656,33.55913],[89.93751,33.80083],[89.63882,34.04583],[89.87708,34.2277],[89.73358,34.65862],[89.81941,34.90395],[89.57908,34.90113],[89.45274,35.22991],[89.68482,35.42207],[89.80224,35.859],[89.42802,35.91602],[89.40811,36.01522],[89.691,36.0935],[88.94119,36.35716],[88.76438,36.29077],[88.53332,36.48755],[86.2619,36.19995],[86.09298,35.8679],[85.57662,35.64055],[85.26489,35.80333],[84.19784,35.35881],[83.1253,35.39688],[82.966,35.62716],[82.45238,35.7309],[82.01568,35.34201],[81.66961,35.24337],[80.45287,35.45172],[79.83283,34.48958],[79.05418,34.4154],[79.05364,34.32482],[78.99802,34.3027],[78.91769,34.15452],[78.66225,34.08858],[78.65657,34.03195],[78.73367,34.01121],[78.77349,33.73871],[78.67599,33.66445],[78.73636,33.56521],[79.15252,33.17156],[79.14016,33.02545],[79.46562,32.69668],[79.26768,32.53277],[79.13174,32.47766],[79.0979,32.38051],[78.99322,32.37948],[78.96713,32.33655],[78.7831,32.46873],[78.73916,32.69438],[78.38897,32.53938]]]}},{"type":"Feature","properties":{"id":"CN-HI"},"geometry":{"type":"Polygon","coordinates":[[[107.44022,18.66249],[110.2534,15.19951],[112.88221,15.61902],[111.04979,20.2622],[108.26073,20.07614],[107.44022,18.66249]]]}},{"type":"Feature","properties":{"id":"IN-DN"},"geometry":{"type":"Polygon","coordinates":[[[72.92346,20.26348],[72.9808,20.21323],[72.98492,20.11784],[73.19675,20.05625],[73.23383,20.14266],[73.171,20.19744],[73.06354,20.18487],[73.06766,20.21806],[73.18508,20.29407],[73.12156,20.36909],[72.94578,20.35331],[72.92346,20.26348]]]}},{"type":"Feature","properties":{"id":"CN-NX"},"geometry":{"type":"Polygon","coordinates":[[[104.29321,37.4367],[105.18997,36.95208],[105.51544,36.0996],[105.32592,35.99911],[105.48454,35.72756],[106.05857,35.48639],[106.36344,35.23889],[106.4994,35.35993],[106.44241,35.69466],[106.921,35.76824],[106.9313,36.12456],[106.49047,36.30848],[106.66076,37.19751],[107.30346,37.0979],[107.2705,37.47921],[107.66139,37.87999],[107.1627,38.16155],[106.47743,38.31903],[106.95396,38.94819],[106.78161,39.37518],[106.59484,39.36934],[106.13479,39.15615],[105.85121,38.62116],[105.8258,38.34219],[105.80039,37.94149],[105.76606,37.79513],[105.10619,37.63707],[104.99084,37.5424],[104.29321,37.4367]]]}},{"type":"Feature","properties":{"id":"IN-MP"},"geometry":{"type":"Polygon","coordinates":[[[74.05677,22.48115],[74.27787,22.38373],[74.08149,22.35896],[74.07909,22.24652],[74.17282,22.06846],[74.14947,21.95323],[74.43717,22.03282],[74.52609,21.90769],[74.51408,21.72314],[74.64317,21.65583],[74.91783,21.63062],[75.07575,21.55209],[75.11455,21.45306],[75.22304,21.40928],[75.96324,21.39618],[76.15653,21.2625],[76.17301,21.08386],[76.38656,21.07809],[76.65847,21.28201],[76.80198,21.59519],[76.90429,21.60349],[77.07733,21.72474],[77.23422,21.7158],[77.49893,21.76332],[77.56484,21.38019],[78.00842,21.41887],[78.37989,21.62296],[78.43654,21.50099],[78.9347,21.49268],[78.91136,21.59295],[79.14585,21.62583],[79.33021,21.70719],[79.4914,21.67306],[79.53861,21.54634],[79.75662,21.60062],[79.91489,21.52207],[79.95025,21.5572],[80.06732,21.55528],[80.13427,21.61115],[80.20946,21.6354],[80.28499,21.59742],[80.38696,21.49619],[80.40824,21.3738],[80.56377,21.36037],[80.65784,21.32967],[80.80203,21.74036],[80.90606,22.13112],[80.98571,22.03441],[81.0918,22.43102],[81.35684,22.52175],[81.39633,22.45291],[81.76849,22.65108],[81.75991,22.85086],[81.91543,22.94701],[81.93886,23.07823],[82.1149,23.10247],[82.16245,23.15077],[82.13687,23.16608],[82.19361,23.32066],[82.03886,23.38433],[81.94942,23.49347],[81.75338,23.56619],[81.62567,23.48875],[81.57091,23.58113],[81.68712,23.71809],[81.59614,23.88834],[81.66274,23.92821],[81.72214,23.83999],[81.79218,23.80764],[81.90685,23.85569],[82.49599,23.7844],[82.65666,23.90216],[82.80876,23.96492],[82.79708,24.00319],[82.70267,24.09693],[82.66216,24.12513],[82.6752,24.16695],[82.71915,24.13876],[82.77854,24.29312],[82.76618,24.3754],[82.71606,24.37305],[82.71846,24.52713],[82.80361,24.55274],[82.69374,24.69755],[82.55195,24.65575],[82.41634,24.70504],[82.42561,24.59458],[82.30854,24.60831],[82.20691,24.78392],[81.95869,24.83301],[81.90101,24.88768],[81.90376,24.99943],[81.79389,25.00783],[81.73587,25.05574],[81.69605,25.03863],[81.56112,25.19748],[81.4904,25.07938],[81.27822,25.16703],[81.22741,24.95431],[80.80375,24.94154],[80.84426,24.99788],[80.88958,25.19375],[80.71723,25.14342],[80.77972,25.05823],[80.61561,25.10425],[80.25649,25.02401],[80.41648,25.1633],[80.29014,25.42281],[79.86854,25.23786],[79.86442,25.1086],[79.39269,25.11731],[79.47303,25.27512],[79.2794,25.35395],[79.31373,25.14404],[79.04182,25.1459],[78.92681,25.56753],[78.73146,25.47086],[78.81214,25.43056],[78.75789,25.33968],[78.66588,25.38807],[78.71051,25.45102],[78.56975,25.39676],[78.57868,25.34961],[78.52306,25.36419],[78.5464,25.3133],[78.42109,25.28164],[78.55464,25.26984],[78.63395,25.0887],[78.65558,24.91197],[78.77471,24.86027],[78.75686,24.60519],[78.87634,24.64483],[78.98483,24.44527],[78.9759,24.35397],[78.80767,24.15677],[78.51516,24.39025],[78.38882,24.26511],[78.2666,24.45277],[78.28033,24.5671],[78.16635,24.87647],[78.30642,24.97454],[78.34178,25.08155],[78.44306,25.12787],[78.30642,25.3707],[78.44066,25.56133],[78.52752,25.57031],[78.82003,25.6375],[78.74914,25.73589],[78.88458,25.90864],[79.12902,26.32665],[79.01779,26.63518],[78.74999,26.77994],[78.21029,26.82407],[76.91493,26.09533],[76.79408,25.94353],[76.59187,25.87745],[76.48544,25.71795],[76.52046,25.5319],[76.59942,25.39304],[76.68079,25.34309],[76.83734,25.32944],[76.93691,25.28071],[77.0763,25.33813],[77.19646,25.30647],[77.27508,25.42746],[77.35645,25.42932],[77.35507,25.2776],[77.41069,25.22109],[77.39559,25.11979],[77.31044,25.07938],[77.27439,25.11482],[77.00248,25.07316],[76.85554,25.03646],[76.94274,24.86401],[76.78481,24.83098],[76.85211,24.74745],[76.95304,24.75805],[77.06737,24.64639],[77.05879,24.52838],[76.94789,24.4509],[76.91802,24.54181],[76.82224,24.544],[76.83494,24.3718],[76.94343,24.20751],[76.91768,24.13735],[76.35875,24.25103],[76.21215,24.21283],[76.18846,24.33364],[76.0889,24.08564],[75.96324,24.02357],[75.98384,23.93574],[75.87879,23.88489],[75.73596,23.89651],[75.6879,23.7602],[75.45993,23.9144],[75.53203,24.0659],[75.70953,23.96617],[75.84377,24.10257],[75.7418,24.13766],[75.82076,24.24727],[75.73081,24.38118],[75.78987,24.47183],[75.88737,24.42401],[75.92101,24.51838],[75.78712,24.7699],[75.62919,24.68632],[75.58593,24.72562],[75.46989,24.68944],[75.18527,24.75057],[75.42629,24.8855],[75.30715,24.90138],[75.35659,25.03397],[75.17291,25.05356],[75.11352,24.88986],[75.03078,24.84563],[74.83543,24.98294],[74.84607,24.81135],[75.03318,24.75431],[74.90066,24.65107],[74.80522,24.79483],[74.80659,24.67322],[74.74616,24.539],[74.86907,24.48996],[74.90375,24.46058],[74.81002,24.41714],[74.75269,24.27576],[74.87182,24.27607],[74.91371,24.24226],[74.88143,24.21816],[74.98958,24.03235],[74.90924,23.86166],[74.93877,23.6376],[74.78702,23.54321],[74.61124,23.45694],[74.53605,23.30095],[74.69707,23.27572],[74.75063,23.22841],[74.53502,23.09868],[74.39769,23.11004],[74.3201,23.0573],[74.47769,22.86004],[74.36714,22.62858],[74.26929,22.64633],[74.05677,22.48115]]]}},{"type":"Feature","properties":{"id":"CN-JX"},"geometry":{"type":"Polygon","coordinates":[[[113.5976,27.42114],[113.62232,27.41017],[113.62232,27.34859],[113.875,27.38548],[113.77715,27.11781],[113.91517,26.94716],[113.83895,26.79342],[113.86402,26.65513],[113.91208,26.61277],[114.104,26.57624],[114.07482,26.40847],[113.94058,26.18193],[114.23789,26.20042],[114.02606,26.021],[114.00787,25.89258],[113.91448,25.69908],[113.98315,25.40916],[114.00924,25.28319],[114.19464,25.29685],[114.5565,25.42281],[114.74739,25.13036],[114.57092,25.08746],[114.17541,24.6545],[114.35462,24.58865],[114.4178,24.48464],[114.72576,24.6055],[114.8442,24.58178],[114.85897,24.55805],[114.9266,24.67478],[114.93415,24.6467],[115.06599,24.70753],[115.10822,24.66792],[115.37738,24.76803],[115.46699,24.76584],[115.56381,24.62954],[115.65444,24.61799],[115.69358,24.54056],[115.84327,24.5671],[115.78559,24.63703],[115.80379,24.69131],[115.76499,24.71221],[115.76362,24.79109],[115.82405,24.91539],[115.86696,24.86743],[115.89889,24.87833],[115.88859,24.94123],[115.8625,25.22544],[116.00601,25.32789],[116.14265,25.87899],[116.36444,25.971],[116.50039,26.15728],[116.38572,26.23368],[116.64321,26.49024],[116.51412,26.70145],[116.60476,26.92513],[117.05451,27.1062],[117.16918,27.2943],[117.01469,27.66163],[117.31269,27.76801],[117.28523,27.87671],[117.56332,27.96408],[117.67249,27.82268],[117.7518,27.83027],[117.85274,27.94891],[118.08792,27.99167],[118.16413,28.05743],[118.35296,28.09409],[118.36875,28.19369],[118.31245,28.22878],[118.42121,28.29239],[118.48308,28.32856],[118.43364,28.41193],[118.47381,28.47925],[118.40343,28.57457],[118.4254,28.68486],[118.3509,28.8164],[118.25134,28.92523],[118.0323,29.10057],[118.07281,29.2852],[118.20052,29.38178],[118.1195,29.42853],[118.12705,29.53523],[117.69584,29.55852],[117.45483,29.69222],[117.24952,29.89899],[117.06893,29.82634],[117.09091,29.69759],[116.70913,29.58301],[116.64939,29.70117],[116.91993,29.94541],[116.74278,30.06077],[116.57936,30.04769],[116.52992,29.89899],[116.12823,29.82754],[115.93803,29.71906],[115.65994,29.85851],[115.50064,29.8323],[115.40107,29.67492],[115.26786,29.63674],[115.11199,29.68029],[115.16693,29.50176],[114.95063,29.55852],[114.88128,29.38397],[114.25231,29.31993],[114.23034,29.2193],[114.0525,29.20761],[113.9447,29.05196],[114.01817,28.89758],[114.14932,28.77187],[114.07653,28.55919],[114.25094,28.38233],[113.99963,28.15253],[114.03945,28.06289],[113.74351,27.9477],[113.72085,27.8755],[113.75656,27.81782],[113.59863,27.63365],[113.5976,27.42114]]]}},{"type":"Feature","properties":{"id":"IN-SK"},"geometry":{"type":"Polygon","coordinates":[[[88.01646,27.21612],[88.04932,27.21631],[88.08331,27.16501],[88.08563,27.14072],[88.22656,27.11842],[88.30226,27.12682],[88.33505,27.10176],[88.45161,27.07655],[88.52045,27.17753],[88.62842,27.1731],[88.74219,27.144],[88.91901,27.32483],[88.82981,27.38814],[88.77517,27.45415],[88.88091,27.85192],[88.83559,28.01936],[88.63235,28.12356],[88.54858,28.06057],[88.25332,27.9478],[88.1278,27.95417],[88.13378,27.88015],[88.1973,27.85067],[88.19107,27.79285],[88.04008,27.49223],[88.07277,27.43007],[88.01646,27.21612]]]}},{"type":"Feature","properties":{"id":"IN-CH"},"geometry":{"type":"Polygon","coordinates":[[[76.69014,30.74699],[76.71306,30.7419],[76.71967,30.73202],[76.73014,30.72479],[76.73894,30.70136],[76.7513,30.68512],[76.76095,30.68619],[76.78945,30.67054],[76.8025,30.67527],[76.81468,30.68693],[76.82825,30.76411],[76.79254,30.76647],[76.80413,30.779],[76.79632,30.78623],[76.78104,30.77392],[76.76911,30.77683],[76.777,30.78483],[76.75975,30.79928],[76.73864,30.7908],[76.72285,30.77067],[76.70808,30.77045],[76.69066,30.75968],[76.69014,30.74699]]]}},{"type":"Feature","properties":{"id":"IN-NL"},"geometry":{"type":"Polygon","coordinates":[[[93.31924,25.53872],[93.47236,25.3074],[93.61175,25.19748],[93.86993,25.55854],[94.33685,25.50588],[94.58953,25.69289],[94.55932,25.51084],[94.68032,25.47003],[94.80117,25.49359],[95.18556,26.07338],[95.11428,26.1019],[95.12801,26.38397],[95.05798,26.45408],[95.23513,26.68499],[95.19515,27.02915],[94.88548,26.92451],[94.78248,26.79771],[94.40551,26.61922],[94.39659,26.52772],[94.30801,26.45459],[94.27093,26.5658],[93.99902,26.16468],[93.96537,25.90185],[93.79371,25.81472],[93.78341,25.97594],[93.69964,25.92717],[93.51905,25.67433],[93.31924,25.53872]]]}},{"type":"Feature","properties":{"id":"TT"},"geometry":{"type":"Polygon","coordinates":[[[-62.08693,10.04435],[-60.89962,9.81445],[-60.07172,11.77667],[-61.62505,11.18974],[-62.08693,10.04435]]]}},{"type":"Feature","properties":{"id":"AI"},"geometry":{"type":"Polygon","coordinates":[[[-63.95092,18.07976],[-63.35989,18.06012],[-62.86666,18.19278],[-62.75637,18.13489],[-62.64209,18.3662],[-63.3414,18.89029],[-63.90607,18.93262],[-63.95092,18.07976]]]}},{"type":"Feature","properties":{"id":"SX"},"geometry":{"type":"Polygon","coordinates":[[[-63.33064,17.9615],[-63.29212,17.90532],[-63.07669,17.79659],[-62.93924,18.02904],[-63.02323,18.05757],[-63.04039,18.05619],[-63.0579,18.06614],[-63.07759,18.04943],[-63.09686,18.04608],[-63.11096,18.05368],[-63.13584,18.0541],[-63.33064,17.9615]]]}},{"type":"Feature","properties":{"id":"VC"},"geometry":{"type":"Polygon","coordinates":[[[-61.73897,12.61191],[-61.38256,12.52991],[-61.13395,12.51526],[-60.70539,13.41452],[-61.43129,13.68336],[-61.73897,12.61191]]]}},{"type":"Feature","properties":{"id":"CW"},"geometry":{"type":"Polygon","coordinates":[[[-69.5195,12.75292],[-69.4514,12.18025],[-68.99639,11.79035],[-68.33524,11.78151],[-68.90012,12.62309],[-69.5195,12.75292]]]}},{"type":"Feature","properties":{"id":"CN-HL"},"geometry":{"type":"Polygon","coordinates":[[[121.20391,52.57468],[121.87133,52.27152],[122.19817,52.50786],[122.7365,52.20255],[122.96447,51.31173],[124.27871,51.304],[125.31005,51.62824],[126.06674,50.96621],[125.78521,50.7408],[125.78933,50.53263],[125.51467,50.39626],[125.18989,49.93442],[125.25649,49.32691],[125.2153,49.23777],[125.10681,49.12377],[124.86099,49.17945],[124.67834,48.83037],[124.61036,48.74894],[124.53552,48.46928],[124.50118,48.12485],[124.26086,48.52933],[123.55361,48.03493],[122.84637,47.67648],[122.3973,47.34626],[122.69393,47.08041],[123.00842,46.72385],[123.46778,46.96291],[123.59275,46.69042],[123.00498,46.56877],[123.17046,46.2283],[123.90655,46.28812],[124.13452,45.61692],[124.55612,45.41002],[125.68771,45.50346],[126.00768,45.12296],[126.65313,45.23621],[127.08846,44.93369],[127.03765,44.59242],[127.53753,44.55525],[127.71331,44.03429],[128.43429,44.50825],[128.88198,43.50374],[129.94903,44.06193],[130.07537,43.8048],[130.38574,44.03824],[130.44616,43.63905],[131.29402,43.46695],[131.19492,43.53047],[131.21105,43.82383],[131.26176,43.94011],[131.23583,43.96085],[131.25484,44.03131],[131.30365,44.04262],[131.1108,44.70266],[130.95639,44.85154],[131.48415,44.99513],[131.68466,45.12374],[131.66852,45.2196],[131.76532,45.22609],[131.86903,45.33636],[131.99417,45.2567],[132.83978,45.05916],[132.96373,45.0212],[133.12293,45.1332],[133.09279,45.25693],[133.19419,45.51913],[133.41083,45.57723],[133.48457,45.86203],[133.60442,45.90053],[133.67569,45.9759],[133.72695,46.05576],[133.68047,46.14697],[133.88097,46.25066],[133.91496,46.4274],[133.84104,46.46681],[134.03538,46.75668],[134.20016,47.33458],[134.50898,47.4812],[134.7671,47.72051],[134.55508,47.98651],[134.67098,48.1564],[134.75328,48.36763],[134.49516,48.42884],[132.66989,47.96491],[132.57309,47.71741],[131.90448,47.68011],[131.2635,47.73325],[131.09871,47.6852],[130.95985,47.6957],[130.90915,47.90623],[130.65103,48.10052],[130.84462,48.30942],[130.52147,48.61745],[130.66946,48.88251],[130.43232,48.90844],[130.2355,48.86741],[129.85416,49.11067],[129.67598,49.29596],[129.50685,49.42398],[129.40398,49.44194],[129.35317,49.3481],[129.23232,49.40353],[129.11153,49.36813],[128.72896,49.58676],[127.83476,49.5748],[127.53516,49.84306],[127.49299,50.01251],[127.60515,50.23503],[127.37384,50.28393],[127.36009,50.43787],[127.28765,50.46585],[127.36335,50.58306],[127.28165,50.72075],[127.14586,50.91152],[126.93135,51.0841],[126.90369,51.3238],[126.68349,51.70607],[126.44606,51.98254],[126.558,52.13738],[125.6131,53.07229],[125.17522,53.20225],[124.46078,53.21881],[123.86158,53.49391],[123.26989,53.54843],[122.85966,53.47395],[122.35063,53.49565],[121.39213,53.31888],[121.82738,53.03956],[121.20391,52.57468]]]}},{"type":"Feature","properties":{"id":"HT"},"geometry":{"type":"Polygon","coordinates":[[[-74.76465,18.06252],[-72.29523,17.48026],[-71.75671,18.03456],[-71.73783,18.07177],[-71.74994,18.11115],[-71.75465,18.14405],[-71.78271,18.18302],[-71.69952,18.34101],[-71.90875,18.45821],[-71.88102,18.50125],[-72.00201,18.62312],[-71.95412,18.64939],[-71.82556,18.62551],[-71.71885,18.78423],[-71.72624,18.87802],[-71.77766,18.95007],[-71.88102,18.95007],[-71.74088,19.0437],[-71.71088,19.08353],[-71.69938,19.10916],[-71.65337,19.11759],[-71.62642,19.21212],[-71.73229,19.26686],[-71.77766,19.33823],[-71.69448,19.37866],[-71.6802,19.45008],[-71.71268,19.53374],[-71.71449,19.55364],[-71.7429,19.58445],[-71.75865,19.70231],[-71.77419,19.73128],[-72.17094,20.08703],[-72.94479,20.79216],[-73.62304,20.6935],[-73.98196,19.51903],[-74.7289,18.71009],[-74.76465,18.06252]]]}},{"type":"Feature","properties":{"id":"KY"},"geometry":{"type":"Polygon","coordinates":[[[-81.81969,19.51758],[-81.52417,18.7521],[-79.33932,19.50496],[-79.63484,20.26689],[-81.81969,19.51758]]]}},{"type":"Feature","properties":{"id":"CU"},"geometry":{"type":"Polygon","coordinates":[[[-85.9092,21.8218],[-85.29304,20.76169],[-80.28428,20.93037],[-78.19353,19.33462],[-73.98196,19.51903],[-73.62304,20.6935],[-80.16442,23.44484],[-82.02215,24.23074],[-85.9092,21.8218]]]}},{"type":"Feature","properties":{"id":"TC"},"geometry":{"type":"Polygon","coordinates":[[[-72.94479,20.79216],[-71.13087,20.98822],[-70.63262,21.53631],[-72.41726,22.40371],[-72.94479,20.79216]]]}},{"type":"Feature","properties":{"id":"MT"},"geometry":{"type":"Polygon","coordinates":[[[13.4634,35.88474],[14.74801,35.36688],[15.10171,36.26215],[14.02721,36.53141],[13.4634,35.88474]]]}},{"type":"Feature","properties":{"id":"GR"},"geometry":{"type":"Polygon","coordinates":[[[19.0384,40.35325],[19.20409,39.7532],[22.5213,33.45682],[29.73302,35.92555],[29.69611,36.10365],[29.61805,36.14179],[29.61002,36.1731],[29.48192,36.18377],[29.30783,36.01033],[28.23708,36.56812],[27.95037,36.46155],[27.89482,36.69898],[27.46117,36.53789],[27.24613,36.71622],[27.45627,36.9008],[27.20312,36.94571],[27.14757,37.32],[26.95583,37.64989],[26.99377,37.69034],[27.16428,37.72343],[27.05537,37.9131],[26.21136,38.17558],[26.24183,38.44695],[26.32173,38.48731],[26.21136,38.65436],[26.61814,38.81372],[26.70773,39.0312],[26.43357,39.43096],[25.94257,39.39358],[25.61285,40.17161],[26.04292,40.3958],[25.94795,40.72797],[26.03489,40.73051],[26.0754,40.72772],[26.08638,40.73214],[26.12495,40.74283],[26.12854,40.77339],[26.15685,40.80709],[26.21351,40.83298],[26.20856,40.86048],[26.26169,40.9168],[26.29441,40.89119],[26.28623,40.93005],[26.32259,40.94042],[26.35894,40.94292],[26.33297,40.98388],[26.3606,41.02027],[26.31928,41.07386],[26.32259,41.24929],[26.39861,41.25053],[26.5209,41.33993],[26.5837,41.32131],[26.62997,41.34613],[26.61767,41.42281],[26.59742,41.48058],[26.59196,41.60491],[26.5209,41.62592],[26.47958,41.67037],[26.35957,41.71149],[26.30255,41.70925],[26.2654,41.71544],[26.22888,41.74139],[26.21325,41.73223],[26.16841,41.74858],[26.06148,41.70345],[26.07083,41.64584],[26.15146,41.60828],[26.14328,41.55496],[26.17951,41.55409],[26.176,41.50072],[26.14796,41.47533],[26.20288,41.43943],[26.16548,41.42278],[26.12926,41.35878],[25.87919,41.30526],[25.8266,41.34563],[25.70507,41.29209],[25.66183,41.31316],[25.61042,41.30614],[25.55082,41.31667],[25.52394,41.2798],[25.48187,41.28506],[25.28322,41.23411],[25.11611,41.34212],[24.942,41.38685],[24.90928,41.40876],[24.86136,41.39298],[24.82514,41.4035],[24.8041,41.34913],[24.71529,41.41928],[24.61129,41.42278],[24.52599,41.56808],[24.30513,41.51297],[24.27124,41.57682],[24.18126,41.51735],[24.10063,41.54796],[24.06323,41.53222],[24.06908,41.46132],[23.96975,41.44118],[23.91483,41.47971],[23.89613,41.45257],[23.80148,41.43943],[23.76525,41.40175],[23.67644,41.41139],[23.63203,41.37632],[23.52453,41.40262],[23.40416,41.39999],[23.33639,41.36317],[23.31301,41.40525],[23.22771,41.37106],[23.21953,41.33773],[23.1833,41.31755],[22.93334,41.34104],[22.81199,41.3398],[22.76408,41.32225],[22.74538,41.16321],[22.71266,41.13945],[22.65306,41.18168],[22.62852,41.14385],[22.58295,41.11568],[22.5549,41.13065],[22.42285,41.11921],[22.26744,41.16409],[22.17629,41.15969],[22.1424,41.12449],[22.06527,41.15617],[21.90869,41.09191],[21.91102,41.04786],[21.7556,40.92525],[21.69601,40.9429],[21.57448,40.86076],[21.53007,40.90759],[21.41555,40.9173],[21.35595,40.87578],[21.25779,40.86165],[21.21105,40.8855],[21.15262,40.85546],[20.97887,40.85475],[20.98396,40.79109],[20.95752,40.76982],[20.98134,40.76046],[21.05833,40.66586],[21.03932,40.56299],[20.96908,40.51526],[20.94925,40.46625],[20.83688,40.47882],[20.7906,40.42726],[20.78234,40.35803],[20.71789,40.27739],[20.67162,40.09433],[20.62566,40.0897],[20.61081,40.07866],[20.55593,40.06524],[20.51297,40.08168],[20.48487,40.06271],[20.42373,40.06777],[20.37911,39.99058],[20.31135,39.99438],[20.41546,39.82832],[20.41475,39.81437],[20.38572,39.78516],[20.30804,39.81563],[20.29152,39.80421],[20.31961,39.72799],[20.27412,39.69884],[20.22707,39.67459],[20.22376,39.64532],[20.15988,39.652],[20.12956,39.65805],[20.05189,39.69112],[20.00957,39.69227],[19.98042,39.6504],[19.92466,39.69533],[19.97622,39.78684],[19.95905,39.82857],[19.0384,40.35325]]]}},{"type":"Feature","properties":{"id":"PN"},"geometry":{"type":"Polygon","coordinates":[[[-133.61511,-21.93325],[-133.59543,-28.4709],[-122.0366,-24.55017],[-133.61511,-21.93325]]]}},{"type":"Feature","properties":{"id":"TO"},"geometry":{"type":"Polygon","coordinates":[[[-180,-24.21376],[-173.10761,-24.19665],[-173.11048,-23.23027],[-173.13438,-14.94228],[-174.17905,-14.94502],[-176.76826,-14.95183],[-176.74538,-22.89767],[-180,-22.90585],[-180,-24.21376]]]}},{"type":"Feature","properties":{"id":"WS"},"geometry":{"type":"Polygon","coordinates":[[[-174.18596,-12.48057],[-174.17905,-14.94502],[-173.13438,-14.94228],[-171.14262,-14.93704],[-171.14953,-12.4725],[-174.18596,-12.48057]]]}},{"type":"Feature","properties":{"id":"IN-HR"},"geometry":{"type":"Polygon","coordinates":[[[74.46464,29.78106],[74.50035,29.74053],[74.61158,29.76199],[74.57244,29.56449],[74.62188,29.52985],[74.57382,29.45992],[74.59785,29.32472],[74.80453,29.39563],[75.05207,29.281],[75.08159,29.22889],[75.33462,29.28909],[75.41084,29.19982],[75.36294,29.14301],[75.44448,29.01609],[75.51315,29.01504],[75.51744,28.9733],[75.4656,28.92689],[75.56877,28.61225],[75.80223,28.40227],[75.94333,28.36663],[76.09336,28.1498],[76.01886,28.16948],[75.93097,28.0959],[76.04032,28.08561],[75.91964,27.93329],[75.99449,27.85789],[76.22177,27.81296],[76.17267,28.01849],[76.20614,28.02486],[76.17216,28.05546],[76.22451,28.04986],[76.24958,28.07031],[76.31635,28.01925],[76.34519,28.02804],[76.35618,28.07591],[76.30262,28.09999],[76.36184,28.12543],[76.27,28.17538],[76.49333,28.15404],[76.54792,27.97393],[76.66225,28.01774],[76.65607,28.09954],[76.79958,28.16796],[76.80747,28.21562],[76.86635,28.22681],[76.96317,28.14269],[76.88575,27.67075],[76.96884,27.65555],[77.03235,27.78623],[77.28126,27.80689],[77.52468,27.9204],[77.53635,27.99167],[77.47283,28.08409],[77.54699,28.17613],[77.48794,28.19792],[77.53532,28.23725],[77.46528,28.30831],[77.48828,28.35515],[77.4979,28.40891],[77.46811,28.3997],[77.47361,28.41918],[77.45704,28.43586],[77.43086,28.4259],[77.41584,28.44039],[77.42477,28.46122],[77.41447,28.47465],[77.38752,28.46506],[77.37447,28.47035],[77.34615,28.51651],[77.32563,28.49004],[77.31413,28.4837],[77.30855,28.48823],[77.30053,28.49007],[77.30053,28.49419],[77.28832,28.49673],[77.27527,28.49358],[77.26991,28.48791],[77.26238,28.48762],[77.24298,28.47917],[77.23483,28.46982],[77.23156,28.45609],[77.24101,28.45616],[77.24628,28.4496],[77.24169,28.42763],[77.22058,28.41344],[77.17818,28.40921],[77.17389,28.40446],[77.17208,28.40559],[77.16161,28.42922],[77.14651,28.43692],[77.14043,28.43848],[77.13076,28.43984],[77.11277,28.4731],[77.11973,28.4952],[77.09775,28.50493],[77.09575,28.50713],[77.09863,28.51146],[77.09265,28.51434],[77.08003,28.51815],[77.07505,28.51877],[77.07235,28.52025],[77.07153,28.51787],[77.06703,28.51199],[77.06428,28.51285],[77.06093,28.51225],[77.05746,28.51297],[77.05226,28.51512],[77.0463,28.5167],[77.04862,28.52107],[77.0434,28.52409],[77.04344,28.52513],[77.03209,28.53118],[77.02424,28.5328],[77.0139,28.54068],[77.00544,28.53947],[77.00098,28.53114],[77.01476,28.52398],[77.01703,28.52104],[77.00982,28.51466],[76.9969,28.51949],[76.99111,28.51365],[76.97845,28.5213],[76.95334,28.50509],[76.92034,28.50678],[76.90635,28.51395],[76.90206,28.50671],[76.89657,28.50686],[76.89116,28.50037],[76.88043,28.50543],[76.88738,28.51998],[76.87391,28.5282],[76.86249,28.54487],[76.84584,28.55037],[76.83915,28.58301],[76.86429,28.58602],[76.8903,28.63274],[76.90738,28.62393],[76.91725,28.63395],[76.93528,28.61865],[76.94601,28.63289],[76.92378,28.64999],[76.93751,28.66942],[76.95502,28.66988],[76.95776,28.68448],[76.96781,28.69917],[76.94832,28.71264],[76.96068,28.73161],[76.95811,28.7432],[76.94403,28.75419],[76.95579,28.76841],[76.94807,28.78097],[76.95433,28.79045],[76.94292,28.79955],[76.95116,28.81798],[76.96146,28.81474],[76.97064,28.82783],[76.98077,28.82113],[76.99398,28.84068],[77.04282,28.8355],[77.06171,28.86963],[77.07939,28.87135],[77.08316,28.88315],[77.09166,28.87098],[77.11063,28.86918],[77.12265,28.8573],[77.13406,28.86301],[77.14616,28.85572],[77.14282,28.83858],[77.15681,28.8367],[77.17457,28.85865],[77.21157,28.8573],[77.22358,28.89939],[77.14187,29.09577],[77.10411,29.50415],[77.12882,29.75305],[77.34443,30.06315],[77.58613,30.31006],[77.5542,30.40278],[77.22083,30.49246],[76.99802,30.80024],[76.92386,30.83415],[76.90017,30.89751],[76.7704,30.9087],[76.82825,30.76411],[76.81468,30.68693],[76.81949,30.66139],[76.83794,30.65755],[76.84902,30.66693],[76.85923,30.65762],[76.85846,30.6416],[76.87133,30.63997],[76.91193,30.60504],[76.89931,30.55206],[76.89571,30.53579],[76.92026,30.52618],[76.88798,30.44038],[76.91579,30.40145],[76.92987,30.39686],[76.88833,30.35569],[76.877,30.36265],[76.88438,30.38516],[76.84885,30.40604],[76.83151,30.43328],[76.80747,30.41196],[76.75769,30.43727],[76.69864,30.39257],[76.73375,30.36458],[76.56045,30.26381],[76.63873,30.20493],[76.6238,30.14497],[76.60903,30.07978],[76.50089,30.0783],[76.44664,30.10385],[76.43394,30.15195],[76.2276,30.12656],[76.18057,29.88947],[76.2355,29.88649],[76.24168,29.85761],[76.11602,29.80222],[76.08169,29.80877],[76.04049,29.74798],[75.9423,29.73069],[75.86162,29.75424],[75.83003,29.81354],[75.70232,29.81145],[75.69339,29.75573],[75.65288,29.77987],[75.60173,29.7456],[75.44448,29.80788],[75.39813,29.76378],[75.37891,29.71161],[75.33908,29.68984],[75.34046,29.66747],[75.27934,29.60779],[75.31162,29.58122],[75.22716,29.54598],[75.21858,29.61495],[75.15747,29.66747],[75.1966,29.69073],[75.22991,29.75603],[75.20072,29.832],[75.17806,29.83826],[75.14099,29.77063],[75.1015,29.81294],[75.09155,29.91774],[74.98821,29.86357],[74.87285,29.96296],[74.80144,29.99092],[74.69467,29.9695],[74.63115,29.90494],[74.52094,29.94303],[74.55116,29.85553],[74.46464,29.78106]]]}},{"type":"Feature","properties":{"id":"WF"},"geometry":{"type":"Polygon","coordinates":[[[-178.60852,-12.49232],[-178.60161,-14.95666],[-176.76826,-14.95183],[-174.17905,-14.94502],[-174.18596,-12.48057],[-178.60852,-12.49232]]]}},{"type":"Feature","properties":{"id":"IN-MN"},"geometry":{"type":"Polygon","coordinates":[[[92.98416,24.11354],[93.32351,24.04468],[93.34735,24.10151],[93.41415,24.07854],[93.46633,23.97067],[93.50616,23.94432],[93.62871,24.00922],[93.75952,24.0003],[93.80279,23.92549],[93.92089,23.95812],[94.14081,23.83333],[94.30215,24.23752],[94.32362,24.27692],[94.45279,24.56656],[94.50729,24.59281],[94.5526,24.70764],[94.60204,24.70889],[94.73937,25.00545],[94.74212,25.13606],[94.57458,25.20318],[94.68032,25.47003],[94.55932,25.51084],[94.58953,25.69289],[94.33685,25.50588],[93.86993,25.55854],[93.61175,25.19748],[93.47236,25.3074],[93.17985,24.80169],[93.10569,24.81883],[93.02329,24.40026],[92.98416,24.11354]]]}},{"type":"Feature","properties":{"id":"PF"},"geometry":{"type":"Polygon","coordinates":[[[-156.4957,-12.32002],[-156.46451,-23.21255],[-156.44843,-28.52556],[-133.59543,-28.4709],[-133.61511,-21.93325],[-133.65593,-7.46952],[-149.6249,-7.51261],[-149.61166,-12.30171],[-156.4957,-12.32002]]]}},{"type":"Feature","properties":{"id":"AS"},"geometry":{"type":"Polygon","coordinates":[[[-174.18596,-12.48057],[-171.14953,-12.4725],[-171.14262,-14.93704],[-167.73854,-14.92809],[-167.75195,-10.12005],[-174.17993,-10.13616],[-174.18596,-12.48057]]]}},{"type":"Feature","properties":{"id":"US-HI"},"geometry":{"type":"Polygon","coordinates":[[[-179.2458,29.18869],[-179.23433,18.15359],[-154.13058,18.17333],[-154.14205,29.20682],[-176.83456,29.19028],[-176.81808,27.68129],[-177.84531,27.68616],[-177.8563,29.18961],[-179.2458,29.18869]]]}},{"type":"Feature","properties":{"id":"CN-AH"},"geometry":{"type":"Polygon","coordinates":[[[114.88128,32.97295],[115.20675,32.84844],[115.1889,32.59946],[115.44982,32.53813],[115.56312,32.38634],[115.91812,32.57922],[115.86181,32.45705],[115.93872,32.06861],[115.87005,31.77195],[115.64002,31.76145],[115.4718,31.65045],[115.36165,31.40228],[115.57823,31.14465],[115.69667,31.20604],[115.77461,31.10527],[115.87692,31.1423],[116.07776,30.96966],[115.84156,30.8312],[115.85082,30.75865],[115.7698,30.6701],[115.90061,30.50992],[115.91949,30.30472],[116.08222,30.12434],[116.12823,29.82754],[116.52992,29.89899],[116.57936,30.04769],[116.74278,30.06077],[116.91993,29.94541],[116.64939,29.70117],[116.70913,29.58301],[117.09091,29.69759],[117.06893,29.82634],[117.24952,29.89899],[117.45483,29.69222],[117.69584,29.55852],[118.12705,29.53523],[118.1195,29.42853],[118.20052,29.38178],[118.33717,29.48503],[118.48548,29.52447],[118.75808,29.76258],[118.89816,30.02332],[118.85627,30.16709],[118.90914,30.20508],[118.89404,30.35391],[119.22912,30.28871],[119.38705,30.37761],[119.31152,30.53032],[119.23393,30.53062],[119.24663,30.61575],[119.38671,30.69018],[119.43134,30.64145],[119.57382,30.85743],[119.58412,30.97407],[119.63149,31.1329],[119.57656,31.11174],[119.50378,31.1611],[119.36782,31.19107],[119.35134,31.30143],[119.27238,31.25272],[119.17968,31.29908],[119.07257,31.23394],[118.78761,31.23394],[118.71482,31.29967],[118.86314,31.43745],[118.8652,31.62415],[118.73954,31.67851],[118.71894,31.62415],[118.64204,31.64812],[118.69354,31.7194],[118.47587,31.78246],[118.49716,31.84373],[118.35605,31.93788],[118.39004,32.02612],[118.50128,32.14393],[118.50299,32.19479],[118.64444,32.21657],[118.6956,32.36198],[118.68049,32.45647],[118.54659,32.58442],[118.89747,32.58905],[119.08218,32.45531],[119.22225,32.60756],[119.17762,32.83286],[118.85559,32.95855],[118.7059,32.71624],[118.37425,32.72144],[118.22456,32.9332],[118.20121,33.2203],[118.03024,33.12777],[117.96775,33.33052],[118.18405,33.74546],[117.71026,33.74718],[117.74322,33.89264],[117.50907,34.06176],[117.02567,34.15556],[116.95014,34.39841],[116.37405,34.64676],[116.19792,34.51759],[116.2429,34.37177],[116.57867,34.27537],[116.53266,34.09588],[116.64974,33.96585],[116.63806,33.8858],[116.22951,33.72148],[116.14986,33.71148],[116.03176,33.83563],[116.05545,33.85787],[115.96034,33.90376],[115.99845,33.97439],[115.76431,34.07598],[115.59059,34.02563],[115.54801,33.8821],[115.62286,33.57115],[115.34957,33.5185],[115.30769,33.20709],[115.13465,33.08348],[114.91287,33.14387],[114.88128,32.97295]]]}},{"type":"Feature","properties":{"id":"CV"},"geometry":{"type":"Polygon","coordinates":[[[-25.86,17.60587],[-25.82546,14.43014],[-22.19117,14.46693],[-22.22571,17.64208],[-25.86,17.60587]]]}},{"type":"Feature","properties":{"id":"SH"},"geometry":{"type":"Polygon","coordinates":[[[-14.91926,-6.63386],[-14.82771,-8.70814],[-13.48367,-36.6746],[-13.41694,-37.88844],[-13.29655,-40.02846],[-9.34669,-41.00353],[-4.97086,-15.55882],[-13.33271,-8.07391],[-14.91926,-6.63386]]]}},{"type":"Feature","properties":{"id":"CN-NM"},"geometry":{"type":"Polygon","coordinates":[[[97.1777,42.7964],[98.3139,40.56806],[99.76958,40.97575],[100.28045,40.67439],[99.70229,39.98659],[100.84075,39.18224],[101.7279,38.64154],[101.8872,39.06931],[102.45712,39.23757],[102.96798,39.10981],[103.9389,39.46482],[104.18884,39.12047],[103.54476,38.15615],[103.36074,38.08809],[103.39507,37.88352],[103.83865,37.65773],[104.29321,37.4367],[104.99084,37.5424],[105.10619,37.63707],[105.76606,37.79513],[105.80039,37.94149],[105.8258,38.34219],[105.85121,38.62116],[106.13479,39.15615],[106.59484,39.36934],[106.78161,39.37518],[106.95396,38.94819],[106.47743,38.31903],[107.1627,38.16155],[107.66139,37.87999],[107.96607,37.79269],[108.01174,37.66561],[108.78936,37.68436],[108.83193,38.0662],[108.93836,37.9182],[109.02831,38.01618],[109.18521,38.03592],[108.93356,38.17883],[108.9624,38.35673],[109.55428,38.80119],[109.70809,39.05011],[109.89795,39.14683],[110.20866,39.28488],[110.10944,39.42876],[110.23063,39.4566],[110.38135,39.30826],[110.43662,39.38261],[110.52005,39.3834],[110.59661,39.27744],[110.68759,39.26522],[110.88363,39.50854],[111.15074,39.58478],[111.04808,39.43022],[111.09289,39.35859],[111.11881,39.36403],[111.12945,39.4025],[111.21288,39.42638],[111.33235,39.42081],[111.36188,39.47489],[111.4199,39.50245],[111.43295,39.64006],[111.53217,39.65698],[111.67121,39.62525],[111.72615,39.59537],[111.91909,39.61468],[111.97059,39.78822],[112.10758,39.97527],[112.30293,40.25463],[112.45399,40.29995],[112.61947,40.23891],[112.73963,40.1626],[112.84572,40.20169],[112.88761,40.32822],[113.24809,40.41349],[113.31504,40.31304],[113.53443,40.3345],[113.6721,40.4425],[113.85234,40.44511],[113.93783,40.50544],[114.07001,40.54093],[114.0652,40.67634],[114.1246,40.74569],[114.05834,40.79795],[114.06829,40.85485],[113.83621,41.08452],[113.99208,41.19054],[113.85612,41.41338],[114.01336,41.52503],[114.21455,41.50703],[114.23309,41.69547],[114.43702,41.9457],[114.50637,42.11095],[114.84626,42.14456],[114.93484,41.85882],[114.8545,41.59593],[115.33927,41.59541],[115.31112,41.67291],[115.81787,41.93191],[115.91194,41.93804],[116.06574,41.77131],[116.10763,41.84399],[116.30058,41.99675],[116.70158,41.93037],[116.86981,42.00236],[116.78054,42.19902],[116.90757,42.18477],[116.88697,42.37985],[117.00782,42.45994],[117.39921,42.46449],[117.44453,42.59151],[117.7851,42.61678],[118.01376,42.39962],[117.98973,42.25952],[118.18267,42.02838],[118.26232,42.08446],[118.34335,41.85728],[118.11538,41.76567],[118.20396,41.62314],[118.38729,41.31082],[118.79585,41.3598],[119.1481,41.29638],[119.23562,41.3149],[119.29435,41.32732],[119.36576,41.43191],[119.39392,41.58566],[119.32456,41.62519],[119.28268,41.78155],[119.3795,42.08803],[119.29916,42.12522],[119.23736,42.19596],[119.27032,42.25901],[119.50584,42.39709],[119.54498,42.29356],[119.83474,42.21428],[120.03662,41.81277],[120.02975,41.71341],[120.09498,41.68932],[120.18424,41.84245],[120.40878,41.98297],[120.5722,42.16747],[121.03774,42.27019],[121.28494,42.43359],[121.56646,42.51968],[121.66534,42.43967],[121.86309,42.53588],[121.92077,42.67032],[122.41241,42.8659],[122.80517,42.73087],[123.24737,42.98255],[123.55224,42.99862],[123.68682,43.3756],[123.32153,43.48979],[123.53301,43.64203],[123.3229,44.06045],[123.37715,44.15215],[123.1327,44.34938],[123.143,44.52294],[122.33001,44.22256],[122.11715,44.57237],[122.08213,44.91327],[122.1453,45.2971],[122.16522,45.41484],[122.01965,45.48324],[121.99836,45.6366],[121.95098,45.71097],[121.74568,45.68267],[121.64337,45.73877],[121.82121,45.8728],[121.75735,45.99505],[121.8727,46.04083],[122.2586,45.79434],[122.39868,45.9139],[122.52433,45.7771],[122.72003,45.70474],[122.79418,45.94064],[123.17046,46.2283],[123.00498,46.56877],[123.59275,46.69042],[123.46778,46.96291],[123.00842,46.72385],[122.69393,47.08041],[122.3973,47.34626],[122.84637,47.67648],[123.55361,48.03493],[124.26086,48.52933],[124.50118,48.12485],[124.53552,48.46928],[124.61036,48.74894],[124.67834,48.83037],[124.86099,49.17945],[125.10681,49.12377],[125.2153,49.23777],[125.25649,49.32691],[125.18989,49.93442],[125.51467,50.39626],[125.78933,50.53263],[125.78521,50.7408],[126.06674,50.96621],[125.31005,51.62824],[124.27871,51.304],[122.96447,51.31173],[122.7365,52.20255],[122.19817,52.50786],[121.87133,52.27152],[121.20391,52.57468],[121.82738,53.03956],[121.39213,53.31888],[120.85633,53.28499],[120.0451,52.7359],[120.04049,52.58773],[120.46454,52.63811],[120.71673,52.54099],[120.61346,52.32447],[120.77337,52.20805],[120.65907,51.93544],[120.10963,51.671],[119.13553,50.37412],[119.38598,50.35162],[119.27996,50.13348],[119.11003,50.00276],[118.61623,49.93809],[117.82343,49.52696],[117.48208,49.62324],[117.27597,49.62544],[117.07142,49.68482],[116.71193,49.83813],[116.03781,48.87014],[116.06565,48.81716],[115.78876,48.51781],[115.811,48.25699],[115.52082,48.15367],[115.57128,47.91988],[115.94296,47.67741],[116.08431,47.80693],[116.2527,47.87766],[116.4465,47.83662],[116.67405,47.89039],[116.87527,47.88836],[117.08918,47.82242],[117.37875,47.63627],[117.50181,47.77216],[117.80196,48.01661],[118.03676,48.00982],[118.11009,48.04],[118.22677,48.03853],[118.29654,48.00246],[118.55766,47.99277],[118.7564,47.76947],[119.12343,47.66458],[119.13995,47.53997],[119.35892,47.48104],[119.31964,47.42617],[119.54918,47.29505],[119.56019,47.24874],[119.62403,47.24575],[119.71209,47.19192],[119.85518,46.92196],[119.91242,46.90091],[119.89261,46.66423],[119.80455,46.67631],[119.77373,46.62947],[119.68127,46.59015],[119.65265,46.62342],[119.42827,46.63783],[119.37306,46.61132],[119.30261,46.6083],[119.24978,46.64761],[119.10448,46.65516],[119.00541,46.74273],[118.92616,46.72765],[118.89974,46.77139],[118.8337,46.77742],[118.78747,46.68689],[118.30534,46.73519],[117.69554,46.50991],[117.60748,46.59771],[117.41782,46.57862],[117.36609,46.36335],[117.07252,46.35818],[116.83166,46.38637],[116.75551,46.33083],[116.58612,46.30211],[116.26678,45.96479],[116.24012,45.8778],[116.27366,45.78637],[116.16989,45.68603],[115.91898,45.6227],[115.69688,45.45761],[115.35757,45.39106],[114.94546,45.37377],[114.74612,45.43585],[114.54801,45.38337],[114.5166,45.27189],[114.08071,44.92847],[113.909,44.91444],[113.63821,44.74326],[112.74662,44.86297],[112.4164,45.06858],[111.98695,45.09074],[111.76275,44.98032],[111.40498,44.3461],[111.96289,43.81596],[111.93776,43.68709],[111.79758,43.6637],[111.59087,43.51207],[111.0149,43.3289],[110.4327,42.78293],[110.08401,42.6411],[109.89402,42.63111],[109.452,42.44842],[109.00679,42.45302],[108.84489,42.40246],[108.23156,42.45532],[107.57258,42.40898],[107.49681,42.46221],[107.29755,42.41395],[107.24774,42.36107],[106.76517,42.28741],[105.24708,41.7442],[105.01119,41.58382],[104.91272,41.64619],[104.51667,41.66113],[104.52258,41.8706],[103.92804,41.78246],[103.3685,41.89696],[102.72403,42.14675],[102.42826,42.15137],[102.07645,42.22519],[101.80515,42.50074],[101.28833,42.58524],[100.84979,42.67087],[100.33297,42.68231],[99.50671,42.56535],[97.1777,42.7964]]]}},{"type":"Feature","properties":{"id":"CP"},"geometry":{"type":"Polygon","coordinates":[[[-109.6462,9.84394],[-108.755,9.84085],[-108.75183,10.72712],[-109.64303,10.7302],[-109.6462,9.84394]]]}},{"type":"Feature","properties":{"id":"MX"},"geometry":{"type":"Polygon","coordinates":[[[-120.12904,18.41089],[-92.37213,14.39277],[-92.2261,14.53423],[-92.1454,14.6804],[-92.18161,14.84147],[-92.1423,14.88647],[-92.1454,14.98143],[-92.0621,15.07406],[-92.20983,15.26077],[-91.73182,16.07371],[-90.44567,16.07573],[-90.40499,16.40524],[-90.61212,16.49832],[-90.69064,16.70697],[-91.04436,16.92175],[-91.43809,17.25373],[-90.99199,17.25192],[-90.98678,17.81655],[-89.14985,17.81563],[-89.15105,17.95104],[-89.03839,18.0067],[-88.8716,17.89535],[-88.71505,18.0707],[-88.48242,18.49164],[-88.3268,18.49048],[-88.29909,18.47591],[-88.26593,18.47617],[-88.03238,18.41778],[-88.03165,18.16657],[-87.90671,18.15213],[-87.87604,18.18313],[-87.86657,18.19971],[-87.85693,18.18266],[-87.84815,18.18511],[-87.24084,17.80373],[-85.9092,21.8218],[-96.92418,25.97377],[-97.13927,25.96583],[-97.35946,25.92189],[-97.37332,25.83854],[-97.42511,25.83969],[-97.45669,25.86874],[-97.49828,25.89877],[-97.52025,25.88518],[-97.66511,26.01708],[-97.95155,26.0625],[-97.97017,26.05232],[-98.24603,26.07191],[-98.27075,26.09457],[-98.30491,26.10475],[-98.35126,26.15129],[-99.00546,26.3925],[-99.03053,26.41249],[-99.08477,26.39849],[-99.53573,27.30926],[-99.49744,27.43746],[-99.482,27.47128],[-99.48045,27.49016],[-99.50208,27.50021],[-99.52955,27.49747],[-99.51478,27.55836],[-99.55409,27.61314],[-100.50029,28.66117],[-100.51222,28.70679],[-100.5075,28.74066],[-100.52313,28.75598],[-100.59809,28.88197],[-100.63689,28.90812],[-100.67294,29.09744],[-100.79696,29.24688],[-100.87982,29.296],[-100.94056,29.33371],[-100.94579,29.34523],[-100.96725,29.3477],[-101.01128,29.36947],[-101.05686,29.44738],[-101.47277,29.7744],[-102.60596,29.8192],[-103.15787,28.93865],[-104.37752,29.54255],[-104.39363,29.55396],[-104.3969,29.57105],[-104.5171,29.64671],[-104.77674,30.4236],[-106.00363,31.39181],[-106.09025,31.40569],[-106.20346,31.46305],[-106.23711,31.51262],[-106.24612,31.54193],[-106.28084,31.56173],[-106.30305,31.62154],[-106.33419,31.66303],[-106.34864,31.69663],[-106.3718,31.71165],[-106.38003,31.73151],[-106.41773,31.75196],[-106.43419,31.75478],[-106.45244,31.76523],[-106.46726,31.75998],[-106.47298,31.75054],[-106.48815,31.74769],[-106.50111,31.75714],[-106.50962,31.76155],[-106.51251,31.76922],[-106.52266,31.77509],[-106.529,31.784],[-108.20899,31.78534],[-108.20979,31.33316],[-109.05235,31.3333],[-111.07523,31.33232],[-112.34553,31.7357],[-114.82011,32.49609],[-114.79524,32.55731],[-114.81141,32.55543],[-114.80584,32.62028],[-114.76736,32.64094],[-114.71871,32.71894],[-115.88053,32.63624],[-117.1243,32.53427],[-118.48109,32.5991],[-120.12904,18.41089]]]}},{"type":"Feature","properties":{"id":"IE"},"geometry":{"type":"Polygon","coordinates":[[[-13.3292,54.24486],[-10.17712,50.85267],[-5.79914,52.03902],[-5.37267,53.63269],[-5.83481,53.87749],[-6.26218,54.09785],[-6.29003,54.11278],[-6.32694,54.09337],[-6.36279,54.11248],[-6.36605,54.07234],[-6.47849,54.06947],[-6.62842,54.03503],[-6.66264,54.0666],[-6.6382,54.17071],[-6.70175,54.20218],[-6.74575,54.18788],[-6.81583,54.22791],[-6.85179,54.29176],[-6.87775,54.34682],[-7.02034,54.4212],[-7.19145,54.31296],[-7.14908,54.22732],[-7.25012,54.20063],[-7.26316,54.13863],[-7.29493,54.12013],[-7.29687,54.1354],[-7.28017,54.16714],[-7.29157,54.17191],[-7.34005,54.14698],[-7.30553,54.11869],[-7.32834,54.11475],[-7.44567,54.1539],[-7.4799,54.12239],[-7.55812,54.12239],[-7.69501,54.20731],[-7.81397,54.20159],[-7.8596,54.21779],[-7.87101,54.29299],[-8.04555,54.36292],[-8.179,54.46763],[-8.04538,54.48941],[-7.99812,54.54427],[-7.8596,54.53671],[-7.70315,54.62077],[-7.93293,54.66603],[-7.83352,54.73854],[-7.75041,54.7103],[-7.64449,54.75265],[-7.54671,54.74606],[-7.54508,54.79401],[-7.47626,54.83084],[-7.4473,54.87003],[-7.44404,54.9403],[-7.40004,54.94498],[-7.4033,55.00391],[-7.34464,55.04688],[-7.2471,55.06933],[-6.9734,55.19878],[-6.71944,55.27952],[-6.79943,55.54107],[-7.93366,55.84142],[-13.3292,54.24486]]]}},{"type":"Feature","properties":{"id":"ST"},"geometry":{"type":"Polygon","coordinates":[[[5.9107,-0.09539],[6.69416,-0.53945],[8.0168,1.79377],[7.23334,2.23756],[5.9107,-0.09539]]]}},{"type":"Feature","properties":{"id":"GD"},"geometry":{"type":"Polygon","coordinates":[[[-62.14806,11.87638],[-61.57265,11.65795],[-61.13395,12.51526],[-61.38256,12.52991],[-61.73897,12.61191],[-62.14806,11.87638]]]}},{"type":"Feature","properties":{"id":"AG"},"geometry":{"type":"Polygon","coordinates":[[[-62.62949,16.82364],[-62.52079,16.69392],[-62.14123,17.02632],[-61.83929,16.66647],[-61.44461,16.81958],[-61.45764,17.9187],[-62.12601,17.9235],[-62.27053,17.22145],[-62.62949,16.82364]]]}},{"type":"Feature","properties":{"id":"AF"},"geometry":{"type":"Polygon","coordinates":[[[60.50209,34.13992],[60.5838,33.80793],[60.5485,33.73422],[60.57762,33.59772],[60.69573,33.56054],[60.91133,33.55596],[60.88908,33.50219],[60.56485,33.12944],[60.86191,32.22565],[60.84541,31.49561],[61.70929,31.37391],[61.80569,31.16167],[61.80957,31.12576],[61.83257,31.0452],[61.8335,30.97669],[61.78268,30.92724],[61.80829,30.84224],[60.87231,29.86514],[62.47751,29.40782],[63.5876,29.50456],[64.12966,29.39157],[64.19796,29.50407],[64.62116,29.58903],[65.04005,29.53957],[66.24175,29.85181],[66.36042,29.9583],[66.23609,30.06321],[66.34869,30.404],[66.28413,30.57001],[66.39194,30.9408],[66.42645,30.95309],[66.58175,30.97532],[66.68166,31.07597],[66.72561,31.20526],[66.83273,31.26867],[67.04147,31.31561],[67.03323,31.24519],[67.29964,31.19586],[67.78854,31.33203],[67.7748,31.4188],[67.62374,31.40473],[67.58323,31.52772],[67.72056,31.52304],[67.86887,31.63536],[68.00071,31.6564],[68.1655,31.82691],[68.25614,31.80357],[68.27605,31.75863],[68.44222,31.76446],[68.57475,31.83158],[68.6956,31.75687],[68.79997,31.61665],[68.91078,31.59687],[68.95995,31.64822],[69.00939,31.62249],[69.11514,31.70782],[69.20577,31.85957],[69.3225,31.93186],[69.27032,32.14141],[69.27932,32.29119],[69.23599,32.45946],[69.2868,32.53938],[69.38155,32.56601],[69.44747,32.6678],[69.43649,32.7302],[69.38018,32.76601],[69.47082,32.85834],[69.5436,32.8768],[69.49854,32.88843],[69.49004,33.01509],[69.57656,33.09911],[69.71526,33.09911],[69.79766,33.13247],[69.85259,33.09451],[70.02563,33.14282],[70.07369,33.22557],[70.13686,33.21064],[70.32775,33.34496],[70.17062,33.53535],[70.20141,33.64387],[70.14785,33.6553],[70.14236,33.71701],[70.00503,33.73528],[69.85671,33.93719],[69.87307,33.9689],[69.90203,34.04194],[70.54336,33.9463],[70.88119,33.97933],[71.07345,34.06242],[71.06933,34.10564],[71.09307,34.11961],[71.09453,34.13524],[71.13078,34.16503],[71.12815,34.26619],[71.17662,34.36769],[71.02401,34.44835],[71.0089,34.54568],[71.11602,34.63047],[71.08718,34.69034],[71.28356,34.80882],[71.29472,34.87728],[71.50329,34.97328],[71.49917,35.00478],[71.55273,35.02615],[71.52938,35.09023],[71.67495,35.21262],[71.5541,35.28776],[71.54294,35.31037],[71.65435,35.4479],[71.49917,35.6267],[71.55273,35.71483],[71.37969,35.95865],[71.19505,36.04134],[71.60491,36.39429],[71.80267,36.49924],[72.18135,36.71838],[72.6323,36.84601],[73.82685,36.91421],[74.04856,36.82648],[74.43389,37.00977],[74.53739,36.96224],[74.56453,37.03023],[74.49981,37.24518],[74.80605,37.21565],[74.88887,37.23275],[74.8294,37.3435],[74.68383,37.3948],[74.56161,37.37734],[74.41055,37.3948],[74.23339,37.41116],[74.20308,37.34208],[73.8564,37.26158],[73.82552,37.22659],[73.64974,37.23643],[73.61129,37.27469],[73.76647,37.33913],[73.77197,37.4417],[73.29633,37.46495],[73.06884,37.31729],[72.79693,37.22222],[72.66381,37.02014],[72.54095,37.00007],[72.31676,36.98115],[71.83229,36.68084],[71.67083,36.67346],[71.57195,36.74943],[71.51502,36.89128],[71.48481,36.93218],[71.46923,36.99925],[71.45578,37.03094],[71.43097,37.05855],[71.44127,37.11856],[71.4494,37.18137],[71.4555,37.21418],[71.47386,37.2269],[71.48339,37.23937],[71.4824,37.24921],[71.48536,37.26017],[71.50674,37.31502],[71.49821,37.31975],[71.4862,37.33405],[71.47685,37.40281],[71.49612,37.4279],[71.5256,37.47971],[71.50616,37.50733],[71.49693,37.53527],[71.5065,37.60912],[71.51972,37.61945],[71.54186,37.69691],[71.55234,37.73209],[71.53053,37.76534],[71.54324,37.77104],[71.55752,37.78677],[71.59255,37.79956],[71.58843,37.92425],[71.51565,37.95349],[71.32871,37.88564],[71.296,37.93403],[71.2809,37.91995],[71.24969,37.93031],[71.27278,37.96496],[71.27622,37.99946],[71.28922,38.01272],[71.29878,38.04429],[71.36444,38.15358],[71.37803,38.25641],[71.33869,38.27335],[71.33114,38.30339],[71.21291,38.32797],[71.1451,38.40106],[71.10957,38.40671],[71.10592,38.42077],[71.09542,38.42517],[71.0556,38.40176],[71.03545,38.44779],[70.98693,38.48862],[70.92728,38.43021],[70.88719,38.46826],[70.84376,38.44688],[70.82538,38.45394],[70.81697,38.44507],[70.80521,38.44447],[70.79766,38.44944],[70.78702,38.45031],[70.78581,38.45502],[70.77132,38.45548],[70.75455,38.4252],[70.72485,38.4131],[70.69807,38.41861],[70.67438,38.40597],[70.6761,38.39144],[70.69189,38.37031],[70.64966,38.34999],[70.61526,38.34774],[70.60407,38.28046],[70.54673,38.24541],[70.4898,38.12546],[70.17206,37.93276],[70.1863,37.84296],[70.27694,37.81258],[70.28243,37.66706],[70.15015,37.52519],[69.95971,37.5659],[69.93362,37.61378],[69.84435,37.60616],[69.80041,37.5746],[69.51888,37.5844],[69.44954,37.4869],[69.36645,37.40462],[69.45022,37.23315],[69.39529,37.16752],[69.25152,37.09426],[69.03274,37.25174],[68.96407,37.32603],[68.88168,37.33368],[68.91189,37.26704],[68.80889,37.32494],[68.81438,37.23862],[68.6798,37.27906],[68.61851,37.19815],[68.41888,37.13906],[68.41201,37.10402],[68.29253,37.10621],[68.27605,37.00977],[68.18542,37.02074],[68.02194,36.91923],[67.87917,37.0591],[67.7803,37.08978],[67.78329,37.1834],[67.51868,37.26102],[67.2581,37.17216],[67.2224,37.24545],[67.13039,37.27168],[67.08232,37.35469],[66.95598,37.40162],[66.64699,37.32958],[66.55743,37.35409],[66.30993,37.32409],[65.72274,37.55438],[65.64137,37.45061],[65.64263,37.34388],[65.51778,37.23881],[64.97945,37.21913],[64.61141,36.6351],[64.62514,36.44311],[64.57295,36.34362],[64.43288,36.24401],[64.05385,36.10433],[63.98519,36.03773],[63.56496,35.95106],[63.53475,35.90881],[63.29579,35.85985],[63.12276,35.86208],[63.10318,35.81782],[63.23262,35.67487],[63.10079,35.63024],[63.12276,35.53196],[63.0898,35.43131],[62.90853,35.37086],[62.74098,35.25432],[62.62288,35.22067],[62.48006,35.28796],[62.29878,35.13312],[62.29191,35.25964],[62.15871,35.33278],[62.05709,35.43803],[61.97743,35.4604],[61.77693,35.41341],[61.58742,35.43803],[61.27371,35.61482],[61.18187,35.30249],[61.0991,35.27845],[61.12831,35.09938],[61.06926,34.82139],[61.00197,34.70631],[60.99922,34.63064],[60.72316,34.52857],[60.91321,34.30411],[60.66502,34.31539],[60.50209,34.13992]]]}},{"type":"Feature","properties":{"id":"AO"},"geometry":{"type":"Polygon","coordinates":[[[10.5065,-17.25284],[11.75063,-17.25013],[12.07076,-17.15165],[12.52111,-17.24495],[12.97145,-16.98567],[13.36212,-16.98048],[13.95896,-17.43141],[14.28743,-17.38814],[18.39229,-17.38927],[18.84226,-17.80375],[21.14283,-17.94318],[21.42741,-18.02787],[23.47474,-17.62877],[23.20038,-17.47563],[22.17217,-16.50269],[22.00323,-16.18028],[21.97988,-13.00148],[24.03339,-12.99091],[23.90937,-12.844],[24.06672,-12.29058],[23.98804,-12.13149],[24.02603,-11.15368],[24.00027,-10.89356],[23.86868,-11.02856],[23.45631,-10.946],[23.16602,-11.10577],[22.54205,-11.05784],[22.25951,-11.24911],[22.17954,-10.85884],[22.32604,-10.76291],[22.19039,-9.94628],[21.84856,-9.59871],[21.79824,-7.29628],[20.56263,-7.28566],[20.61689,-6.90876],[20.31846,-6.91953],[20.30218,-6.98955],[19.5469,-7.00195],[19.33698,-7.99743],[18.33635,-8.00126],[17.5828,-8.13784],[16.96282,-7.21787],[16.55507,-5.85631],[13.04371,-5.87078],[12.42245,-6.07585],[11.95767,-5.94705],[12.20376,-5.76338],[12.26557,-5.74031],[12.52318,-5.74353],[12.52301,-5.17481],[12.53599,-5.1618],[12.53586,-5.14658],[12.51589,-5.1332],[12.49815,-5.14058],[12.46297,-5.09408],[12.60251,-5.01715],[12.63465,-4.94632],[12.70868,-4.95505],[12.8733,-4.74346],[13.11195,-4.67745],[13.09648,-4.63739],[12.91489,-4.47907],[12.87096,-4.40315],[12.76844,-4.38709],[12.64835,-4.55937],[12.40964,-4.60609],[12.32324,-4.78415],[12.25587,-4.79437],[12.20901,-4.75642],[12.16068,-4.90089],[12.00924,-5.02627],[11.50888,-5.33417],[10.5065,-17.25284]]]}},{"type":"Feature","properties":{"id":"AL"},"geometry":{"type":"Polygon","coordinates":[[[19.0384,40.35325],[19.95905,39.82857],[19.97622,39.78684],[19.92466,39.69533],[19.98042,39.6504],[20.00957,39.69227],[20.05189,39.69112],[20.12956,39.65805],[20.15988,39.652],[20.22376,39.64532],[20.22707,39.67459],[20.27412,39.69884],[20.31961,39.72799],[20.29152,39.80421],[20.30804,39.81563],[20.38572,39.78516],[20.41475,39.81437],[20.41546,39.82832],[20.31135,39.99438],[20.37911,39.99058],[20.42373,40.06777],[20.48487,40.06271],[20.51297,40.08168],[20.55593,40.06524],[20.61081,40.07866],[20.62566,40.0897],[20.67162,40.09433],[20.71789,40.27739],[20.78234,40.35803],[20.7906,40.42726],[20.83688,40.47882],[20.94925,40.46625],[20.96908,40.51526],[21.03932,40.56299],[21.05833,40.66586],[20.98134,40.76046],[20.95752,40.76982],[20.98396,40.79109],[20.97887,40.85475],[20.97693,40.90103],[20.94305,40.92399],[20.83671,40.92752],[20.81567,40.89662],[20.73504,40.9081],[20.71634,40.91781],[20.65558,41.08009],[20.63454,41.0889],[20.59832,41.09066],[20.58546,41.11179],[20.59715,41.13644],[20.51068,41.2323],[20.49432,41.33679],[20.52119,41.34381],[20.55976,41.4087],[20.51301,41.442],[20.49039,41.49277],[20.45331,41.51436],[20.45809,41.5549],[20.52103,41.56473],[20.55508,41.58113],[20.51769,41.65975],[20.52937,41.69292],[20.51301,41.72433],[20.53405,41.78099],[20.57144,41.7897],[20.55976,41.87068],[20.59524,41.8818],[20.57946,41.91593],[20.63069,41.94913],[20.59434,42.03879],[20.55633,42.08173],[20.56955,42.12097],[20.48857,42.25444],[20.3819,42.3029],[20.34479,42.32656],[20.24399,42.32168],[20.21797,42.41237],[20.17127,42.50469],[20.07761,42.55582],[20.01834,42.54622],[20.00842,42.5109],[19.9324,42.51699],[19.82333,42.46581],[19.76549,42.50237],[19.74731,42.57422],[19.77375,42.58517],[19.73244,42.66299],[19.65972,42.62774],[19.4836,42.40831],[19.42352,42.36546],[19.42,42.33019],[19.28623,42.17745],[19.40687,42.10024],[19.37548,42.06835],[19.36867,42.02564],[19.37691,41.96977],[19.34601,41.95675],[19.33812,41.90669],[19.37451,41.8842],[19.37597,41.84849],[19.26406,41.74971],[19.0384,40.35325]]]}},{"type":"Feature","properties":{"id":"AD"},"geometry":{"type":"Polygon","coordinates":[[[1.41245,42.53539],[1.44759,42.54431],[1.46661,42.50949],[1.41648,42.48315],[1.43838,42.47848],[1.44529,42.43724],[1.5127,42.42959],[1.55073,42.43299],[1.55937,42.45808],[1.57953,42.44957],[1.58933,42.46275],[1.65674,42.47125],[1.66826,42.50779],[1.70571,42.48867],[1.72515,42.50338],[1.73683,42.55492],[1.7858,42.57698],[1.72588,42.59098],[1.73452,42.61515],[1.68267,42.62533],[1.6625,42.61982],[1.63485,42.62957],[1.60085,42.62703],[1.55418,42.65669],[1.50867,42.64483],[1.48043,42.65203],[1.46718,42.63296],[1.47986,42.61346],[1.44197,42.60217],[1.42512,42.58292],[1.44529,42.56722],[1.4234,42.55959],[1.41245,42.53539]]]}},{"type":"Feature","properties":{"id":"CN-SC"},"geometry":{"type":"Polygon","coordinates":[[[97.33989,32.9038],[97.37834,32.86978],[97.37766,32.80718],[97.4271,32.7122],[97.48168,32.65556],[97.5325,32.64241],[97.54417,32.62332],[97.66296,32.55781],[97.72544,32.52886],[97.99255,32.46748],[98.21639,32.33936],[98.30497,32.12619],[98.43475,32.00458],[98.409,31.83089],[98.56246,31.67705],[98.71971,31.50626],[98.84433,31.42954],[98.88896,31.37767],[98.77567,31.24949],[98.69052,31.33751],[98.63559,31.33839],[98.60469,31.18725],[98.71009,31.11997],[98.75404,31.03293],[98.80725,30.98496],[98.78528,30.92431],[98.95523,30.74862],[98.90613,30.68457],[98.99642,30.15344],[99.0438,30.07979],[99.05651,29.93708],[99.01119,29.8189],[98.98956,29.66359],[99.06234,29.45051],[99.05479,29.30586],[99.11487,29.22679],[99.15504,28.43488],[99.39742,28.16463],[99.38713,28.51033],[99.66384,28.82362],[99.96116,28.56582],[100.00785,28.21063],[100.33607,27.72608],[100.71166,27.83907],[101.15867,27.0316],[101.37428,26.88778],[101.46629,26.60387],[101.37908,26.60571],[101.79725,26.16653],[101.81167,26.05061],[102.10075,26.07652],[102.38708,26.29095],[102.957,26.34019],[103.05725,26.53079],[102.90412,26.90308],[102.91992,27.13248],[102.88352,27.25585],[102.93777,27.41078],[103.09295,27.39676],[103.57601,27.97135],[103.40434,28.04077],[103.63574,28.26719],[103.70304,28.19974],[103.76552,28.23453],[103.87367,28.30256],[103.85959,28.38807],[103.78749,28.51757],[103.85616,28.68004],[103.89015,28.62672],[104.08241,28.61014],[104.36977,28.65293],[104.46659,28.61556],[104.23725,28.54532],[104.30076,28.30679],[104.35432,28.34366],[104.44805,28.11801],[104.28909,28.05834],[104.56409,27.85],[104.87205,27.90766],[105.03616,28.09742],[105.17074,28.07258],[105.31219,27.71088],[105.60676,27.69751],[105.92262,27.72973],[106.35864,27.83907],[106.20826,28.13497],[106.13857,28.16948],[105.9233,28.1277],[105.8876,28.23785],[105.65895,28.308],[105.61981,28.43488],[105.68778,28.57547],[105.88142,28.602],[105.96588,28.7496],[106.36756,28.52662],[106.26113,28.83715],[106.0033,28.97811],[105.90408,28.9054],[105.74958,29.01894],[105.7101,29.30047],[105.65036,29.24357],[105.43888,29.31783],[105.44128,29.40131],[105.37261,29.42285],[105.29193,29.58122],[105.45707,29.67612],[105.60127,29.83707],[105.70667,29.83796],[105.75302,30.01619],[105.59062,30.11216],[105.61431,30.26381],[105.68778,30.26618],[105.79627,30.44393],[106.16397,30.30413],[106.21067,30.18134],[106.54643,30.32843],[106.76582,30.01738],[106.96941,30.08484],[107.03155,30.04532],[107.19291,30.18727],[107.50431,30.63732],[107.42637,30.7318],[107.48508,30.84476],[107.63683,30.81233],[107.69657,30.876],[107.84866,30.79405],[107.99114,30.91076],[107.92282,30.92578],[108.07182,31.18049],[108.02032,31.24803],[108.54011,31.67559],[108.27369,31.92885],[108.50234,32.20641],[108.25309,32.28365],[107.97569,32.13782],[107.42362,32.55433],[107.25677,32.40779],[107.1215,32.48543],[107.1009,32.67174],[106.85851,32.71855],[106.43073,32.6307],[106.11007,32.72144],[106.04484,32.86805],[105.62461,32.70526],[105.47878,32.89406],[105.43647,32.94875],[105.39321,32.72433],[105.11032,32.60004],[104.40238,32.79189],[104.4326,33.00636],[104.33921,33.1979],[104.45526,33.32938],[104.28634,33.35978],[104.10026,33.68435],[103.77891,33.66606],[103.16162,33.7974],[103.17672,34.07484],[102.89039,34.33266],[102.66792,34.07541],[102.45849,34.09929],[102.3912,33.97753],[102.14332,33.98379],[102.46879,33.47211],[101.81579,33.10937],[101.94625,33.58773],[101.76223,33.46925],[101.64001,33.09844],[101.1621,33.22835],[101.22665,32.76071],[101.1185,32.63619],[100.94032,32.60756],[100.71819,32.67492],[100.66463,32.52481],[100.54687,32.5714],[100.49554,32.65671],[99.8822,33.04781],[99.73388,32.72375],[99.36035,32.90092],[98.85497,33.14675],[98.73962,33.43717],[98.42651,33.85217],[98.41689,34.09816],[97.66158,34.12431],[97.65266,33.93538],[97.39517,33.89492],[97.40409,33.63062],[97.7584,33.40536],[97.62348,33.33769],[97.59841,33.25964],[97.4858,33.166],[97.49061,33.11512],[97.53078,32.99167],[97.4319,32.9813],[97.33989,32.9038]]]}},{"type":"Feature","properties":{"id":"AR"},"geometry":{"type":"Polygon","coordinates":[[[-73.55259,-49.92488],[-73.15765,-50.78337],[-72.31343,-50.58411],[-72.33873,-51.59954],[-71.99889,-51.98018],[-69.97824,-52.00845],[-68.41683,-52.33516],[-68.60702,-52.65781],[-68.60733,-54.9125],[-68.01394,-54.8753],[-67.46182,-54.92205],[-67.11046,-54.94199],[-66.07313,-55.19618],[-63.07977,-55.04486],[-62.78369,-53.1401],[-62.3754,-50.36819],[-55.71154,-35.78518],[-57.83001,-34.69099],[-58.34425,-34.15035],[-58.44442,-33.84033],[-58.40475,-33.11777],[-58.1224,-32.98842],[-58.22362,-32.52416],[-58.10036,-32.25338],[-58.20252,-31.86966],[-58.00076,-31.65016],[-58.0023,-31.53084],[-58.07569,-31.44916],[-57.98127,-31.3872],[-57.9908,-31.34924],[-57.86729,-31.06352],[-57.89476,-30.95994],[-57.8024,-30.77193],[-57.89115,-30.49572],[-57.64859,-30.35095],[-57.61478,-30.25165],[-57.65132,-30.19229],[-57.09386,-29.74211],[-56.81251,-29.48154],[-56.62789,-29.18073],[-56.57295,-29.11357],[-56.54171,-29.11447],[-56.05265,-28.62651],[-56.00458,-28.60421],[-56.01729,-28.51223],[-55.65418,-28.18304],[-55.6262,-28.17124],[-55.33303,-27.94661],[-55.16872,-27.86224],[-55.1349,-27.89759],[-54.90805,-27.73149],[-54.90159,-27.63132],[-54.67657,-27.57214],[-54.50416,-27.48232],[-54.41888,-27.40882],[-54.19268,-27.30751],[-54.19062,-27.27639],[-54.15978,-27.2889],[-53.80144,-27.09844],[-53.73372,-26.6131],[-53.68269,-26.33359],[-53.64505,-26.28089],[-53.64186,-26.25976],[-53.64632,-26.24798],[-53.63881,-26.25075],[-53.63739,-26.2496],[-53.65237,-26.23289],[-53.65018,-26.19501],[-53.73968,-26.10012],[-53.73391,-26.07006],[-53.7264,-26.0664],[-53.73086,-26.05842],[-53.73511,-26.04211],[-53.83691,-25.94849],[-53.90831,-25.55513],[-54.52926,-25.62846],[-54.5502,-25.58915],[-54.59398,-25.59224],[-54.62063,-25.91213],[-54.60664,-25.9691],[-54.67359,-25.98607],[-54.69333,-26.37705],[-54.70732,-26.45099],[-54.80868,-26.55669],[-55.00584,-26.78754],[-55.06351,-26.80195],[-55.16948,-26.96068],[-55.25243,-26.93808],[-55.39611,-26.97679],[-55.62322,-27.1941],[-55.59094,-27.32444],[-55.74475,-27.44485],[-55.89195,-27.3467],[-56.18313,-27.29851],[-56.85337,-27.5165],[-58.04205,-27.2387],[-58.59549,-27.29973],[-58.65321,-27.14028],[-58.3198,-26.83443],[-58.1188,-26.16704],[-57.87176,-25.93604],[-57.57431,-25.47269],[-57.80821,-25.13863],[-58.25492,-24.92528],[-58.33055,-24.97099],[-59.33886,-24.49935],[-59.45482,-24.34787],[-60.03367,-24.00701],[-60.28163,-24.04436],[-60.99754,-23.80934],[-61.0782,-23.62932],[-61.9756,-23.0507],[-62.22768,-22.55807],[-62.51761,-22.37684],[-62.64455,-22.25091],[-62.8078,-22.12534],[-62.81124,-21.9987],[-63.66482,-21.99918],[-63.68113,-22.0544],[-63.70963,-21.99934],[-63.93287,-21.99934],[-64.22918,-22.55807],[-64.31489,-22.88824],[-64.35108,-22.73282],[-64.4176,-22.67692],[-64.58888,-22.25035],[-64.67174,-22.18957],[-64.90014,-22.12136],[-64.99524,-22.08255],[-65.47435,-22.08908],[-65.57743,-22.07675],[-65.58694,-22.09794],[-65.61166,-22.09504],[-65.7467,-22.10105],[-65.9261,-21.93335],[-66.04832,-21.9187],[-66.03836,-21.84829],[-66.24077,-21.77837],[-66.29714,-22.08741],[-66.7298,-22.23644],[-67.18382,-22.81525],[-66.99632,-22.99839],[-67.33563,-24.04237],[-68.24825,-24.42596],[-68.56909,-24.69831],[-68.38372,-25.08636],[-68.57622,-25.32505],[-68.38372,-26.15353],[-68.56909,-26.28146],[-68.59048,-26.49861],[-68.27677,-26.90626],[-68.43363,-27.08414],[-68.77586,-27.16029],[-69.22504,-27.95042],[-69.66709,-28.44055],[-69.80969,-29.07185],[-69.99507,-29.28351],[-69.8596,-30.26131],[-70.14479,-30.36595],[-70.55832,-31.51559],[-69.88099,-33.34489],[-69.87386,-34.13344],[-70.49416,-35.24145],[-70.38008,-36.02375],[-70.95047,-36.4321],[-71.24279,-37.20264],[-70.89532,-38.6923],[-71.37826,-38.91474],[-71.92726,-40.72714],[-71.74901,-42.11711],[-72.15541,-42.15941],[-72.14828,-42.85321],[-71.64206,-43.64774],[-71.81318,-44.38097],[-71.16436,-44.46244],[-71.26418,-44.75684],[-72.06985,-44.81756],[-71.35687,-45.22075],[-71.75614,-45.61611],[-71.68577,-46.55385],[-71.94152,-47.13595],[-72.50478,-47.80586],[-72.27662,-48.28727],[-72.54042,-48.52392],[-72.56894,-48.81116],[-73.09655,-49.14342],[-73.45156,-49.79461],[-73.55259,-49.92488]]]}},{"type":"Feature","properties":{"id":"CN-YN"},"geometry":{"type":"Polygon","coordinates":[[[97.5247,23.94032],[97.64667,23.84574],[97.72302,23.89288],[97.79456,23.94836],[97.79416,23.95663],[97.84328,23.97603],[97.86545,23.97723],[97.88811,23.97446],[97.8955,23.97758],[97.89676,23.97931],[97.89683,23.98389],[97.88814,23.98605],[97.88414,23.99405],[97.88616,24.00463],[97.90998,24.02094],[97.93951,24.01953],[97.98691,24.03897],[97.99583,24.04932],[98.04709,24.07616],[98.05302,24.07408],[98.05671,24.07961],[98.0607,24.07812],[98.06703,24.08028],[98.07806,24.07988],[98.20666,24.11406],[98.54476,24.13119],[98.59256,24.08371],[98.85319,24.13042],[98.87998,24.15624],[98.89632,24.10612],[98.67797,23.9644],[98.68209,23.80492],[98.79607,23.77947],[98.82933,23.72921],[98.81775,23.694],[98.88396,23.59555],[98.80294,23.5345],[98.82877,23.47908],[98.87683,23.48995],[98.92104,23.36946],[98.87573,23.33038],[98.93958,23.31414],[98.92515,23.29535],[98.88597,23.18656],[99.05975,23.16382],[99.04601,23.12215],[99.25741,23.09025],[99.34127,23.13099],[99.52214,23.08218],[99.54218,22.90014],[99.43537,22.94086],[99.45654,22.85726],[99.31243,22.73893],[99.38247,22.57544],[99.37972,22.50188],[99.28771,22.4105],[99.17318,22.18025],[99.19176,22.16983],[99.1552,22.15874],[99.33166,22.09656],[99.47585,22.13345],[99.85351,22.04183],[99.96612,22.05965],[99.99084,21.97053],[99.94003,21.82782],[99.98654,21.71064],[100.04956,21.66843],[100.12679,21.70539],[100.17486,21.65306],[100.10757,21.59945],[100.12542,21.50365],[100.1625,21.48704],[100.18447,21.51898],[100.25863,21.47043],[100.35201,21.53176],[100.42892,21.54325],[100.4811,21.46148],[100.57861,21.45637],[100.72143,21.51898],[100.87265,21.67396],[101.11744,21.77659],[101.15156,21.56129],[101.2124,21.56422],[101.19349,21.41959],[101.26912,21.36482],[101.2229,21.23271],[101.29326,21.17254],[101.54563,21.25668],[101.6068,21.23329],[101.59491,21.18621],[101.60886,21.17947],[101.66977,21.20004],[101.70548,21.14911],[101.7622,21.14813],[101.79266,21.19025],[101.76745,21.21571],[101.83887,21.20983],[101.84412,21.25291],[101.74014,21.30967],[101.74224,21.48276],[101.7727,21.51794],[101.7475,21.5873],[101.80001,21.57461],[101.83257,21.61562],[101.74555,21.72852],[101.7791,21.83019],[101.62566,21.96574],[101.57525,22.13026],[101.60675,22.13513],[101.53638,22.24794],[101.56789,22.28876],[101.61306,22.27515],[101.68973,22.46843],[101.7685,22.50337],[101.86828,22.38397],[101.90714,22.38688],[101.91344,22.44417],[101.98487,22.42766],[102.03633,22.46164],[102.1245,22.43372],[102.14099,22.40092],[102.16621,22.43336],[102.26428,22.41321],[102.25339,22.4607],[102.41061,22.64184],[102.38415,22.67919],[102.42618,22.69212],[102.46665,22.77108],[102.51802,22.77969],[102.57095,22.7036],[102.60675,22.73376],[102.8636,22.60735],[102.9321,22.48659],[103.0722,22.44775],[103.07843,22.50097],[103.17961,22.55705],[103.15782,22.59873],[103.18895,22.64471],[103.28079,22.68063],[103.32282,22.8127],[103.43179,22.75816],[103.43646,22.70648],[103.52675,22.59155],[103.57812,22.65764],[103.56255,22.69499],[103.64506,22.79979],[103.87904,22.56683],[103.93286,22.52703],[103.94513,22.52553],[103.95191,22.5134],[103.96352,22.50584],[103.96783,22.51173],[103.97384,22.50634],[103.99247,22.51958],[104.01088,22.51823],[104.03734,22.72945],[104.11384,22.80363],[104.27084,22.8457],[104.25683,22.76534],[104.35593,22.69353],[104.47225,22.75813],[104.58122,22.85571],[104.60457,22.81841],[104.65283,22.83419],[104.72755,22.81984],[104.77114,22.90017],[104.84942,22.93631],[104.86765,22.95178],[104.8334,23.01484],[104.79478,23.12934],[104.87382,23.12854],[104.87992,23.17141],[104.91435,23.18666],[104.9486,23.17235],[104.96532,23.20463],[104.98712,23.19176],[105.07002,23.26248],[105.11672,23.25247],[105.17276,23.28679],[105.22569,23.27249],[105.32376,23.39684],[105.40782,23.28107],[105.42805,23.30824],[105.49966,23.20669],[105.56037,23.16806],[105.59509,23.31766],[105.8773,23.53314],[106.00089,23.4519],[106.14097,23.5772],[106.15608,23.90592],[106.00364,24.12858],[105.64796,24.03831],[105.57174,24.13798],[105.49552,24.01949],[105.16868,24.14988],[105.19615,24.34209],[105.0238,24.43839],[104.71481,24.43777],[104.70588,24.31018],[104.56718,24.44527],[104.4659,24.65044],[104.52804,24.73498],[104.60151,24.89889],[104.71206,25.00348],[104.72545,25.19096],[104.83119,25.172],[104.78622,25.28195],[104.66022,25.2745],[104.52152,25.52695],[104.44633,25.47861],[104.309,25.65947],[104.47002,26.02007],[104.59258,26.31926],[104.68666,26.3691],[104.56031,26.59405],[104.47963,26.58422],[104.40925,26.73028],[104.34162,26.62444],[104.15279,26.67323],[104.00104,26.51389],[103.8153,26.53417],[103.7051,26.83173],[103.78063,26.949],[103.68621,27.06248],[103.61377,27.00591],[103.62716,27.11872],[103.83865,27.27202],[103.94165,27.45375],[104.18334,27.2708],[104.37217,27.47233],[104.50984,27.40712],[104.6046,27.30528],[104.85145,27.34523],[104.86312,27.28362],[105.08079,27.42114],[105.21057,27.37603],[105.31219,27.71088],[105.23254,27.90584],[105.17074,28.07258],[105.03616,28.09742],[104.87205,27.90766],[104.56409,27.85],[104.28909,28.05834],[104.44805,28.11801],[104.35432,28.34366],[104.30076,28.30679],[104.23725,28.54532],[104.46659,28.61556],[104.36977,28.65293],[104.08241,28.61014],[103.89015,28.62672],[103.85616,28.68004],[103.78749,28.51757],[103.85959,28.38807],[103.87367,28.30256],[103.76552,28.23453],[103.70304,28.19974],[103.63574,28.26719],[103.40434,28.04077],[103.57601,27.97135],[103.09295,27.39676],[102.93777,27.41078],[102.88352,27.25585],[102.91992,27.13248],[102.90412,26.90308],[103.05725,26.53079],[102.957,26.34019],[102.38708,26.29095],[102.10075,26.07652],[101.81167,26.05061],[101.79725,26.16653],[101.37908,26.60571],[101.46629,26.60387],[101.37428,26.88778],[101.15867,27.0316],[100.71166,27.83907],[100.33607,27.72608],[100.00785,28.21063],[99.96116,28.56582],[99.66384,28.82362],[99.38713,28.51033],[99.39742,28.16463],[99.15504,28.43488],[99.11487,29.22679],[98.96003,29.18663],[99.0184,29.03425],[98.92501,28.98111],[98.91815,28.88796],[98.97376,28.87564],[98.97548,28.82933],[98.83197,28.80406],[98.78974,29.01054],[98.62495,28.9721],[98.65447,28.8585],[98.66254,28.79549],[98.68125,28.73319],[98.63113,28.69103],[98.5968,28.68622],[98.63697,28.49072],[98.75884,28.33218],[98.69361,28.21789],[98.60881,28.1725],[98.39355,28.10953],[98.36952,28.26084],[98.28987,28.39804],[98.20129,28.35666],[98.26789,28.24421],[98.16696,28.21002],[98.15337,28.12114],[98.13964,27.9478],[98.32641,27.51385],[98.42529,27.55404],[98.43353,27.67086],[98.69582,27.56499],[98.7333,26.85615],[98.77547,26.61994],[98.72741,26.36183],[98.67797,26.24487],[98.7329,26.17218],[98.66884,26.09165],[98.63128,26.15492],[98.57085,26.11547],[98.60763,26.01512],[98.70818,25.86241],[98.63128,25.79937],[98.54064,25.85129],[98.40606,25.61129],[98.31268,25.55307],[98.25774,25.6051],[98.16848,25.62739],[98.18084,25.56298],[98.12591,25.50722],[98.14925,25.41547],[97.92541,25.20815],[97.83614,25.2715],[97.77023,25.11492],[97.72216,25.08508],[97.72903,24.91332],[97.79949,24.85655],[97.76481,24.8289],[97.73127,24.83015],[97.70181,24.84557],[97.64354,24.79171],[97.56648,24.76475],[97.56383,24.75535],[97.5542,24.74943],[97.54675,24.74202],[97.56525,24.72838],[97.56286,24.54535],[97.52757,24.43748],[97.60029,24.4401],[97.66998,24.45288],[97.7098,24.35658],[97.65624,24.33781],[97.66723,24.30027],[97.71941,24.29652],[97.76799,24.26365],[97.72998,24.2302],[97.72799,24.18883],[97.75305,24.16902],[97.72903,24.12606],[97.62363,24.00506],[97.5247,23.94032]]]}},{"type":"Feature","properties":{"id":"IN-TR"},"geometry":{"type":"Polygon","coordinates":[[[91.15579,23.6599],[91.28293,23.37538],[91.36453,23.06612],[91.40848,23.07117],[91.4035,23.27522],[91.46615,23.2328],[91.54993,23.01051],[91.61571,22.93929],[91.7324,23.00043],[91.81634,23.08001],[91.76417,23.26619],[91.84789,23.42235],[91.95642,23.47361],[91.95093,23.73284],[92.04706,23.64229],[92.15417,23.73409],[92.26541,23.70392],[92.31777,23.86543],[92.29819,24.25406],[92.21786,24.25259],[92.27931,24.39213],[92.22953,24.50245],[92.15796,24.54435],[92.11662,24.38997],[91.96603,24.3799],[91.89258,24.14674],[91.82596,24.22345],[91.76004,24.23848],[91.73257,24.14703],[91.65292,24.22095],[91.63782,24.1132],[91.55542,24.08687],[91.37414,24.10693],[91.35741,23.99072],[91.29587,24.0041],[91.22308,23.89616],[91.25192,23.83463],[91.15579,23.6599]]]}},{"type":"Feature","properties":{"id":"AT"},"geometry":{"type":"Polygon","coordinates":[[[9.53116,47.27029],[9.56766,47.24281],[9.55176,47.22585],[9.56981,47.21926],[9.58264,47.20673],[9.56539,47.17124],[9.62623,47.14685],[9.63395,47.08443],[9.61216,47.07732],[9.60717,47.06091],[9.87935,47.01337],[9.88266,46.93343],[9.98058,46.91434],[10.10715,46.84296],[10.22675,46.86942],[10.24128,46.93147],[10.30031,46.92093],[10.36933,47.00212],[10.48376,46.93891],[10.47197,46.85698],[10.54783,46.84505],[10.66405,46.87614],[10.75753,46.82258],[10.72974,46.78972],[11.00764,46.76896],[11.10618,46.92966],[11.33355,46.99862],[11.50739,47.00644],[11.74789,46.98484],[12.19254,47.09331],[12.21781,47.03996],[12.11675,47.01241],[12.2006,46.88854],[12.27591,46.88651],[12.38708,46.71529],[12.59992,46.6595],[12.94445,46.60401],[13.27627,46.56059],[13.64088,46.53438],[13.7148,46.5222],[13.89837,46.52331],[14.00422,46.48474],[14.04002,46.49117],[14.12097,46.47724],[14.15989,46.43327],[14.28326,46.44315],[14.314,46.43327],[14.42608,46.44614],[14.45877,46.41717],[14.52176,46.42617],[14.56463,46.37208],[14.5942,46.43434],[14.66892,46.44936],[14.72185,46.49974],[14.81836,46.51046],[14.83549,46.56614],[14.86419,46.59411],[14.87129,46.61],[14.92283,46.60848],[14.96002,46.63459],[14.98024,46.6009],[15.01451,46.641],[15.14215,46.66131],[15.23711,46.63994],[15.41235,46.65556],[15.45514,46.63697],[15.46906,46.61321],[15.54431,46.6312],[15.55333,46.64988],[15.54533,46.66985],[15.59826,46.68908],[15.62317,46.67947],[15.63255,46.68069],[15.6365,46.6894],[15.6543,46.69228],[15.6543,46.70616],[15.67411,46.70735],[15.69523,46.69823],[15.72279,46.69548],[15.73823,46.70011],[15.76771,46.69863],[15.78518,46.70712],[15.8162,46.71897],[15.87691,46.7211],[15.94864,46.68769],[15.98512,46.68463],[15.99988,46.67947],[16.04036,46.6549],[16.04347,46.68694],[16.02808,46.71094],[15.99769,46.7266],[15.98432,46.74991],[15.99126,46.78199],[15.99054,46.82772],[16.05786,46.83927],[16.10983,46.867],[16.19904,46.94134],[16.22403,46.939],[16.27594,46.9643],[16.28202,47.00159],[16.51369,47.00084],[16.43936,47.03548],[16.52176,47.05747],[16.46134,47.09395],[16.52863,47.13974],[16.44932,47.14418],[16.46442,47.16845],[16.4523,47.18812],[16.42801,47.18422],[16.41739,47.20649],[16.43663,47.21127],[16.44142,47.25079],[16.47782,47.25918],[16.45104,47.41181],[16.49908,47.39416],[16.52414,47.41007],[16.57152,47.40868],[16.6718,47.46139],[16.64821,47.50155],[16.71059,47.52692],[16.64193,47.63114],[16.58699,47.61772],[16.4222,47.66537],[16.55129,47.72268],[16.53514,47.73837],[16.54779,47.75074],[16.61183,47.76171],[16.65679,47.74197],[16.72089,47.73469],[16.7511,47.67878],[16.82938,47.68432],[16.86509,47.72268],[16.87538,47.68895],[17.08893,47.70928],[17.05048,47.79377],[17.07039,47.81129],[17.00997,47.86245],[17.08275,47.87719],[17.11022,47.92461],[17.09786,47.97336],[17.16001,48.00636],[17.07039,48.0317],[17.09168,48.09366],[17.05735,48.14179],[17.02919,48.13996],[16.97701,48.17385],[16.89461,48.31332],[16.90903,48.32519],[16.84243,48.35258],[16.83317,48.38138],[16.83588,48.3844],[16.8497,48.38321],[16.85204,48.44968],[16.94611,48.53614],[16.93955,48.60371],[16.90354,48.71541],[16.79779,48.70998],[16.71883,48.73806],[16.68518,48.7281],[16.67008,48.77699],[16.46134,48.80865],[16.40915,48.74576],[16.37345,48.729],[16.06034,48.75436],[15.84404,48.86921],[15.78087,48.87644],[15.75341,48.8516],[15.6921,48.85973],[15.61622,48.89541],[15.51357,48.91549],[15.48027,48.94481],[15.34823,48.98444],[15.28305,48.98831],[15.26177,48.95766],[15.16358,48.94278],[15.15534,48.99056],[14.99878,49.01444],[14.97612,48.96983],[14.98917,48.90082],[14.95072,48.79101],[14.98032,48.77959],[14.9782,48.7766],[14.98112,48.77524],[14.9758,48.76857],[14.95641,48.75915],[14.94773,48.76268],[14.81545,48.7874],[14.80821,48.77711],[14.80584,48.73489],[14.72756,48.69502],[14.71794,48.59794],[14.66762,48.58215],[14.60808,48.62881],[14.56139,48.60429],[14.4587,48.64695],[14.43076,48.58855],[14.33909,48.55852],[14.20691,48.5898],[14.09104,48.5943],[14.01482,48.63788],[14.06151,48.66873],[13.84023,48.76988],[13.82266,48.75544],[13.81863,48.73257],[13.79337,48.71375],[13.81791,48.69832],[13.81283,48.68426],[13.81901,48.6761],[13.82609,48.62345],[13.80038,48.59487],[13.80519,48.58026],[13.76921,48.55324],[13.7513,48.5624],[13.74816,48.53058],[13.72802,48.51208],[13.66113,48.53558],[13.65186,48.55092],[13.62508,48.55501],[13.59705,48.57013],[13.57535,48.55912],[13.51291,48.59023],[13.50131,48.58091],[13.50663,48.57506],[13.46967,48.55157],[13.45214,48.56472],[13.43695,48.55776],[13.45727,48.51092],[13.42527,48.45711],[13.43929,48.43386],[13.40709,48.37292],[13.30897,48.31575],[13.26039,48.29422],[13.18093,48.29577],[13.126,48.27867],[13.0851,48.27711],[13.02083,48.25689],[12.95306,48.20629],[12.87126,48.20318],[12.84475,48.16556],[12.836,48.1647],[12.8362,48.15876],[12.82673,48.15245],[12.80676,48.14979],[12.78595,48.12445],[12.7617,48.12796],[12.74973,48.10885],[12.76141,48.07373],[12.8549,48.01122],[12.87476,47.96195],[12.91683,47.95647],[12.9211,47.95135],[12.91985,47.94069],[12.92668,47.93879],[12.93419,47.94063],[12.93642,47.94436],[12.93886,47.94046],[12.94163,47.92927],[13.00588,47.84374],[12.98543,47.82896],[12.96311,47.79957],[12.93202,47.77302],[12.94371,47.76281],[12.9353,47.74788],[12.91711,47.74026],[12.90274,47.72513],[12.91333,47.7178],[12.92969,47.71094],[12.98578,47.7078],[13.01382,47.72116],[13.07692,47.68814],[13.09562,47.63304],[13.06407,47.60075],[13.06641,47.58577],[13.04537,47.58183],[13.05355,47.56291],[13.03252,47.53373],[13.04537,47.49426],[12.9998,47.46267],[12.98344,47.48716],[12.9624,47.47452],[12.85256,47.52741],[12.84672,47.54556],[12.80699,47.54477],[12.77427,47.58025],[12.82101,47.61493],[12.76492,47.64485],[12.77777,47.66689],[12.7357,47.6787],[12.6071,47.6741],[12.57438,47.63238],[12.53816,47.63553],[12.50076,47.62293],[12.44117,47.6741],[12.43883,47.6977],[12.37222,47.68433],[12.336,47.69534],[12.27991,47.68827],[12.26004,47.67725],[12.24017,47.69534],[12.26238,47.73544],[12.2542,47.7433],[12.22571,47.71776],[12.18303,47.70065],[12.16217,47.70105],[12.16769,47.68167],[12.18347,47.66663],[12.18507,47.65984],[12.19895,47.64085],[12.20801,47.61082],[12.20398,47.60667],[12.18568,47.6049],[12.17737,47.60121],[12.18145,47.61019],[12.17824,47.61506],[12.13734,47.60639],[12.05788,47.61742],[12.02282,47.61033],[12.0088,47.62451],[11.85572,47.60166],[11.84052,47.58354],[11.63934,47.59202],[11.60681,47.57881],[11.58811,47.55515],[11.58578,47.52281],[11.52618,47.50939],[11.4362,47.51413],[11.38128,47.47465],[11.4175,47.44621],[11.33804,47.44937],[11.29597,47.42566],[11.27844,47.39956],[11.22002,47.3964],[11.25157,47.43277],[11.20482,47.43198],[11.12536,47.41222],[11.11835,47.39719],[10.97111,47.39561],[10.97111,47.41617],[10.98513,47.42882],[10.92437,47.46991],[10.93839,47.48018],[10.90918,47.48571],[10.87061,47.4786],[10.86945,47.5015],[10.91268,47.51334],[10.88814,47.53701],[10.77596,47.51729],[10.7596,47.53228],[10.6965,47.54253],[10.68832,47.55752],[10.63456,47.5591],[10.60337,47.56755],[10.56912,47.53584],[10.48849,47.54057],[10.47329,47.58552],[10.43473,47.58394],[10.44992,47.5524],[10.4324,47.50111],[10.44291,47.48453],[10.46278,47.47901],[10.47446,47.43318],[10.4359,47.41183],[10.4324,47.38494],[10.39851,47.37623],[10.33424,47.30813],[10.23257,47.27088],[10.17531,47.27167],[10.17648,47.29149],[10.2147,47.31014],[10.19998,47.32832],[10.23757,47.37609],[10.22774,47.38904],[10.2127,47.38019],[10.17648,47.38889],[10.16362,47.36674],[10.11805,47.37228],[10.09819,47.35724],[10.06897,47.40709],[10.1052,47.4316],[10.09001,47.46005],[10.07131,47.45531],[10.03859,47.48927],[10.00003,47.48216],[9.96029,47.53899],[9.92407,47.53111],[9.87733,47.54688],[9.87499,47.52953],[9.8189,47.54688],[9.82591,47.58158],[9.80254,47.59419],[9.76748,47.5934],[9.72736,47.53457],[9.55125,47.53629],[9.56312,47.49495],[9.58208,47.48344],[9.59482,47.46305],[9.60205,47.46165],[9.60484,47.46358],[9.60841,47.47178],[9.62158,47.45858],[9.62475,47.45685],[9.6423,47.45599],[9.65728,47.45383],[9.65863,47.44847],[9.64483,47.43842],[9.6446,47.43233],[9.65043,47.41937],[9.65136,47.40504],[9.6629,47.39591],[9.67334,47.39191],[9.67445,47.38429],[9.6711,47.37824],[9.66243,47.37136],[9.65427,47.36824],[9.62476,47.36639],[9.59978,47.34671],[9.58513,47.31334],[9.55857,47.29919],[9.54773,47.2809],[9.53116,47.27029]]]}},{"type":"Feature","properties":{"id":"AZ-NX"},"geometry":{"type":"Polygon","coordinates":[[[44.75779,39.7148],[44.80977,39.65768],[44.81043,39.62677],[44.88916,39.59653],[44.96746,39.42998],[45.05932,39.36435],[45.08751,39.35052],[45.16168,39.21952],[45.30489,39.18333],[45.40148,39.09007],[45.40452,39.07224],[45.44811,39.04927],[45.44966,38.99243],[45.6131,38.964],[45.6155,38.94304],[45.65172,38.95199],[45.83883,38.90768],[45.90266,38.87739],[45.94624,38.89072],[46.00228,38.87376],[46.06766,38.87861],[46.14785,38.84206],[46.06973,39.0744],[46.02303,39.09978],[45.99774,39.28931],[45.79225,39.3695],[45.83,39.46487],[45.80804,39.56716],[45.70547,39.60174],[45.46992,39.49888],[45.29606,39.57654],[45.30385,39.61373],[45.23535,39.61373],[45.21784,39.58074],[45.17464,39.58614],[45.18554,39.67846],[45.06604,39.79277],[44.92869,39.72157],[44.88354,39.74432],[44.75779,39.7148]]]}},{"type":"Feature","properties":{"id":"BI"},"geometry":{"type":"Polygon","coordinates":[[[29.00167,-2.78523],[29.00404,-2.81978],[29.0505,-2.81774],[29.09119,-2.87871],[29.09797,-2.91935],[29.16037,-2.95457],[29.17258,-2.99385],[29.25633,-3.05471],[29.21463,-3.3514],[29.23708,-3.75856],[29.43673,-4.44845],[29.63827,-4.44681],[29.75109,-4.45836],[29.77289,-4.41733],[29.82885,-4.36153],[29.88172,-4.35743],[30.03323,-4.26631],[30.22042,-4.01738],[30.45915,-3.56532],[30.84165,-3.25152],[30.83823,-2.97837],[30.6675,-2.98987],[30.57926,-2.89791],[30.4987,-2.9573],[30.40662,-2.86151],[30.52747,-2.65841],[30.41789,-2.66266],[30.54501,-2.41404],[30.42933,-2.31064],[30.14034,-2.43626],[29.95911,-2.33348],[29.88237,-2.75105],[29.36805,-2.82933],[29.32234,-2.6483],[29.0562,-2.58632],[29.04081,-2.7416],[29.00167,-2.78523]]]}},{"type":"Feature","properties":{"id":"BJ"},"geometry":{"type":"Polygon","coordinates":[[[0.77666,10.37665],[1.35507,9.99525],[1.36624,9.5951],[1.33675,9.54765],[1.41746,9.3226],[1.5649,9.16941],[1.61838,9.0527],[1.64249,6.99562],[1.55877,6.99737],[1.61812,6.74843],[1.58105,6.68619],[1.76906,6.43189],[1.79826,6.28221],[1.62913,6.24075],[1.67336,6.02702],[2.74181,6.13349],[2.70566,6.38038],[2.70464,6.50831],[2.74334,6.57291],[2.7325,6.64057],[2.78204,6.70514],[2.78823,6.76356],[2.73405,6.78508],[2.74024,6.92802],[2.71702,6.95722],[2.76965,7.13543],[2.74489,7.42565],[2.79442,7.43486],[2.78668,7.5116],[2.73405,7.5423],[2.73095,7.7755],[2.67523,7.87825],[2.77907,9.06924],[3.08017,9.10006],[3.14147,9.28375],[3.13928,9.47167],[3.25093,9.61632],[3.34726,9.70696],[3.32099,9.78032],[3.35383,9.83641],[3.54429,9.87739],[3.66908,10.18136],[3.57275,10.27185],[3.6844,10.46351],[3.78292,10.40538],[3.84243,10.59316],[3.71505,11.13015],[3.49175,11.29765],[3.59375,11.70269],[3.48187,11.86092],[3.31613,11.88495],[3.25352,12.01467],[2.83978,12.40585],[2.6593,12.30631],[2.37783,12.24804],[2.39657,12.10952],[2.45824,11.98672],[2.39723,11.89473],[2.29983,11.68254],[2.00988,11.42227],[1.42823,11.46822],[1.03409,11.04719],[0.9813,11.08876],[0.91245,10.99597],[0.8804,10.803],[0.80358,10.71459],[0.77666,10.37665]]]}},{"type":"Feature","properties":{"id":"BF"},"geometry":{"type":"Polygon","coordinates":[[[-5.51058,10.43177],[-5.39602,10.2929],[-5.12465,10.29788],[-4.96453,9.99923],[-4.96621,9.89132],[-4.6426,9.70696],[-4.31392,9.60062],[-4.25999,9.76012],[-3.69703,9.94279],[-3.31779,9.91125],[-3.27228,9.84981],[-3.19306,9.93781],[-3.16609,9.85147],[-3.00765,9.74019],[-2.93012,9.57403],[-2.76494,9.40778],[-2.68802,9.49343],[-2.76534,9.56589],[-2.74174,9.83172],[-2.83108,10.40252],[-2.94232,10.64281],[-2.83373,11.0067],[-0.67143,10.99811],[-0.61937,10.91305],[-0.44298,11.04292],[-0.42391,11.11661],[-0.38219,11.12596],[-0.35955,11.07801],[-0.28566,11.12713],[-0.27374,11.17157],[-0.13493,11.14075],[0.50388,11.01011],[0.48852,10.98561],[0.50521,10.98035],[0.4958,10.93269],[0.66104,10.99964],[0.91245,10.99597],[0.9813,11.08876],[1.03409,11.04719],[1.42823,11.46822],[2.00988,11.42227],[2.29983,11.68254],[2.39723,11.89473],[2.05785,12.35539],[2.26349,12.41915],[0.99167,13.10727],[0.99253,13.37515],[1.18873,13.31771],[1.21217,13.37853],[1.24516,13.33968],[1.28509,13.35488],[1.24429,13.39373],[1.20088,13.38951],[1.02813,13.46635],[0.99514,13.5668],[0.77637,13.64442],[0.77377,13.6866],[0.61924,13.68491],[0.38051,14.05575],[0.16936,14.51654],[0.23859,15.00135],[0.06588,14.96961],[-0.24673,15.07805],[-0.72004,15.08655],[-1.05875,14.7921],[-1.32166,14.72774],[-1.68083,14.50023],[-1.97945,14.47709],[-1.9992,14.19011],[-2.10223,14.14878],[-2.47587,14.29671],[-2.66175,14.14713],[-2.84667,14.05532],[-2.90831,13.81174],[-2.88189,13.64921],[-3.26407,13.70699],[-3.28396,13.5422],[-3.23599,13.29035],[-3.43507,13.27272],[-3.4313,13.1588],[-3.54454,13.1781],[-3.7911,13.36665],[-3.96282,13.38164],[-3.90558,13.44375],[-3.96501,13.49778],[-4.34477,13.12927],[-4.21819,12.95722],[-4.238,12.71467],[-4.47356,12.71252],[-4.41412,12.31922],[-4.57703,12.19875],[-4.54841,12.1385],[-4.62546,12.13204],[-4.62987,12.06531],[-4.70692,12.06746],[-4.72893,12.01579],[-5.07897,11.97918],[-5.26389,11.84778],[-5.40258,11.8327],[-5.26389,11.75728],[-5.29251,11.61715],[-5.22867,11.60421],[-5.20665,11.43811],[-5.25509,11.36905],[-5.25949,11.24816],[-5.32553,11.21578],[-5.32994,11.13371],[-5.49284,11.07538],[-5.41579,10.84628],[-5.47083,10.75329],[-5.46643,10.56074],[-5.51058,10.43177]]]}},{"type":"Feature","properties":{"id":"BD"},"geometry":{"type":"Polygon","coordinates":[[[88.00683,24.66477],[88.08786,24.63232],[88.12296,24.51301],[88.50934,24.32474],[88.68801,24.31464],[88.74841,24.1959],[88.6976,24.14703],[88.73743,23.91751],[88.66189,23.87607],[88.58087,23.87105],[88.56507,23.64044],[88.74841,23.47361],[88.79351,23.50535],[88.79254,23.46028],[88.71133,23.2492],[88.99148,23.21134],[88.86377,23.08759],[88.88327,23.03885],[88.87063,22.95235],[88.96713,22.83346],[88.9151,22.75228],[88.94614,22.66941],[88.9367,22.58527],[89.07114,22.15335],[89.03553,21.77397],[89.13927,21.60785],[89.13606,21.42955],[92.39837,20.38919],[92.4302,20.5688],[92.31348,20.57137],[92.28464,20.63179],[92.37665,20.72172],[92.26071,21.05697],[92.17752,21.17445],[92.20087,21.337],[92.37939,21.47764],[92.43158,21.37025],[92.55105,21.3856],[92.60187,21.24615],[92.68152,21.28454],[92.59775,21.6092],[92.62187,21.87037],[92.60949,21.97638],[92.56616,22.13554],[92.60029,22.1522],[92.5181,22.71441],[92.37665,22.9435],[92.38214,23.28705],[92.26541,23.70392],[92.15417,23.73409],[92.04706,23.64229],[91.95093,23.73284],[91.95642,23.47361],[91.84789,23.42235],[91.76417,23.26619],[91.81634,23.08001],[91.7324,23.00043],[91.61571,22.93929],[91.54993,23.01051],[91.46615,23.2328],[91.4035,23.27522],[91.40848,23.07117],[91.36453,23.06612],[91.28293,23.37538],[91.15579,23.6599],[91.25192,23.83463],[91.22308,23.89616],[91.29587,24.0041],[91.35741,23.99072],[91.37414,24.10693],[91.55542,24.08687],[91.63782,24.1132],[91.65292,24.22095],[91.73257,24.14703],[91.76004,24.23848],[91.82596,24.22345],[91.89258,24.14674],[91.96603,24.3799],[92.11662,24.38997],[92.15796,24.54435],[92.25854,24.9191],[92.38626,24.86055],[92.49887,24.88796],[92.39147,25.01471],[92.33957,25.07593],[92.0316,25.1834],[91.63648,25.12846],[91.25517,25.20677],[90.87427,25.15799],[90.65042,25.17788],[90.40034,25.1534],[90.1155,25.22686],[89.90478,25.31038],[89.87629,25.28337],[89.83371,25.29548],[89.84086,25.31854],[89.81208,25.37244],[89.86129,25.61714],[89.84388,25.70042],[89.80585,25.82489],[89.86592,25.93115],[89.77728,26.04254],[89.77865,26.08387],[89.73581,26.15818],[89.70201,26.15138],[89.63968,26.22595],[89.57101,25.9682],[89.53515,26.00382],[89.35953,26.0077],[89.15869,26.13708],[89.08899,26.38845],[88.95612,26.4564],[88.92357,26.40711],[88.91321,26.37984],[89.05328,26.2469],[88.85004,26.23211],[88.78961,26.31093],[88.67837,26.26291],[88.69485,26.38353],[88.62144,26.46783],[88.4298,26.54489],[88.41196,26.63837],[88.33093,26.48929],[88.35153,26.45241],[88.36938,26.48683],[88.48749,26.45855],[88.51649,26.35923],[88.35153,26.29123],[88.34757,26.22216],[88.1844,26.14417],[88.16581,26.0238],[88.08804,25.91334],[88.13138,25.78773],[88.242,25.80811],[88.45103,25.66245],[88.4559,25.59227],[88.677,25.46959],[88.81296,25.51546],[88.85278,25.34679],[89.01105,25.30303],[89.00463,25.26583],[88.94067,25.18534],[88.44766,25.20149],[88.46277,25.07468],[88.33917,24.86803],[88.27325,24.88796],[88.21832,24.96642],[88.14004,24.93529],[88.15515,24.85806],[88.00683,24.66477]]]}},{"type":"Feature","properties":{"id":"BG"},"geometry":{"type":"Polygon","coordinates":[[[22.34773,42.31725],[22.38136,42.30339],[22.47251,42.20393],[22.50289,42.19527],[22.51224,42.15457],[22.67701,42.06614],[22.86749,42.02275],[22.90254,41.87587],[22.96682,41.77137],[23.01239,41.76527],[23.03342,41.71034],[22.95513,41.63265],[22.96331,41.35782],[22.93334,41.34104],[23.1833,41.31755],[23.21953,41.33773],[23.22771,41.37106],[23.31301,41.40525],[23.33639,41.36317],[23.40416,41.39999],[23.52453,41.40262],[23.63203,41.37632],[23.67644,41.41139],[23.76525,41.40175],[23.80148,41.43943],[23.89613,41.45257],[23.91483,41.47971],[23.96975,41.44118],[24.06908,41.46132],[24.06323,41.53222],[24.10063,41.54796],[24.18126,41.51735],[24.27124,41.57682],[24.30513,41.51297],[24.52599,41.56808],[24.61129,41.42278],[24.71529,41.41928],[24.8041,41.34913],[24.82514,41.4035],[24.86136,41.39298],[24.90928,41.40876],[24.942,41.38685],[25.11611,41.34212],[25.28322,41.23411],[25.48187,41.28506],[25.52394,41.2798],[25.55082,41.31667],[25.61042,41.30614],[25.66183,41.31316],[25.70507,41.29209],[25.8266,41.34563],[25.87919,41.30526],[26.12926,41.35878],[26.16548,41.42278],[26.20288,41.43943],[26.14796,41.47533],[26.176,41.50072],[26.17951,41.55409],[26.14328,41.55496],[26.15146,41.60828],[26.07083,41.64584],[26.06148,41.70345],[26.16841,41.74858],[26.21325,41.73223],[26.22888,41.74139],[26.2654,41.71544],[26.30255,41.70925],[26.35957,41.71149],[26.32952,41.73637],[26.33589,41.76802],[26.36952,41.82265],[26.53968,41.82653],[26.57961,41.90024],[26.56051,41.92995],[26.62996,41.97644],[26.79143,41.97386],[26.95638,42.00741],[27.03277,42.0809],[27.08486,42.08735],[27.19251,42.06028],[27.22376,42.10152],[27.27411,42.10409],[27.45478,41.96591],[27.52379,41.93756],[27.55191,41.90928],[27.69949,41.97515],[27.81235,41.94803],[27.83492,41.99709],[27.91479,41.97902],[28.02971,41.98066],[28.32297,41.98371],[29.24336,43.70874],[28.23293,43.76],[27.99558,43.84193],[27.92008,44.00761],[27.73468,43.95326],[27.64542,44.04958],[27.60834,44.01206],[27.39757,44.0141],[27.26845,44.12602],[26.95141,44.13555],[26.62712,44.05698],[26.38764,44.04356],[26.10115,43.96908],[26.05584,43.90925],[25.94911,43.85745],[25.72792,43.69263],[25.39528,43.61866],[25.17144,43.70261],[25.10718,43.6831],[24.96682,43.72693],[24.73542,43.68523],[24.62281,43.74082],[24.50264,43.76314],[24.35364,43.70211],[24.18149,43.68218],[23.73978,43.80627],[23.61687,43.79289],[23.4507,43.84936],[23.26772,43.84843],[23.05288,43.79494],[22.85314,43.84452],[22.83753,43.88055],[22.87873,43.9844],[23.01674,44.01946],[23.04988,44.07694],[22.67173,44.21564],[22.61711,44.16938],[22.61688,44.06534],[22.41449,44.00514],[22.35558,43.81281],[22.41043,43.69566],[22.47582,43.6558],[22.53397,43.47225],[22.82036,43.33665],[22.89727,43.22417],[23.00806,43.19279],[22.98104,43.11199],[22.89521,43.03625],[22.78397,42.98253],[22.74826,42.88701],[22.54302,42.87774],[22.43309,42.82057],[22.4997,42.74144],[22.43983,42.56851],[22.55669,42.50144],[22.51961,42.3991],[22.47498,42.3915],[22.45919,42.33822],[22.34773,42.31725]]]}},{"type":"Feature","properties":{"id":"BH"},"geometry":{"type":"Polygon","coordinates":[[[50.26923,26.08243],[50.302,25.87592],[50.57069,25.57887],[50.80824,25.54641],[50.7801,25.595],[50.86149,25.6965],[50.81266,25.88946],[50.93865,26.30758],[50.71771,26.73086],[50.38162,26.53976],[50.26923,26.08243]]]}},{"type":"Feature","properties":{"id":"BA"},"geometry":{"type":"Polygon","coordinates":[[[15.72584,44.82334],[15.8255,44.71501],[15.89348,44.74964],[16.05828,44.61538],[16.00884,44.58605],[16.03012,44.55572],[16.10566,44.52586],[16.16814,44.40679],[16.12969,44.38275],[16.21346,44.35231],[16.18688,44.27012],[16.36864,44.08263],[16.43662,44.07523],[16.43629,44.02826],[16.50528,44.0244],[16.55472,43.95326],[16.70922,43.84887],[16.75316,43.77157],[16.80736,43.76011],[17.00585,43.58037],[17.15828,43.49376],[17.24411,43.49376],[17.29699,43.44542],[17.25579,43.40353],[17.286,43.33065],[17.46986,43.16559],[17.64268,43.08595],[17.70879,42.97223],[17.5392,42.92787],[17.6444,42.88641],[17.68151,42.92725],[17.7948,42.89556],[17.80854,42.9182],[17.88201,42.83668],[18.24318,42.6112],[18.36197,42.61423],[18.43735,42.55921],[18.49778,42.58409],[18.53751,42.57376],[18.55504,42.58409],[18.52232,42.62279],[18.57373,42.64429],[18.54841,42.68328],[18.54603,42.69171],[18.55221,42.69045],[18.56789,42.72074],[18.47324,42.74992],[18.45921,42.81682],[18.47633,42.85829],[18.4935,42.86433],[18.49661,42.89306],[18.49076,42.95553],[18.52232,43.01451],[18.66254,43.03928],[18.64735,43.14766],[18.66605,43.2056],[18.71747,43.2286],[18.6976,43.25243],[18.76538,43.29838],[18.85342,43.32426],[18.84794,43.33735],[18.83912,43.34795],[18.90911,43.36383],[18.95819,43.32899],[18.95001,43.29327],[19.00844,43.24988],[19.04233,43.30008],[19.08206,43.29668],[19.08673,43.31453],[19.04071,43.397],[19.01078,43.43854],[18.96053,43.45042],[18.95469,43.49367],[18.91379,43.50299],[19.01078,43.55806],[19.04934,43.50384],[19.13933,43.5282],[19.15685,43.53943],[19.22807,43.5264],[19.24774,43.53061],[19.2553,43.5938],[19.33426,43.58833],[19.36653,43.60921],[19.41941,43.54056],[19.42696,43.57987],[19.50455,43.58385],[19.5176,43.71403],[19.3986,43.79668],[19.23465,43.98764],[19.24363,44.01502],[19.38439,43.96611],[19.52515,43.95573],[19.56498,43.99922],[19.61836,44.01464],[19.61991,44.05254],[19.57467,44.04716],[19.55999,44.06894],[19.51167,44.08158],[19.47321,44.1193],[19.48386,44.14332],[19.47338,44.15034],[19.43905,44.13088],[19.40927,44.16722],[19.3588,44.18353],[19.34773,44.23244],[19.32464,44.27185],[19.26945,44.26957],[19.23306,44.26097],[19.20508,44.2917],[19.18328,44.28383],[19.16741,44.28648],[19.13332,44.31492],[19.13556,44.338],[19.11547,44.34218],[19.1083,44.3558],[19.11865,44.36712],[19.10298,44.36924],[19.10365,44.37795],[19.10704,44.38249],[19.10749,44.39421],[19.11785,44.40313],[19.14681,44.41463],[19.14837,44.45253],[19.12278,44.50132],[19.13369,44.52521],[19.16699,44.52197],[19.26388,44.65412],[19.32543,44.74058],[19.36722,44.88164],[19.18183,44.92055],[19.01994,44.85493],[18.8704,44.85097],[18.76347,44.90669],[18.76369,44.93707],[18.80661,44.93561],[18.78357,44.97741],[18.65723,45.07544],[18.47939,45.05871],[18.41896,45.11083],[18.32077,45.1021],[18.24387,45.13699],[18.1624,45.07654],[18.03121,45.12632],[18.01594,45.15163],[17.99479,45.14958],[17.97834,45.13831],[17.97336,45.12245],[17.93706,45.08016],[17.87148,45.04645],[17.84826,45.04489],[17.66571,45.13408],[17.59104,45.10816],[17.51469,45.10791],[17.47589,45.12656],[17.45615,45.12523],[17.4498,45.16119],[17.41229,45.13335],[17.33573,45.14521],[17.32092,45.16246],[17.26815,45.18444],[17.25131,45.14957],[17.24325,45.146],[17.18438,45.14764],[17.0415,45.20759],[16.9385,45.22742],[16.92405,45.27607],[16.83804,45.18951],[16.81137,45.18434],[16.78219,45.19002],[16.74845,45.20393],[16.64962,45.20714],[16.60194,45.23042],[16.56559,45.22307],[16.5501,45.2212],[16.52982,45.22713],[16.49155,45.21153],[16.4634,45.14522],[16.40023,45.1147],[16.38309,45.05955],[16.38219,45.05139],[16.3749,45.05206],[16.35863,45.03529],[16.35404,45.00241],[16.29036,44.99732],[16.12153,45.09616],[15.98412,45.23088],[15.83512,45.22459],[15.76371,45.16508],[15.78842,45.11519],[15.74585,45.0638],[15.78568,44.97401],[15.74723,44.96818],[15.76096,44.87045],[15.79472,44.8455],[15.72584,44.82334]]]}},{"type":"Feature","properties":{"id":"BY"},"geometry":{"type":"Polygon","coordinates":[[[23.18196,52.28812],[23.20071,52.22848],[23.47859,52.18215],[23.54314,52.12148],[23.61,52.11264],[23.64066,52.07626],[23.68733,51.9906],[23.61523,51.92066],[23.62691,51.78208],[23.53198,51.74298],[23.57053,51.55938],[23.56236,51.53673],[23.62751,51.50512],[23.6736,51.50255],[23.60906,51.62122],[23.7766,51.66809],[23.91118,51.63316],[23.8741,51.59734],[23.99907,51.58369],[24.13075,51.66979],[24.3163,51.75063],[24.29021,51.80841],[24.37123,51.88222],[24.98784,51.91273],[25.20228,51.97143],[25.46163,51.92205],[25.73673,51.91973],[25.80574,51.94556],[25.83217,51.92587],[26.00408,51.92967],[26.19084,51.86781],[26.39367,51.87315],[26.46962,51.80501],[26.69759,51.82284],[26.80043,51.75777],[26.9489,51.73788],[26.99422,51.76933],[27.20602,51.77291],[27.20948,51.66713],[27.26613,51.65957],[27.24828,51.60161],[27.47212,51.61184],[27.51058,51.5854],[27.55727,51.63486],[27.71932,51.60672],[27.67125,51.50854],[27.76052,51.47604],[27.85253,51.62293],[27.91844,51.61952],[27.95827,51.56065],[28.10658,51.57857],[28.23452,51.66988],[28.37592,51.54505],[28.47051,51.59734],[28.64429,51.5664],[28.69161,51.44695],[28.73143,51.46236],[28.75615,51.41442],[28.78224,51.45294],[28.76027,51.48802],[28.81795,51.55552],[28.95528,51.59222],[28.99098,51.56833],[29.1187,51.65872],[29.16402,51.64679],[29.20659,51.56918],[29.25603,51.57089],[29.25191,51.49828],[29.32881,51.37843],[29.42357,51.4187],[29.49773,51.39814],[29.54372,51.48372],[29.7408,51.53417],[29.77376,51.4461],[30.17888,51.51025],[30.34642,51.42555],[30.36153,51.33984],[30.56203,51.25655],[30.64992,51.35014],[30.51946,51.59649],[30.68804,51.82806],[30.76443,51.89739],[30.90897,52.00699],[30.95589,52.07775],[31.13332,52.1004],[31.25142,52.04131],[31.38326,52.12991],[31.7822,52.11406],[31.77877,52.18636],[31.6895,52.1973],[31.70735,52.26711],[31.57971,52.32146],[31.62084,52.33849],[31.61397,52.48843],[31.56316,52.51518],[31.63869,52.55361],[31.50406,52.69707],[31.57277,52.71613],[31.592,52.79011],[31.35667,52.97854],[31.24147,53.031],[31.32283,53.04101],[31.33519,53.08805],[31.3915,53.09712],[31.36403,53.13504],[31.40523,53.21406],[31.56316,53.19432],[31.62496,53.22886],[31.787,53.18033],[31.82373,53.10042],[32.15368,53.07594],[32.40773,53.18856],[32.51725,53.28431],[32.73257,53.33494],[32.74968,53.45597],[32.47777,53.5548],[32.40499,53.6656],[32.50112,53.68594],[32.45717,53.74039],[32.36663,53.7166],[32.12621,53.81586],[31.89137,53.78099],[31.77028,53.80015],[31.85019,53.91801],[31.88744,54.03653],[31.89599,54.0837],[31.57002,54.14535],[31.30791,54.25315],[31.3177,54.34067],[31.22945,54.46585],[31.08543,54.50361],[31.21399,54.63113],[31.19339,54.66947],[30.99187,54.67046],[30.98226,54.68872],[31.0262,54.70698],[30.97127,54.71967],[30.95479,54.74346],[30.75165,54.80699],[30.8264,54.90062],[30.81759,54.94064],[30.93144,54.9585],[30.95754,54.98609],[30.9081,55.02232],[30.94243,55.03964],[31.00972,55.02783],[31.02071,55.06167],[30.97369,55.17134],[30.87944,55.28223],[30.81946,55.27931],[30.8257,55.3313],[30.93144,55.3914],[30.90123,55.46621],[30.95204,55.50667],[30.93419,55.6185],[30.86003,55.63169],[30.7845,55.58514],[30.72957,55.66268],[30.67464,55.64176],[30.63344,55.73079],[30.51037,55.76568],[30.51346,55.78982],[30.48257,55.81066],[30.30987,55.83592],[30.27776,55.86819],[30.12136,55.8358],[29.97975,55.87281],[29.80672,55.79569],[29.61446,55.77716],[29.51283,55.70294],[29.3604,55.75862],[29.44692,55.95978],[29.21717,55.98971],[29.08299,56.03427],[28.73418,55.97131],[28.63668,56.07262],[28.68337,56.10173],[28.5529,56.11705],[28.43068,56.09407],[28.37987,56.11399],[28.36888,56.05805],[28.30571,56.06035],[28.15217,56.16964],[27.97865,56.11849],[27.63065,55.89687],[27.61683,55.78558],[27.3541,55.8089],[27.27804,55.78299],[27.1559,55.85032],[26.97153,55.8102],[26.87448,55.7172],[26.76872,55.67658],[26.71802,55.70645],[26.64888,55.70515],[26.63231,55.67968],[26.63167,55.57887],[26.55094,55.5093],[26.5522,55.40277],[26.44937,55.34832],[26.5709,55.32572],[26.6714,55.33902],[26.80929,55.31642],[26.83266,55.30444],[26.835,55.28182],[26.73017,55.24226],[26.72983,55.21788],[26.68075,55.19787],[26.69243,55.16718],[26.54753,55.14181],[26.51481,55.16051],[26.46249,55.12814],[26.35121,55.1525],[26.30628,55.12536],[26.23202,55.10439],[26.26941,55.08032],[26.20397,54.99729],[26.13386,54.98924],[26.05907,54.94631],[25.99129,54.95705],[25.89462,54.93438],[25.74122,54.80108],[25.75977,54.57252],[25.68045,54.5321],[25.64813,54.48704],[25.62203,54.4656],[25.63371,54.42075],[25.5376,54.33158],[25.55425,54.31591],[25.68513,54.31727],[25.78553,54.23327],[25.78563,54.15747],[25.71084,54.16704],[25.64875,54.1259],[25.54724,54.14925],[25.51452,54.17799],[25.56823,54.25212],[25.509,54.30267],[25.35559,54.26544],[25.22705,54.26271],[25.19199,54.219],[25.0728,54.13419],[24.991,54.14241],[24.96894,54.17589],[24.77131,54.11091],[24.85311,54.02862],[24.74279,53.96663],[24.69185,53.96543],[24.69652,54.01901],[24.62275,54.00217],[24.44411,53.90076],[24.34128,53.90076],[24.19638,53.96405],[23.98837,53.92554],[23.95098,53.9613],[23.81309,53.94205],[23.80543,53.89558],[23.71726,53.93379],[23.61677,53.92691],[23.51284,53.95052],[23.62004,53.60942],[23.81995,53.24131],[23.85657,53.22923],[23.91393,53.16469],[23.87548,53.0831],[23.92184,53.02079],[23.94689,52.95919],[23.91805,52.94016],[23.93763,52.71332],[23.73615,52.6149],[23.58296,52.59868],[23.45112,52.53774],[23.34141,52.44845],[23.18196,52.28812]]]}},{"type":"Feature","properties":{"id":"BZ"},"geometry":{"type":"Polygon","coordinates":[[[-89.22683,15.88619],[-89.17418,15.90898],[-89.02415,15.9063],[-88.95358,15.88698],[-88.40779,16.09624],[-87.3359,17.10872],[-87.24084,17.80373],[-87.84815,18.18511],[-87.85693,18.18266],[-87.86657,18.19971],[-87.87604,18.18313],[-87.90671,18.15213],[-88.03165,18.16657],[-88.03238,18.41778],[-88.26593,18.47617],[-88.29909,18.47591],[-88.3268,18.49048],[-88.48242,18.49164],[-88.71505,18.0707],[-88.8716,17.89535],[-89.03839,18.0067],[-89.15105,17.95104],[-89.14985,17.81563],[-89.15025,17.04813],[-89.22683,15.88619]]]}},{"type":"Feature","properties":{"id":"BO"},"geometry":{"type":"Polygon","coordinates":[[[-69.62883,-17.28142],[-69.46863,-17.37466],[-69.46897,-17.4988],[-69.46623,-17.60518],[-69.34126,-17.72753],[-69.28671,-17.94844],[-69.07496,-18.03715],[-69.14807,-18.16893],[-69.07432,-18.28259],[-68.94987,-18.93302],[-68.87082,-19.06003],[-68.80602,-19.08355],[-68.61989,-19.27584],[-68.41218,-19.40499],[-68.66761,-19.72118],[-68.54611,-19.84651],[-68.57132,-20.03134],[-68.74273,-20.08817],[-68.7276,-20.46178],[-68.44023,-20.62701],[-68.55383,-20.7355],[-68.53957,-20.91542],[-68.40403,-20.94562],[-68.18816,-21.28614],[-67.85114,-22.87076],[-67.54284,-22.89771],[-67.18382,-22.81525],[-66.7298,-22.23644],[-66.29714,-22.08741],[-66.24077,-21.77837],[-66.03836,-21.84829],[-66.04832,-21.9187],[-65.9261,-21.93335],[-65.7467,-22.10105],[-65.61166,-22.09504],[-65.58694,-22.09794],[-65.57743,-22.07675],[-65.47435,-22.08908],[-64.99524,-22.08255],[-64.90014,-22.12136],[-64.67174,-22.18957],[-64.58888,-22.25035],[-64.4176,-22.67692],[-64.35108,-22.73282],[-64.31489,-22.88824],[-64.22918,-22.55807],[-63.93287,-21.99934],[-63.70963,-21.99934],[-63.68113,-22.0544],[-63.66482,-21.99918],[-62.81124,-21.9987],[-62.8078,-22.12534],[-62.64455,-22.25091],[-62.2757,-21.06657],[-62.26883,-20.55311],[-61.93912,-20.10053],[-61.73723,-19.63958],[-60.00638,-19.2981],[-59.06965,-19.29148],[-58.23216,-19.80058],[-58.16225,-20.16193],[-57.8496,-19.98346],[-58.14215,-19.76276],[-57.78463,-19.03259],[-57.71113,-19.03161],[-57.69134,-19.00544],[-57.71995,-18.97546],[-57.71995,-18.89573],[-57.76764,-18.90087],[-57.56807,-18.25655],[-57.48237,-18.24219],[-57.69877,-17.8431],[-57.73949,-17.56095],[-57.90082,-17.44555],[-57.99661,-17.5273],[-58.32935,-17.28195],[-58.5058,-16.80958],[-58.30918,-16.3699],[-58.32431,-16.25861],[-58.41506,-16.32636],[-60.16069,-16.26479],[-60.23797,-15.50267],[-60.58224,-15.09887],[-60.23968,-15.09515],[-60.27887,-14.63021],[-60.46037,-14.22496],[-60.48053,-13.77981],[-61.05527,-13.50054],[-61.81151,-13.49564],[-63.76259,-12.42952],[-63.90248,-12.52544],[-64.22539,-12.45267],[-64.30708,-12.46398],[-64.99778,-11.98604],[-65.30027,-11.48749],[-65.28141,-10.86289],[-65.35402,-10.78685],[-65.37923,-10.35141],[-65.29019,-9.86253],[-65.40615,-9.63894],[-65.56244,-9.84266],[-65.68343,-9.75323],[-67.17784,-10.34016],[-68.71533,-11.14749],[-68.7651,-11.0496],[-68.75179,-11.03688],[-68.75265,-11.02383],[-68.74802,-11.00891],[-69.42792,-10.93451],[-69.47839,-10.95254],[-69.57156,-10.94555],[-68.98115,-11.8979],[-68.65044,-12.50689],[-68.85615,-12.87769],[-68.8864,-13.40792],[-69.05265,-13.68546],[-68.88135,-14.18639],[-69.36254,-14.94634],[-69.14856,-15.23478],[-69.40336,-15.61358],[-69.20291,-16.16668],[-69.09986,-16.22693],[-68.96238,-16.194],[-68.79464,-16.33272],[-68.98358,-16.42165],[-69.04027,-16.57214],[-69.00853,-16.66769],[-69.16896,-16.72233],[-69.62883,-17.28142]]]}},{"type":"Feature","properties":{"id":"BR"},"geometry":{"type":"Polygon","coordinates":[[[-73.96938,-7.58465],[-73.65485,-7.77897],[-73.76576,-7.89884],[-72.92886,-9.04074],[-73.21498,-9.40904],[-72.72216,-9.41397],[-72.31883,-9.5184],[-72.14742,-9.98049],[-71.23394,-9.9668],[-70.53373,-9.42628],[-70.58453,-9.58303],[-70.55429,-9.76692],[-70.62487,-9.80666],[-70.64134,-11.0108],[-70.51395,-10.92249],[-70.38791,-11.07096],[-69.90896,-10.92744],[-69.57835,-10.94051],[-69.57156,-10.94555],[-69.47839,-10.95254],[-69.42792,-10.93451],[-68.74802,-11.00891],[-68.75265,-11.02383],[-68.75179,-11.03688],[-68.7651,-11.0496],[-68.71533,-11.14749],[-67.17784,-10.34016],[-65.68343,-9.75323],[-65.56244,-9.84266],[-65.40615,-9.63894],[-65.29019,-9.86253],[-65.37923,-10.35141],[-65.35402,-10.78685],[-65.28141,-10.86289],[-65.30027,-11.48749],[-64.99778,-11.98604],[-64.30708,-12.46398],[-64.22539,-12.45267],[-63.90248,-12.52544],[-63.76259,-12.42952],[-61.81151,-13.49564],[-61.05527,-13.50054],[-60.48053,-13.77981],[-60.46037,-14.22496],[-60.27887,-14.63021],[-60.23968,-15.09515],[-60.58224,-15.09887],[-60.23797,-15.50267],[-60.16069,-16.26479],[-58.41506,-16.32636],[-58.32431,-16.25861],[-58.30918,-16.3699],[-58.5058,-16.80958],[-58.32935,-17.28195],[-57.99661,-17.5273],[-57.90082,-17.44555],[-57.73949,-17.56095],[-57.69877,-17.8431],[-57.48237,-18.24219],[-57.56807,-18.25655],[-57.76764,-18.90087],[-57.71995,-18.89573],[-57.71995,-18.97546],[-57.69134,-19.00544],[-57.71113,-19.03161],[-57.78463,-19.03259],[-58.14215,-19.76276],[-57.8496,-19.98346],[-58.16225,-20.16193],[-57.84536,-20.93155],[-57.93492,-21.65505],[-57.88239,-21.6868],[-57.94642,-21.73799],[-57.98625,-22.09157],[-56.6508,-22.28387],[-56.5212,-22.11556],[-56.45893,-22.08072],[-56.23206,-22.25347],[-55.8331,-22.29008],[-55.74941,-22.46436],[-55.741,-22.52018],[-55.72366,-22.5519],[-55.6986,-22.56268],[-55.68742,-22.58407],[-55.62493,-22.62765],[-55.63849,-22.95122],[-55.5446,-23.22811],[-55.52288,-23.2595],[-55.5555,-23.28237],[-55.43585,-23.87157],[-55.44117,-23.9185],[-55.41784,-23.9657],[-55.12292,-23.99669],[-55.0518,-23.98666],[-55.02691,-23.97317],[-54.6238,-23.83078],[-54.32807,-24.01865],[-54.28207,-24.07305],[-54.4423,-25.13381],[-54.62033,-25.46026],[-54.60196,-25.48397],[-54.59509,-25.53696],[-54.59398,-25.59224],[-54.5502,-25.58915],[-54.52926,-25.62846],[-53.90831,-25.55513],[-53.83691,-25.94849],[-53.73511,-26.04211],[-53.73086,-26.05842],[-53.7264,-26.0664],[-53.73391,-26.07006],[-53.73968,-26.10012],[-53.65018,-26.19501],[-53.65237,-26.23289],[-53.63739,-26.2496],[-53.63881,-26.25075],[-53.64632,-26.24798],[-53.64186,-26.25976],[-53.64505,-26.28089],[-53.68269,-26.33359],[-53.73372,-26.6131],[-53.80144,-27.09844],[-54.15978,-27.2889],[-54.19062,-27.27639],[-54.19268,-27.30751],[-54.41888,-27.40882],[-54.50416,-27.48232],[-54.67657,-27.57214],[-54.90159,-27.63132],[-54.90805,-27.73149],[-55.1349,-27.89759],[-55.16872,-27.86224],[-55.33303,-27.94661],[-55.6262,-28.17124],[-55.65418,-28.18304],[-56.01729,-28.51223],[-56.00458,-28.60421],[-56.05265,-28.62651],[-56.54171,-29.11447],[-56.57295,-29.11357],[-56.62789,-29.18073],[-56.81251,-29.48154],[-57.09386,-29.74211],[-57.65132,-30.19229],[-57.22502,-30.26121],[-56.90236,-30.02578],[-56.49267,-30.39471],[-56.4795,-30.3899],[-56.4619,-30.38457],[-55.87388,-31.05053],[-55.58866,-30.84117],[-55.5634,-30.8686],[-55.55373,-30.8732],[-55.55218,-30.88193],[-55.54572,-30.89051],[-55.53431,-30.89714],[-55.53276,-30.90218],[-55.52712,-30.89997],[-55.51862,-30.89828],[-55.50841,-30.9027],[-55.50821,-30.91349],[-54.17384,-31.86168],[-53.76024,-32.0751],[-53.39572,-32.58596],[-53.37671,-32.57005],[-53.1111,-32.71147],[-53.53459,-33.16843],[-53.52794,-33.68908],[-53.44031,-33.69344],[-53.39593,-33.75169],[-53.37138,-33.74313],[-53.18243,-33.86894],[-28.34015,-20.99094],[-28.99601,1.86593],[-51.35485,4.8383],[-51.63798,4.51394],[-51.61983,4.14596],[-51.79599,3.89336],[-51.82312,3.85825],[-51.85573,3.83427],[-52.31787,3.17896],[-52.6906,2.37298],[-52.96539,2.1881],[-53.78743,2.34412],[-54.16286,2.10779],[-54.6084,2.32856],[-55.01919,2.564],[-55.71493,2.40342],[-55.96292,2.53188],[-56.13054,2.27723],[-55.92159,2.05236],[-55.89863,1.89861],[-55.99278,1.83137],[-56.47045,1.95135],[-56.7659,1.89509],[-57.07092,1.95304],[-57.09109,2.01854],[-57.23981,1.95808],[-57.35073,1.98327],[-57.55743,1.69605],[-57.77281,1.73344],[-57.97336,1.64566],[-58.01873,1.51966],[-58.33887,1.58014],[-58.4858,1.48399],[-58.53571,1.29154],[-58.84229,1.17749],[-58.92072,1.31293],[-59.25583,1.40559],[-59.74066,1.87596],[-59.7264,2.27497],[-59.91177,2.36759],[-59.99733,2.92312],[-59.79769,3.37162],[-59.86899,3.57089],[-59.51963,3.91951],[-59.73353,4.20399],[-59.69361,4.34069],[-59.78878,4.45637],[-60.15953,4.53456],[-60.04189,4.69801],[-59.98129,5.07097],[-60.20944,5.28754],[-60.32352,5.21299],[-60.73204,5.20931],[-60.5802,4.94312],[-60.86539,4.70512],[-60.98303,4.54167],[-61.15703,4.49839],[-61.31457,4.54167],[-61.29675,4.44216],[-61.48569,4.43149],[-61.54629,4.2822],[-62.13094,4.08309],[-62.44822,4.18621],[-62.57656,4.04754],[-62.74411,4.03331],[-62.7655,3.73099],[-62.98296,3.59935],[-63.21111,3.96219],[-63.4464,3.9693],[-63.42233,3.89995],[-63.50611,3.83592],[-63.67099,4.01731],[-63.70218,3.91417],[-63.86082,3.94796],[-63.99183,3.90172],[-64.14512,4.12932],[-64.57648,4.12576],[-64.72977,4.28931],[-64.84028,4.24665],[-64.48379,3.7879],[-64.02908,2.79797],[-64.0257,2.48156],[-63.39114,2.4317],[-63.39827,2.16098],[-64.06135,1.94722],[-64.08274,1.64792],[-64.34654,1.35569],[-64.38932,1.5125],[-65.11657,1.12046],[-65.57288,0.62856],[-65.50158,0.92086],[-65.6727,1.01353],[-66.28507,0.74585],[-66.85795,1.22998],[-67.08222,1.17441],[-67.15784,1.80439],[-67.299,1.87494],[-67.40488,2.22258],[-67.9292,1.82455],[-68.18632,2.00091],[-68.26699,1.83463],[-68.18128,1.72881],[-69.38621,1.70865],[-69.53746,1.76408],[-69.83491,1.69353],[-69.82987,1.07864],[-69.26017,1.06856],[-69.14422,0.84172],[-69.20976,0.57958],[-69.47696,0.71065],[-70.04162,0.55437],[-70.03658,-0.19681],[-69.603,-0.51947],[-69.59796,-0.75136],[-69.4215,-1.01853],[-69.43395,-1.42219],[-69.94708,-4.2431],[-70.00888,-4.37833],[-70.11305,-4.27281],[-70.19582,-4.3607],[-70.33236,-4.15214],[-70.77601,-4.15717],[-70.96814,-4.36915],[-71.87003,-4.51661],[-72.64391,-5.0391],[-72.83973,-5.14765],[-73.24579,-6.05764],[-73.12983,-6.43852],[-73.73986,-6.87919],[-73.77011,-7.28944],[-73.96938,-7.58465]]]}},{"type":"Feature","properties":{"id":"BB"},"geometry":{"type":"Polygon","coordinates":[[[-59.92255,13.58015],[-59.88892,12.77667],[-59.14146,12.80638],[-59.17509,13.60976],[-59.92255,13.58015]]]}},{"type":"Feature","properties":{"id":"BN"},"geometry":{"type":"Polygon","coordinates":[[[114.07448,4.58441],[114.15813,4.57],[114.26876,4.49878],[114.32176,4.34942],[114.32176,4.2552],[114.4416,4.27588],[114.49922,4.13108],[114.64211,4.00694],[114.78539,4.12205],[114.88039,4.4257],[114.83189,4.42387],[114.77303,4.72871],[114.8266,4.75062],[114.88841,4.81905],[114.96982,4.81146],[114.99417,4.88201],[115.05038,4.90275],[115.02955,4.82087],[115.02278,4.74137],[115.04064,4.63706],[115.07737,4.53418],[115.09978,4.39123],[115.31275,4.30806],[115.36346,4.33563],[115.2851,4.42295],[115.27819,4.63661],[115.20737,4.8256],[115.15092,4.87604],[115.16236,5.01011],[115.02521,5.35005],[114.08532,4.64632],[114.07448,4.58441]]]}},{"type":"Feature","properties":{"id":"BT"},"geometry":{"type":"Polygon","coordinates":[[[88.74219,27.144],[88.86984,27.10937],[88.8714,26.97488],[88.92301,26.99286],[88.95807,26.92668],[89.09554,26.89089],[89.12825,26.81661],[89.1926,26.81329],[89.37913,26.86224],[89.38319,26.85963],[89.3901,26.84225],[89.42349,26.83727],[89.63369,26.74402],[89.86124,26.73307],[90.04535,26.72422],[90.30402,26.85098],[90.39271,26.90704],[90.48504,26.8594],[90.67715,26.77215],[91.50067,26.79223],[91.83181,26.87318],[92.05523,26.8692],[92.11863,26.893],[92.03457,27.07334],[92.04702,27.26861],[92.12019,27.27829],[92.01132,27.47352],[91.65007,27.48287],[91.55819,27.6144],[91.6469,27.76358],[91.5629,27.84823],[91.48973,27.93903],[91.46327,28.0064],[91.25779,28.07509],[91.20019,27.98715],[90.69894,28.07784],[90.58842,28.02838],[90.13387,28.19178],[89.79762,28.23979],[89.59525,28.16433],[89.12825,27.62502],[89.0582,27.60985],[88.97213,27.51671],[88.95355,27.4106],[89.00216,27.32532],[88.96947,27.30319],[88.93678,27.33777],[88.91901,27.32483],[88.74219,27.144]]]}},{"type":"Feature","properties":{"id":"BW"},"geometry":{"type":"Polygon","coordinates":[[[19.99817,-24.76768],[20.02809,-24.78725],[20.03678,-24.81004],[20.29826,-24.94869],[20.64795,-25.47827],[20.86081,-26.14892],[20.61754,-26.4692],[20.63275,-26.78181],[20.68596,-26.9039],[20.87031,-26.80047],[21.13353,-26.86661],[21.37869,-26.82083],[21.69322,-26.86152],[21.7854,-26.79199],[21.77114,-26.69015],[21.83291,-26.65959],[21.90703,-26.66808],[22.06192,-26.61882],[22.21206,-26.3773],[22.41921,-26.23078],[22.56365,-26.19668],[22.70808,-25.99186],[22.86012,-25.50572],[23.03497,-25.29971],[23.47588,-25.29971],[23.9244,-25.64286],[24.18287,-25.62916],[24.36531,-25.773],[24.44703,-25.73021],[24.67319,-25.81749],[24.8946,-25.80723],[25.01718,-25.72507],[25.12266,-25.75931],[25.33076,-25.76616],[25.58543,-25.6343],[25.6643,-25.4491],[25.69661,-25.29284],[25.72702,-25.25503],[25.88571,-24.87802],[25.84295,-24.78661],[25.8515,-24.75727],[26.39409,-24.63468],[26.46346,-24.60358],[26.51667,-24.47219],[26.84165,-24.24885],[26.99749,-23.65486],[27.33768,-23.40917],[27.52393,-23.37952],[27.6066,-23.21894],[27.74154,-23.2137],[27.93539,-23.04941],[27.93729,-22.96194],[28.04752,-22.90243],[28.04562,-22.8394],[28.34874,-22.5694],[28.63287,-22.55887],[28.91889,-22.44299],[29.0151,-22.22907],[29.10881,-22.21202],[29.15268,-22.21399],[29.18974,-22.18599],[29.21955,-22.17771],[29.37703,-22.19581],[29.3533,-22.18363],[29.24648,-22.05967],[29.1974,-22.07472],[29.14501,-22.07275],[29.08495,-22.04867],[29.04108,-22.00563],[29.02191,-21.95665],[29.02191,-21.90647],[29.04023,-21.85864],[29.07763,-21.81877],[28.58114,-21.63455],[28.49942,-21.66634],[28.29416,-21.59037],[28.01669,-21.57624],[27.91407,-21.31621],[27.69171,-21.08409],[27.72972,-20.51735],[27.69361,-20.48531],[27.28865,-20.49873],[27.29831,-20.28935],[27.21278,-20.08244],[26.72246,-19.92707],[26.17227,-19.53709],[25.96226,-19.08152],[25.99837,-19.02943],[25.94326,-18.90362],[25.82353,-18.82808],[25.79217,-18.6355],[25.68859,-18.56165],[25.53465,-18.39041],[25.39972,-18.12691],[25.31799,-18.07091],[25.23909,-17.90832],[25.26433,-17.79571],[25.16882,-17.78253],[25.05895,-17.84452],[24.95586,-17.79674],[24.73364,-17.89338],[24.71887,-17.9218],[24.6303,-17.9863],[24.57485,-18.07151],[24.40577,-17.95726],[24.19416,-18.01919],[23.61088,-18.4881],[23.29618,-17.99855],[23.0996,-18.00075],[21.45556,-18.31795],[20.99904,-18.31743],[20.99751,-22.00026],[19.99912,-21.99991],[19.99817,-24.76768]]]}},{"type":"Feature","properties":{"id":"CF"},"geometry":{"type":"Polygon","coordinates":[[[14.42917,6.00508],[14.49455,5.91683],[14.60974,5.91838],[14.62375,5.70466],[14.58951,5.59777],[14.62531,5.51411],[14.52724,5.28319],[14.57083,5.23979],[14.65489,5.21343],[14.73383,4.6135],[15.00825,4.41458],[15.08609,4.30282],[15.10644,4.1362],[15.17482,4.05131],[15.07686,4.01805],[15.73522,3.24348],[15.77725,3.26835],[16.05449,3.02306],[16.08252,2.45708],[16.19357,2.21537],[16.50126,2.84739],[16.46701,2.92512],[16.57598,3.47999],[16.68283,3.54257],[17.01746,3.55136],[17.35649,3.63045],[17.46876,3.70515],[17.60966,3.63705],[17.83421,3.61068],[17.85842,3.53378],[18.05656,3.56893],[18.14902,3.54476],[18.17323,3.47665],[18.24148,3.50302],[18.2723,3.57992],[18.39558,3.58212],[18.49245,3.63924],[18.58711,3.49423],[18.62755,3.47564],[20.60184,4.42394],[20.90383,4.44877],[21.08793,4.39603],[21.11214,4.33895],[21.21341,4.29285],[21.25744,4.33676],[21.55904,4.25553],[21.6405,4.317],[22.10721,4.20723],[22.27682,4.11347],[22.45504,4.13039],[22.5431,4.22041],[22.60915,4.48821],[22.6928,4.47285],[22.78526,4.71423],[22.84691,4.69887],[22.89094,4.79321],[22.94817,4.82392],[23.38847,4.60013],[24.46719,5.0915],[24.71816,4.90509],[25.31256,5.03668],[25.34558,5.29101],[25.53271,5.37431],[25.86073,5.19455],[26.13371,5.25594],[26.48595,5.04984],[26.74572,5.10685],[26.85579,5.03887],[26.93064,5.13535],[27.09575,5.22305],[27.44012,5.07349],[27.26886,5.25876],[27.23017,5.37167],[27.28621,5.56382],[27.22705,5.62889],[27.22705,5.71254],[26.51721,6.09655],[26.58259,6.1987],[26.32729,6.36272],[26.38022,6.63493],[25.90076,7.09549],[25.37461,7.33024],[25.35281,7.42595],[25.20337,7.50312],[25.20649,7.61115],[25.29214,7.66675],[25.25319,7.8487],[24.98855,7.96588],[24.85156,8.16933],[24.35965,8.26177],[24.13238,8.36959],[24.25691,8.69288],[23.51905,8.71749],[23.59065,8.99743],[23.44744,8.99128],[23.4848,9.16959],[23.56263,9.19418],[23.64358,9.28637],[23.64981,9.44303],[23.62179,9.53823],[23.69155,9.67566],[23.67164,9.86923],[23.3128,10.45214],[23.02221,10.69235],[22.87758,10.91915],[22.45889,11.00246],[21.72139,10.64136],[21.71479,10.29932],[21.63553,10.217],[21.52766,10.2105],[21.34934,9.95907],[21.26348,9.97642],[20.82979,9.44696],[20.36748,9.11019],[19.06421,9.00367],[18.86388,8.87971],[19.11044,8.68172],[18.79783,8.25929],[18.67455,8.22226],[18.62612,8.14163],[18.64153,8.08714],[18.6085,8.05009],[18.02731,8.01085],[17.93926,7.95853],[17.67288,7.98905],[16.8143,7.53971],[16.6668,7.67281],[16.658,7.75353],[16.59415,7.76444],[16.58315,7.88657],[16.41583,7.77971],[16.40703,7.68809],[15.79942,7.44149],[15.73118,7.52006],[15.49743,7.52179],[15.23397,7.25135],[15.04717,6.77085],[14.96311,6.75693],[14.79966,6.39043],[14.80122,6.34866],[14.74206,6.26356],[14.56149,6.18928],[14.43073,6.08867],[14.42917,6.00508]]]}},{"type":"Feature","properties":{"id":"CA"},"geometry":{"type":"Polygon","coordinates":[[[-141.00555,72.20369],[-141.00116,60.30648],[-140.5227,60.22077],[-140.45648,60.30919],[-139.98024,60.18027],[-139.68991,60.33693],[-139.05831,60.35205],[-139.20603,60.08896],[-139.05365,59.99655],[-138.71149,59.90728],[-138.62145,59.76431],[-137.60623,59.24465],[-137.4925,58.89415],[-136.82619,59.16198],[-136.52365,59.16752],[-136.47323,59.46617],[-136.33727,59.44466],[-136.22381,59.55526],[-136.31566,59.59083],[-135.48007,59.79937],[-135.03069,59.56208],[-135.00267,59.28745],[-134.7047,59.2458],[-134.55699,59.1297],[-134.48059,59.13231],[-134.27175,58.8634],[-133.84645,58.73543],[-133.38523,58.42773],[-131.8271,56.62247],[-130.77769,56.36185],[-130.33965,56.10849],[-130.10173,56.12178],[-130.00093,56.00325],[-130.00857,55.91344],[-130.15373,55.74895],[-129.97513,55.28029],[-130.08035,55.21556],[-130.18765,55.07744],[-130.27203,54.97174],[-130.44184,54.85377],[-130.64499,54.76912],[-130.61931,54.70835],[-133.92876,54.62289],[-133.36909,48.51151],[-125.03842,48.53282],[-123.50039,48.21223],[-123.15614,48.35395],[-123.26565,48.6959],[-123.0093,48.76586],[-123.0093,48.83186],[-123.32163,49.00419],[-117.03266,49.00056],[-116.04938,48.99999],[-114.0683,48.99885],[-110.0051,48.99901],[-104.05004,48.99925],[-101.36198,48.99935],[-97.24024,48.99952],[-95.15355,48.9996],[-95.15357,49.384],[-95.12903,49.37056],[-95.05825,49.35311],[-95.01419,49.35647],[-94.99532,49.36579],[-94.95681,49.37035],[-94.85381,49.32492],[-94.8159,49.32299],[-94.82487,49.29483],[-94.77355,49.11998],[-94.75017,49.09931],[-94.687,48.84077],[-94.70087,48.8339],[-94.70486,48.82365],[-94.69669,48.80918],[-94.69335,48.77883],[-94.58903,48.71803],[-94.54885,48.71543],[-94.53826,48.70216],[-94.44258,48.69223],[-94.4174,48.71049],[-94.27153,48.70232],[-94.25172,48.68404],[-94.25104,48.65729],[-94.23215,48.65202],[-93.85769,48.63284],[-93.83288,48.62745],[-93.80676,48.58232],[-93.80939,48.52439],[-93.79267,48.51631],[-93.66382,48.51845],[-93.47022,48.54357],[-93.44472,48.59147],[-93.40693,48.60948],[-93.39758,48.60364],[-93.3712,48.60599],[-93.33946,48.62787],[-93.25391,48.64266],[-92.94973,48.60866],[-92.7287,48.54005],[-92.6342,48.54133],[-92.62747,48.50278],[-92.69927,48.49573],[-92.71323,48.46081],[-92.65606,48.43471],[-92.50712,48.44921],[-92.45588,48.40624],[-92.48147,48.36609],[-92.37185,48.22259],[-92.27167,48.25046],[-92.30939,48.31251],[-92.26662,48.35651],[-92.202,48.35252],[-92.14732,48.36578],[-92.05339,48.35958],[-91.98929,48.25409],[-91.86125,48.21278],[-91.71231,48.19875],[-91.70451,48.11805],[-91.55649,48.10611],[-91.58025,48.04339],[-91.45829,48.07454],[-91.43248,48.04912],[-91.25025,48.08522],[-91.08016,48.18096],[-90.87588,48.2484],[-90.75045,48.09143],[-90.56444,48.12184],[-90.56312,48.09488],[-90.07418,48.11043],[-89.89974,47.98109],[-89.77248,48.02607],[-89.57972,48.00023],[-89.48837,48.01412],[-88.37033,48.30586],[-84.85871,46.88881],[-84.55635,46.45974],[-84.47607,46.45225],[-84.4481,46.48972],[-84.42101,46.49853],[-84.34174,46.50683],[-84.29893,46.49127],[-84.26351,46.49508],[-84.2264,46.53337],[-84.1945,46.54061],[-84.17723,46.52753],[-84.12885,46.53068],[-84.11196,46.50248],[-84.13451,46.39218],[-84.11254,46.32329],[-84.11615,46.2681],[-84.09756,46.25512],[-84.1096,46.23987],[-83.95399,46.05634],[-83.90453,46.05922],[-83.83329,46.12169],[-83.57017,46.105],[-83.43746,45.99749],[-83.59589,45.82131],[-82.48419,45.30225],[-82.42469,42.992],[-82.4146,42.97626],[-82.4253,42.95423],[-82.45331,42.93139],[-82.4826,42.8068],[-82.46613,42.76615],[-82.51063,42.66025],[-82.51858,42.611],[-82.57583,42.5718],[-82.58873,42.54984],[-82.64242,42.55594],[-82.82964,42.37355],[-83.02253,42.33045],[-83.07837,42.30978],[-83.09837,42.28877],[-83.12724,42.2376],[-83.14962,42.04089],[-83.11184,41.95671],[-82.67862,41.67615],[-80.53581,42.29896],[-79.77073,42.55308],[-78.93684,42.82887],[-78.90712,42.89733],[-78.90905,42.93022],[-78.93224,42.95229],[-78.96312,42.95509],[-78.98126,42.97],[-79.02074,42.98444],[-79.02424,43.01983],[-78.99941,43.05612],[-79.01055,43.06659],[-79.07486,43.07845],[-79.05671,43.10937],[-79.06881,43.12029],[-79.0427,43.13934],[-79.04652,43.16396],[-79.05384,43.17418],[-79.05002,43.20133],[-79.05544,43.21224],[-79.05512,43.25375],[-79.06921,43.26183],[-79.25796,43.54052],[-76.79706,43.63099],[-76.43859,44.09393],[-76.35324,44.13493],[-76.31222,44.19894],[-76.244,44.19643],[-76.1664,44.23051],[-76.16285,44.28262],[-76.00018,44.34896],[-75.95947,44.34463],[-75.8217,44.43176],[-75.76813,44.51537],[-75.41441,44.76614],[-75.2193,44.87821],[-75.01363,44.95608],[-74.99101,44.98051],[-74.8447,45.00606],[-74.66689,45.00646],[-74.32699,44.99029],[-73.35025,45.00942],[-71.50067,45.01357],[-71.48735,45.07784],[-71.42778,45.12624],[-71.40364,45.21382],[-71.44252,45.2361],[-71.37133,45.24624],[-71.29371,45.29996],[-71.22338,45.25184],[-71.19723,45.25438],[-71.14568,45.24128],[-71.08364,45.30623],[-71.01866,45.31573],[-71.0107,45.34819],[-70.95193,45.33895],[-70.91169,45.29849],[-70.89864,45.2398],[-70.84816,45.22698],[-70.80236,45.37444],[-70.82638,45.39828],[-70.78372,45.43269],[-70.65383,45.37592],[-70.62518,45.42286],[-70.72651,45.49771],[-70.68516,45.56964],[-70.54019,45.67291],[-70.38934,45.73215],[-70.41523,45.79497],[-70.25976,45.89675],[-70.24694,45.95138],[-70.31025,45.96424],[-70.23855,46.1453],[-70.29078,46.18832],[-70.18547,46.35357],[-70.05812,46.41768],[-69.99966,46.69543],[-69.22119,47.46461],[-69.05148,47.42012],[-69.05073,47.30076],[-69.05039,47.2456],[-68.89222,47.1807],[-68.70125,47.24399],[-68.60575,47.24659],[-68.57914,47.28431],[-68.38332,47.28723],[-68.37458,47.35851],[-68.23244,47.35712],[-67.94843,47.1925],[-67.87993,47.10377],[-67.78578,47.06473],[-67.78111,45.9392],[-67.75196,45.91814],[-67.80961,45.87531],[-67.75654,45.82324],[-67.80653,45.80022],[-67.80705,45.69528],[-67.6049,45.60725],[-67.43815,45.59162],[-67.42144,45.50584],[-67.50578,45.48971],[-67.42394,45.37969],[-67.48201,45.27351],[-67.34927,45.122],[-67.29754,45.14865],[-67.29748,45.18173],[-67.27039,45.1934],[-67.22751,45.16344],[-67.20349,45.1722],[-67.19603,45.16771],[-67.15965,45.16179],[-67.11316,45.11176],[-67.0216,44.95333],[-66.96824,44.90965],[-66.98249,44.87071],[-66.96824,44.83078],[-66.93432,44.82597],[-67.16117,44.20069],[-65.81187,42.91911],[-59.437,43.71683],[-57.60106,47.38123],[-56.67989,47.3339],[-56.25228,47.31192],[-55.8643,46.64935],[-51.16966,45.93987],[-45.64471,55.43944],[-53.68108,62.9266],[-74.12379,75.70014],[-73.91222,78.42484],[-67.48417,80.75493],[-63.1988,81.66522],[-59.93819,82.31398],[-62.36036,83.40597],[-85.36473,83.41631],[-110.08928,79.74266],[-141.00555,72.20369]]]}},{"type":"Feature","properties":{"id":"CN"},"geometry":{"type":"Polygon","coordinates":[[[73.5004,39.38402],[73.55396,39.3543],[73.54572,39.27567],[73.60638,39.24534],[73.75823,39.023],[73.81728,39.04007],[73.82964,38.91517],[73.7445,38.93867],[73.7033,38.84782],[73.80656,38.66449],[73.79806,38.61106],[73.97933,38.52945],[74.17022,38.65504],[74.51217,38.47034],[74.69619,38.42947],[74.69894,38.22155],[74.80331,38.19889],[74.82665,38.07359],[74.9063,38.03033],[74.92416,37.83428],[75.00935,37.77486],[74.8912,37.67576],[74.94338,37.55501],[75.06011,37.52779],[75.15899,37.41443],[75.09719,37.37297],[75.12328,37.31839],[74.88887,37.23275],[74.80605,37.21565],[74.49981,37.24518],[74.56453,37.03023],[75.13839,37.02622],[75.40481,36.95382],[75.45562,36.71971],[75.72737,36.7529],[75.92391,36.56986],[76.0324,36.41198],[76.00906,36.17511],[75.93028,36.13136],[76.15325,35.9264],[76.14913,35.82848],[76.33453,35.84296],[76.50961,35.8908],[76.77323,35.66062],[76.84539,35.67356],[76.96624,35.5932],[77.44277,35.46132],[77.70232,35.46244],[77.80532,35.52058],[78.11664,35.48022],[78.03466,35.3785],[78.00033,35.23954],[78.22692,34.88771],[78.18435,34.7998],[78.27781,34.61484],[78.54964,34.57283],[78.56475,34.50835],[78.74465,34.45174],[79.05364,34.32482],[78.99802,34.3027],[78.91769,34.15452],[78.66225,34.08858],[78.65657,34.03195],[78.73367,34.01121],[78.77349,33.73871],[78.67599,33.66445],[78.73636,33.56521],[79.15252,33.17156],[79.14016,33.02545],[79.46562,32.69668],[79.26768,32.53277],[79.13174,32.47766],[79.0979,32.38051],[78.99322,32.37948],[78.96713,32.33655],[78.7831,32.46873],[78.73916,32.69438],[78.38897,32.53938],[78.4645,32.45367],[78.49609,32.2762],[78.68754,32.10256],[78.74404,32.00384],[78.78036,31.99478],[78.69933,31.78723],[78.84516,31.60631],[78.71032,31.50197],[78.77898,31.31209],[79.01931,31.42817],[79.14016,31.43403],[79.22805,31.34963],[79.59884,30.93943],[79.93255,30.88288],[80.20721,30.58541],[80.54504,30.44936],[80.83343,30.32023],[81.03953,30.20059],[81.12842,30.01395],[81.24362,30.0126],[81.29032,30.08806],[81.2623,30.14596],[81.33355,30.15303],[81.39928,30.21862],[81.41018,30.42153],[81.62033,30.44703],[81.99082,30.33423],[82.10135,30.35439],[82.10757,30.23745],[82.19475,30.16884],[82.16984,30.0692],[82.38622,30.02608],[82.5341,29.9735],[82.73024,29.81695],[83.07116,29.61957],[83.28131,29.56813],[83.44787,29.30513],[83.63156,29.16249],[83.82303,29.30513],[83.97559,29.33091],[84.18107,29.23451],[84.24801,29.02783],[84.2231,28.89571],[84.47528,28.74023],[84.62317,28.73887],[84.85511,28.58041],[85.06059,28.68562],[85.19135,28.62825],[85.18668,28.54076],[85.10729,28.34092],[85.38127,28.28336],[85.4233,28.32996],[85.59765,28.30529],[85.60854,28.25045],[85.69105,28.38475],[85.71907,28.38064],[85.74864,28.23126],[85.84672,28.18187],[85.90743,28.05144],[85.97813,27.99023],[85.94946,27.9401],[86.06309,27.90021],[86.12069,27.93047],[86.08333,28.02121],[86.088,28.09264],[86.18607,28.17364],[86.22966,27.9786],[86.42736,27.91122],[86.51609,27.96623],[86.56265,28.09569],[86.74181,28.10638],[86.75582,28.04182],[87.03757,27.94835],[87.11696,27.84104],[87.56996,27.84517],[87.72718,27.80938],[87.82681,27.95248],[88.13378,27.88015],[88.1278,27.95417],[88.25332,27.9478],[88.54858,28.06057],[88.63235,28.12356],[88.83559,28.01936],[88.88091,27.85192],[88.77517,27.45415],[88.82981,27.38814],[88.91901,27.32483],[88.93678,27.33777],[88.96947,27.30319],[89.00216,27.32532],[88.95355,27.4106],[88.97213,27.51671],[89.0582,27.60985],[89.12825,27.62502],[89.59525,28.16433],[89.79762,28.23979],[90.13387,28.19178],[90.58842,28.02838],[90.69894,28.07784],[91.20019,27.98715],[91.25779,28.07509],[91.46327,28.0064],[91.48973,27.93903],[91.5629,27.84823],[91.6469,27.76358],[91.84722,27.76325],[91.87057,27.7195],[92.27432,27.89077],[92.32101,27.79363],[92.42538,27.80092],[92.7275,27.98662],[92.73025,28.05814],[92.65472,28.07632],[92.67486,28.15018],[92.93075,28.25671],[93.14635,28.37035],[93.18069,28.50319],[93.44621,28.67189],[93.72797,28.68821],[94.35897,29.01965],[94.2752,29.11687],[94.69318,29.31739],[94.81353,29.17804],[95.0978,29.14446],[95.11291,29.09527],[95.2214,29.10727],[95.26122,29.07727],[95.3038,29.13847],[95.41091,29.13007],[95.50842,29.13487],[95.72086,29.20797],[95.75149,29.32063],[95.84899,29.31464],[96.05361,29.38167],[96.31316,29.18643],[96.18682,29.11087],[96.20467,29.02325],[96.3626,29.10607],[96.61391,28.72742],[96.40929,28.51526],[96.48895,28.42955],[96.6455,28.61657],[96.85561,28.4875],[96.88445,28.39452],[96.98882,28.32564],[97.1289,28.3619],[97.34547,28.21385],[97.41729,28.29783],[97.47085,28.2688],[97.50518,28.49716],[97.56835,28.55628],[97.70705,28.5056],[97.79632,28.33168],[97.90069,28.3776],[98.15337,28.12114],[98.13964,27.9478],[98.32641,27.51385],[98.42529,27.55404],[98.43353,27.67086],[98.69582,27.56499],[98.7333,26.85615],[98.77547,26.61994],[98.72741,26.36183],[98.67797,26.24487],[98.7329,26.17218],[98.66884,26.09165],[98.63128,26.15492],[98.57085,26.11547],[98.60763,26.01512],[98.70818,25.86241],[98.63128,25.79937],[98.54064,25.85129],[98.40606,25.61129],[98.31268,25.55307],[98.25774,25.6051],[98.16848,25.62739],[98.18084,25.56298],[98.12591,25.50722],[98.14925,25.41547],[97.92541,25.20815],[97.83614,25.2715],[97.77023,25.11492],[97.72216,25.08508],[97.72903,24.91332],[97.79949,24.85655],[97.76481,24.8289],[97.73127,24.83015],[97.70181,24.84557],[97.64354,24.79171],[97.56648,24.76475],[97.56383,24.75535],[97.5542,24.74943],[97.54675,24.74202],[97.56525,24.72838],[97.56286,24.54535],[97.52757,24.43748],[97.60029,24.4401],[97.66998,24.45288],[97.7098,24.35658],[97.65624,24.33781],[97.66723,24.30027],[97.71941,24.29652],[97.76799,24.26365],[97.72998,24.2302],[97.72799,24.18883],[97.75305,24.16902],[97.72903,24.12606],[97.62363,24.00506],[97.5247,23.94032],[97.64667,23.84574],[97.72302,23.89288],[97.79456,23.94836],[97.79416,23.95663],[97.84328,23.97603],[97.86545,23.97723],[97.88811,23.97446],[97.8955,23.97758],[97.89676,23.97931],[97.89683,23.98389],[97.88814,23.98605],[97.88414,23.99405],[97.88616,24.00463],[97.90998,24.02094],[97.93951,24.01953],[97.98691,24.03897],[97.99583,24.04932],[98.04709,24.07616],[98.05302,24.07408],[98.05671,24.07961],[98.0607,24.07812],[98.06703,24.08028],[98.07806,24.07988],[98.20666,24.11406],[98.54476,24.13119],[98.59256,24.08371],[98.85319,24.13042],[98.87998,24.15624],[98.89632,24.10612],[98.67797,23.9644],[98.68209,23.80492],[98.79607,23.77947],[98.82933,23.72921],[98.81775,23.694],[98.88396,23.59555],[98.80294,23.5345],[98.82877,23.47908],[98.87683,23.48995],[98.92104,23.36946],[98.87573,23.33038],[98.93958,23.31414],[98.92515,23.29535],[98.88597,23.18656],[99.05975,23.16382],[99.04601,23.12215],[99.25741,23.09025],[99.34127,23.13099],[99.52214,23.08218],[99.54218,22.90014],[99.43537,22.94086],[99.45654,22.85726],[99.31243,22.73893],[99.38247,22.57544],[99.37972,22.50188],[99.28771,22.4105],[99.17318,22.18025],[99.19176,22.16983],[99.1552,22.15874],[99.33166,22.09656],[99.47585,22.13345],[99.85351,22.04183],[99.96612,22.05965],[99.99084,21.97053],[99.94003,21.82782],[99.98654,21.71064],[100.04956,21.66843],[100.12679,21.70539],[100.17486,21.65306],[100.10757,21.59945],[100.12542,21.50365],[100.1625,21.48704],[100.18447,21.51898],[100.25863,21.47043],[100.35201,21.53176],[100.42892,21.54325],[100.4811,21.46148],[100.57861,21.45637],[100.72143,21.51898],[100.87265,21.67396],[101.11744,21.77659],[101.15156,21.56129],[101.2124,21.56422],[101.19349,21.41959],[101.26912,21.36482],[101.2229,21.23271],[101.29326,21.17254],[101.54563,21.25668],[101.6068,21.23329],[101.59491,21.18621],[101.60886,21.17947],[101.66977,21.20004],[101.70548,21.14911],[101.7622,21.14813],[101.79266,21.19025],[101.76745,21.21571],[101.83887,21.20983],[101.84412,21.25291],[101.74014,21.30967],[101.74224,21.48276],[101.7727,21.51794],[101.7475,21.5873],[101.80001,21.57461],[101.83257,21.61562],[101.74555,21.72852],[101.7791,21.83019],[101.62566,21.96574],[101.57525,22.13026],[101.60675,22.13513],[101.53638,22.24794],[101.56789,22.28876],[101.61306,22.27515],[101.68973,22.46843],[101.7685,22.50337],[101.86828,22.38397],[101.90714,22.38688],[101.91344,22.44417],[101.98487,22.42766],[102.03633,22.46164],[102.1245,22.43372],[102.14099,22.40092],[102.16621,22.43336],[102.26428,22.41321],[102.25339,22.4607],[102.41061,22.64184],[102.38415,22.67919],[102.42618,22.69212],[102.46665,22.77108],[102.51802,22.77969],[102.57095,22.7036],[102.60675,22.73376],[102.8636,22.60735],[102.9321,22.48659],[103.0722,22.44775],[103.07843,22.50097],[103.17961,22.55705],[103.15782,22.59873],[103.18895,22.64471],[103.28079,22.68063],[103.32282,22.8127],[103.43179,22.75816],[103.43646,22.70648],[103.52675,22.59155],[103.57812,22.65764],[103.56255,22.69499],[103.64506,22.79979],[103.87904,22.56683],[103.93286,22.52703],[103.94513,22.52553],[103.95191,22.5134],[103.96352,22.50584],[103.96783,22.51173],[103.97384,22.50634],[103.99247,22.51958],[104.01088,22.51823],[104.03734,22.72945],[104.11384,22.80363],[104.27084,22.8457],[104.25683,22.76534],[104.35593,22.69353],[104.47225,22.75813],[104.58122,22.85571],[104.60457,22.81841],[104.65283,22.83419],[104.72755,22.81984],[104.77114,22.90017],[104.84942,22.93631],[104.86765,22.95178],[104.8334,23.01484],[104.79478,23.12934],[104.87382,23.12854],[104.87992,23.17141],[104.91435,23.18666],[104.9486,23.17235],[104.96532,23.20463],[104.98712,23.19176],[105.07002,23.26248],[105.11672,23.25247],[105.17276,23.28679],[105.22569,23.27249],[105.32376,23.39684],[105.40782,23.28107],[105.42805,23.30824],[105.49966,23.20669],[105.56037,23.16806],[105.57594,23.075],[105.72382,23.06641],[105.8726,22.92756],[105.90119,22.94168],[105.99568,22.94178],[106.00179,22.99049],[106.19705,22.98475],[106.27022,22.87722],[106.34961,22.86718],[106.49749,22.91164],[106.51306,22.94891],[106.55976,22.92311],[106.60179,22.92884],[106.6516,22.86862],[106.6734,22.89587],[106.71387,22.88296],[106.71128,22.85982],[106.78422,22.81532],[106.81271,22.8226],[106.83685,22.8098],[106.82404,22.7881],[106.76293,22.73491],[106.72321,22.63606],[106.71698,22.58432],[106.65316,22.5757],[106.61269,22.60301],[106.58395,22.474],[106.55665,22.46498],[106.57221,22.37],[106.55976,22.34841],[106.6516,22.33977],[106.69986,22.22309],[106.67495,22.1885],[106.6983,22.15102],[106.70142,22.02409],[106.68274,21.99811],[106.69276,21.96013],[106.72551,21.97923],[106.74345,22.00965],[106.81038,21.97934],[106.9178,21.97357],[106.92714,21.93459],[106.97228,21.92592],[106.99252,21.95191],[107.05634,21.92303],[107.06101,21.88982],[107.00964,21.85948],[107.02615,21.81981],[107.10771,21.79879],[107.20734,21.71493],[107.24625,21.7077],[107.29296,21.74674],[107.35834,21.6672],[107.35989,21.60063],[107.38636,21.59774],[107.41593,21.64839],[107.47197,21.6672],[107.49532,21.62958],[107.49065,21.59774],[107.54047,21.5934],[107.56537,21.61945],[107.66967,21.60787],[107.80355,21.66141],[107.86114,21.65128],[107.90006,21.5905],[107.92652,21.58906],[107.95232,21.5388],[107.96774,21.53601],[107.97074,21.54072],[107.97383,21.53961],[107.97932,21.54503],[108.02926,21.54997],[108.0569,21.53604],[108.10003,21.47338],[108.26073,20.07614],[107.44022,18.66249],[110.2534,15.19951],[112.88221,15.61902],[117.76968,23.10828],[118.41371,24.06775],[118.179,24.33015],[118.09488,24.38193],[118.28244,24.51231],[118.35291,24.51645],[118.42453,24.54644],[118.56434,24.49266],[120.49232,25.22863],[121.03532,26.8787],[123.5458,31.01942],[122.29378,31.76513],[122.80525,33.30571],[123.85601,37.49093],[123.90497,38.79949],[124.17532,39.8232],[124.23201,39.9248],[124.35029,39.95639],[124.37089,40.03004],[124.3322,40.05573],[124.38556,40.11047],[124.40719,40.13655],[124.86913,40.45387],[125.71172,40.85223],[125.76869,40.87908],[126.00335,40.92835],[126.242,41.15454],[126.53189,41.35206],[126.60631,41.65565],[126.90729,41.79955],[127.17841,41.59714],[127.29712,41.49473],[127.92943,41.44291],[128.02633,41.42103],[128.03311,41.39232],[128.12967,41.37931],[128.18546,41.41279],[128.20061,41.40895],[128.30716,41.60322],[128.15119,41.74568],[128.04487,42.01769],[128.94007,42.03537],[128.96068,42.06657],[129.15178,42.17224],[129.22285,42.26491],[129.22423,42.3553],[129.28541,42.41574],[129.42882,42.44702],[129.54701,42.37254],[129.60482,42.44461],[129.72541,42.43739],[129.75294,42.59409],[129.77183,42.69435],[129.7835,42.76521],[129.80719,42.79218],[129.83277,42.86746],[129.85261,42.96494],[129.8865,43.00395],[129.95082,43.01051],[129.96409,42.97306],[130.12957,42.98361],[130.09764,42.91425],[130.26095,42.9027],[130.23068,42.80125],[130.2385,42.71127],[130.41826,42.6011],[130.44361,42.54849],[130.50123,42.61636],[130.55143,42.52158],[130.62107,42.58413],[130.56576,42.68925],[130.40213,42.70788],[130.44361,42.76205],[130.66524,42.84753],[131.02438,42.86518],[131.02668,42.91246],[131.135,42.94114],[131.10274,43.04734],[131.20414,43.13654],[131.19031,43.21385],[131.30324,43.39498],[131.29402,43.46695],[131.19492,43.53047],[131.21105,43.82383],[131.26176,43.94011],[131.23583,43.96085],[131.25484,44.03131],[131.30365,44.04262],[131.1108,44.70266],[130.95639,44.85154],[131.48415,44.99513],[131.68466,45.12374],[131.66852,45.2196],[131.76532,45.22609],[131.86903,45.33636],[131.99417,45.2567],[132.83978,45.05916],[132.96373,45.0212],[133.12293,45.1332],[133.09279,45.25693],[133.19419,45.51913],[133.41083,45.57723],[133.48457,45.86203],[133.60442,45.90053],[133.67569,45.9759],[133.72695,46.05576],[133.68047,46.14697],[133.88097,46.25066],[133.91496,46.4274],[133.84104,46.46681],[134.03538,46.75668],[134.20016,47.33458],[134.50898,47.4812],[134.7671,47.72051],[134.55508,47.98651],[134.67098,48.1564],[134.75328,48.36763],[134.49516,48.42884],[132.66989,47.96491],[132.57309,47.71741],[131.90448,47.68011],[131.2635,47.73325],[131.09871,47.6852],[130.95985,47.6957],[130.90915,47.90623],[130.65103,48.10052],[130.84462,48.30942],[130.52147,48.61745],[130.66946,48.88251],[130.43232,48.90844],[130.2355,48.86741],[129.85416,49.11067],[129.67598,49.29596],[129.50685,49.42398],[129.40398,49.44194],[129.35317,49.3481],[129.23232,49.40353],[129.11153,49.36813],[128.72896,49.58676],[127.83476,49.5748],[127.53516,49.84306],[127.49299,50.01251],[127.60515,50.23503],[127.37384,50.28393],[127.36009,50.43787],[127.28765,50.46585],[127.36335,50.58306],[127.28165,50.72075],[127.14586,50.91152],[126.93135,51.0841],[126.90369,51.3238],[126.68349,51.70607],[126.44606,51.98254],[126.558,52.13738],[125.6131,53.07229],[125.17522,53.20225],[124.46078,53.21881],[123.86158,53.49391],[123.26989,53.54843],[122.85966,53.47395],[122.35063,53.49565],[121.39213,53.31888],[120.85633,53.28499],[120.0451,52.7359],[120.04049,52.58773],[120.46454,52.63811],[120.71673,52.54099],[120.61346,52.32447],[120.77337,52.20805],[120.65907,51.93544],[120.10963,51.671],[119.13553,50.37412],[119.38598,50.35162],[119.27996,50.13348],[119.11003,50.00276],[118.61623,49.93809],[117.82343,49.52696],[117.48208,49.62324],[117.27597,49.62544],[117.07142,49.68482],[116.71193,49.83813],[116.03781,48.87014],[116.06565,48.81716],[115.78876,48.51781],[115.811,48.25699],[115.52082,48.15367],[115.57128,47.91988],[115.94296,47.67741],[116.08431,47.80693],[116.2527,47.87766],[116.4465,47.83662],[116.67405,47.89039],[116.87527,47.88836],[117.08918,47.82242],[117.37875,47.63627],[117.50181,47.77216],[117.80196,48.01661],[118.03676,48.00982],[118.11009,48.04],[118.22677,48.03853],[118.29654,48.00246],[118.55766,47.99277],[118.7564,47.76947],[119.12343,47.66458],[119.13995,47.53997],[119.35892,47.48104],[119.31964,47.42617],[119.54918,47.29505],[119.56019,47.24874],[119.62403,47.24575],[119.71209,47.19192],[119.85518,46.92196],[119.91242,46.90091],[119.89261,46.66423],[119.80455,46.67631],[119.77373,46.62947],[119.68127,46.59015],[119.65265,46.62342],[119.42827,46.63783],[119.37306,46.61132],[119.30261,46.6083],[119.24978,46.64761],[119.10448,46.65516],[119.00541,46.74273],[118.92616,46.72765],[118.89974,46.77139],[118.8337,46.77742],[118.78747,46.68689],[118.30534,46.73519],[117.69554,46.50991],[117.60748,46.59771],[117.41782,46.57862],[117.36609,46.36335],[117.07252,46.35818],[116.83166,46.38637],[116.75551,46.33083],[116.58612,46.30211],[116.26678,45.96479],[116.24012,45.8778],[116.27366,45.78637],[116.16989,45.68603],[115.91898,45.6227],[115.69688,45.45761],[115.35757,45.39106],[114.94546,45.37377],[114.74612,45.43585],[114.54801,45.38337],[114.5166,45.27189],[114.08071,44.92847],[113.909,44.91444],[113.63821,44.74326],[112.74662,44.86297],[112.4164,45.06858],[111.98695,45.09074],[111.76275,44.98032],[111.40498,44.3461],[111.96289,43.81596],[111.93776,43.68709],[111.79758,43.6637],[111.59087,43.51207],[111.0149,43.3289],[110.4327,42.78293],[110.08401,42.6411],[109.89402,42.63111],[109.452,42.44842],[109.00679,42.45302],[108.84489,42.40246],[108.23156,42.45532],[107.57258,42.40898],[107.49681,42.46221],[107.29755,42.41395],[107.24774,42.36107],[106.76517,42.28741],[105.24708,41.7442],[105.01119,41.58382],[104.91272,41.64619],[104.51667,41.66113],[104.52258,41.8706],[103.92804,41.78246],[103.3685,41.89696],[102.72403,42.14675],[102.42826,42.15137],[102.07645,42.22519],[101.80515,42.50074],[101.28833,42.58524],[100.84979,42.67087],[100.33297,42.68231],[99.50671,42.56535],[97.1777,42.7964],[96.37926,42.72055],[96.35658,42.90363],[95.89543,43.2528],[95.52594,43.99353],[95.32891,44.02407],[95.39772,44.2805],[95.01191,44.25274],[94.71959,44.35284],[94.10003,44.71016],[93.51161,44.95964],[91.64048,45.07408],[90.89169,45.19667],[90.65114,45.49314],[90.70907,45.73437],[91.03026,46.04194],[90.99672,46.14207],[90.89639,46.30711],[91.07696,46.57315],[91.0147,46.58171],[91.03649,46.72916],[90.84035,46.99525],[90.76108,46.99399],[90.48542,47.30438],[90.48854,47.41826],[90.33598,47.68303],[90.10871,47.7375],[90.06512,47.88177],[89.76624,47.82745],[89.55453,48.0423],[89.0711,47.98528],[88.93186,48.10263],[88.8011,48.11302],[88.58316,48.21893],[88.58939,48.34531],[87.96361,48.58478],[88.0788,48.71436],[87.73822,48.89582],[87.88171,48.95853],[87.81333,49.17354],[87.48983,49.13794],[87.478,49.07403],[87.28386,49.11626],[86.87238,49.12432],[86.73568,48.99918],[86.75343,48.70331],[86.38069,48.46064],[85.73581,48.3939],[85.5169,48.05493],[85.61067,47.49753],[85.69696,47.2898],[85.54294,47.06171],[85.22443,47.04816],[84.93995,46.87399],[84.73077,47.01394],[83.92184,46.98912],[83.04622,47.19053],[82.21792,45.56619],[82.58474,45.40027],[82.51374,45.1755],[81.73278,45.3504],[80.11169,45.03352],[79.8987,44.89957],[80.38384,44.63073],[80.40229,44.23319],[80.40031,44.10986],[80.75156,43.44948],[80.69718,43.32589],[80.77771,43.30065],[80.78817,43.14235],[80.62913,43.141],[80.3735,43.01557],[80.58999,42.9011],[80.38169,42.83142],[80.26886,42.8366],[80.16892,42.61137],[80.26841,42.23797],[80.17807,42.21166],[80.17842,42.03211],[79.92977,42.04113],[78.3732,41.39603],[78.15757,41.38565],[78.12873,41.23091],[77.81287,41.14307],[77.76206,41.01574],[77.52723,41.00227],[77.3693,41.0375],[77.28004,41.0033],[76.99302,41.0696],[76.75681,40.95354],[76.5261,40.46114],[76.33659,40.3482],[75.96168,40.38064],[75.91361,40.2948],[75.69663,40.28642],[75.5854,40.66874],[75.22834,40.45382],[75.08243,40.43945],[74.82013,40.52197],[74.78168,40.44886],[74.85996,40.32857],[74.69875,40.34668],[74.35063,40.09742],[74.25533,40.13191],[73.97049,40.04378],[73.83006,39.76136],[73.9051,39.75073],[73.92354,39.69565],[73.94683,39.60733],[73.87018,39.47879],[73.59831,39.46425],[73.59241,39.40843],[73.5004,39.38402]]]}},{"type":"Feature","properties":{"id":"CL"},"geometry":{"type":"Polygon","coordinates":[[[-113.52687,-26.52828],[-68.11646,-58.14883],[-66.07313,-55.19618],[-67.11046,-54.94199],[-67.46182,-54.92205],[-68.01394,-54.8753],[-68.60733,-54.9125],[-68.60702,-52.65781],[-68.41683,-52.33516],[-69.97824,-52.00845],[-71.99889,-51.98018],[-72.33873,-51.59954],[-72.31343,-50.58411],[-73.15765,-50.78337],[-73.55259,-49.92488],[-73.45156,-49.79461],[-73.09655,-49.14342],[-72.56894,-48.81116],[-72.54042,-48.52392],[-72.27662,-48.28727],[-72.50478,-47.80586],[-71.94152,-47.13595],[-71.68577,-46.55385],[-71.75614,-45.61611],[-71.35687,-45.22075],[-72.06985,-44.81756],[-71.26418,-44.75684],[-71.16436,-44.46244],[-71.81318,-44.38097],[-71.64206,-43.64774],[-72.14828,-42.85321],[-72.15541,-42.15941],[-71.74901,-42.11711],[-71.92726,-40.72714],[-71.37826,-38.91474],[-70.89532,-38.6923],[-71.24279,-37.20264],[-70.95047,-36.4321],[-70.38008,-36.02375],[-70.49416,-35.24145],[-69.87386,-34.13344],[-69.88099,-33.34489],[-70.55832,-31.51559],[-70.14479,-30.36595],[-69.8596,-30.26131],[-69.99507,-29.28351],[-69.80969,-29.07185],[-69.66709,-28.44055],[-69.22504,-27.95042],[-68.77586,-27.16029],[-68.43363,-27.08414],[-68.27677,-26.90626],[-68.59048,-26.49861],[-68.56909,-26.28146],[-68.38372,-26.15353],[-68.57622,-25.32505],[-68.38372,-25.08636],[-68.56909,-24.69831],[-68.24825,-24.42596],[-67.33563,-24.04237],[-66.99632,-22.99839],[-67.18382,-22.81525],[-67.54284,-22.89771],[-67.85114,-22.87076],[-68.18816,-21.28614],[-68.40403,-20.94562],[-68.53957,-20.91542],[-68.55383,-20.7355],[-68.44023,-20.62701],[-68.7276,-20.46178],[-68.74273,-20.08817],[-68.57132,-20.03134],[-68.54611,-19.84651],[-68.66761,-19.72118],[-68.41218,-19.40499],[-68.61989,-19.27584],[-68.80602,-19.08355],[-68.87082,-19.06003],[-68.94987,-18.93302],[-69.07432,-18.28259],[-69.14807,-18.16893],[-69.07496,-18.03715],[-69.28671,-17.94844],[-69.34126,-17.72753],[-69.46623,-17.60518],[-69.46897,-17.4988],[-69.66483,-17.65083],[-69.79087,-17.65563],[-69.82868,-17.72048],[-69.75305,-17.94605],[-69.81607,-18.12582],[-69.96732,-18.25992],[-70.16394,-18.31737],[-70.31267,-18.31258],[-70.378,-18.3495],[-70.59118,-18.35072],[-73.98689,-20.10822],[-113.52687,-26.52828]]]}},{"type":"Feature","properties":{"id":"CI"},"geometry":{"type":"Polygon","coordinates":[[[-8.59456,6.50612],[-8.48652,6.43797],[-8.45666,6.49977],[-8.38453,6.35887],[-8.3298,6.36381],[-8.17557,6.28222],[-8.00642,6.31684],[-7.90692,6.27728],[-7.83478,6.20309],[-7.8497,6.08932],[-7.79747,6.07696],[-7.78254,5.99037],[-7.70294,5.90625],[-7.67309,5.94337],[-7.48155,5.80974],[-7.46165,5.84934],[-7.43677,5.84687],[-7.43926,5.74787],[-7.37209,5.61173],[-7.43428,5.42355],[-7.36463,5.32944],[-7.46165,5.26256],[-7.48901,5.14118],[-7.55369,5.08667],[-7.53876,4.94294],[-7.59349,4.8909],[-7.53259,4.35145],[-7.52774,3.7105],[-3.34019,4.17519],[-3.10675,5.08515],[-3.11073,5.12675],[-3.063,5.13665],[-2.96554,5.10397],[-2.95261,5.12477],[-2.75502,5.10657],[-2.73074,5.1364],[-2.77625,5.34621],[-2.72737,5.34789],[-2.76614,5.60963],[-2.85378,5.65156],[-2.93132,5.62137],[-2.96671,5.6415],[-2.95323,5.71865],[-3.01896,5.71697],[-3.25999,6.62521],[-3.21954,6.74407],[-3.23327,6.81744],[-2.95438,7.23737],[-2.97822,7.27165],[-2.92339,7.60847],[-2.79467,7.86002],[-2.78395,7.94974],[-2.74819,7.92613],[-2.67787,8.02055],[-2.61232,8.02645],[-2.62901,8.11495],[-2.49037,8.20872],[-2.58243,8.7789],[-2.66357,9.01771],[-2.77799,9.04949],[-2.69814,9.22717],[-2.68802,9.49343],[-2.76494,9.40778],[-2.93012,9.57403],[-3.00765,9.74019],[-3.16609,9.85147],[-3.19306,9.93781],[-3.27228,9.84981],[-3.31779,9.91125],[-3.69703,9.94279],[-4.25999,9.76012],[-4.31392,9.60062],[-4.6426,9.70696],[-4.96621,9.89132],[-4.96453,9.99923],[-5.12465,10.29788],[-5.39602,10.2929],[-5.51058,10.43177],[-5.65135,10.46767],[-5.78124,10.43952],[-5.99478,10.19694],[-6.18851,10.24244],[-6.1731,10.46983],[-6.24795,10.74248],[-6.325,10.68624],[-6.40646,10.69922],[-6.42847,10.5694],[-6.52974,10.59104],[-6.63541,10.66893],[-6.68164,10.35074],[-6.93921,10.35291],[-7.01186,10.25111],[-6.97444,10.21644],[-7.00966,10.15794],[-7.0603,10.14711],[-7.13331,10.24877],[-7.3707,10.24677],[-7.44555,10.44602],[-7.52261,10.4655],[-7.54462,10.40921],[-7.63048,10.46334],[-7.92107,10.15577],[-7.97971,10.17117],[-8.01225,10.1021],[-8.11921,10.04577],[-8.15652,9.94288],[-8.09434,9.86936],[-8.14657,9.55062],[-8.03463,9.39604],[-7.85056,9.41812],[-7.90777,9.20456],[-7.73862,9.08422],[-7.92518,8.99332],[-7.95503,8.81146],[-7.69882,8.66148],[-7.65653,8.36873],[-7.92518,8.50652],[-8.22991,8.48438],[-8.2411,8.24196],[-8.062,8.16071],[-7.98675,8.20134],[-7.99919,8.11023],[-7.94695,8.00925],[-8.06449,8.04989],[-8.13414,7.87991],[-8.09931,7.78626],[-8.21374,7.54466],[-8.4003,7.6285],[-8.47114,7.55676],[-8.41935,7.51203],[-8.37458,7.25794],[-8.29249,7.1691],[-8.31736,6.82837],[-8.59456,6.50612]]]}},{"type":"Feature","properties":{"id":"CM"},"geometry":{"type":"Polygon","coordinates":[[[8.34397,4.30689],[8.6479,4.06346],[9.22018,3.72052],[9.6225,2.44901],[9.81162,2.33797],[9.82123,2.35097],[9.83754,2.32428],[9.83238,2.29079],[9.84716,2.24676],[9.89012,2.20457],[9.90749,2.20049],[9.991,2.16561],[11.3561,2.17217],[11.37116,2.29975],[13.28534,2.25716],[13.29457,2.16106],[14.61145,2.17866],[15.00996,1.98887],[15.22634,2.03243],[15.34776,1.91264],[15.48942,1.98265],[16.02959,1.76483],[16.02647,1.65591],[16.14634,1.70259],[16.05294,1.9811],[16.08563,2.19733],[16.15568,2.18955],[16.19357,2.21537],[16.08252,2.45708],[16.05449,3.02306],[15.77725,3.26835],[15.73522,3.24348],[15.07686,4.01805],[15.17482,4.05131],[15.10644,4.1362],[15.08609,4.30282],[15.00825,4.41458],[14.73383,4.6135],[14.65489,5.21343],[14.57083,5.23979],[14.52724,5.28319],[14.62531,5.51411],[14.58951,5.59777],[14.62375,5.70466],[14.60974,5.91838],[14.49455,5.91683],[14.42917,6.00508],[14.43073,6.08867],[14.56149,6.18928],[14.74206,6.26356],[14.80122,6.34866],[14.79966,6.39043],[14.96311,6.75693],[15.04717,6.77085],[15.23397,7.25135],[15.49743,7.52179],[15.56964,7.58936],[15.59272,7.7696],[15.50743,7.79302],[15.20426,8.50892],[15.09484,8.65982],[14.83566,8.80557],[14.35707,9.19611],[14.37094,9.2954],[13.97544,9.6365],[14.01793,9.73169],[14.1317,9.82413],[14.20411,10.00055],[14.4673,10.00264],[14.80082,9.93818],[14.95722,9.97926],[15.05999,9.94845],[15.14043,9.99246],[15.24618,9.99246],[15.41408,9.92876],[15.68761,9.99344],[15.50535,10.1098],[15.30874,10.31063],[15.23724,10.47764],[15.14936,10.53915],[15.15532,10.62846],[15.06737,10.80921],[15.09127,10.87431],[15.04957,11.02347],[15.10021,11.04101],[15.0585,11.40481],[15.13149,11.5537],[15.06595,11.71126],[15.11579,11.79313],[15.04808,11.8731],[15.05786,12.0608],[15.0349,12.10698],[15.00146,12.1223],[14.96952,12.0925],[14.89019,12.16593],[14.90827,12.3269],[14.83314,12.62963],[14.55058,12.78256],[14.56101,12.91036],[14.46881,13.08259],[14.08251,13.0797],[14.20204,12.53405],[14.17523,12.41916],[14.22215,12.36533],[14.4843,12.35223],[14.6474,12.17466],[14.61612,11.7798],[14.55207,11.72001],[14.64591,11.66166],[14.6124,11.51283],[14.17821,11.23831],[13.97489,11.30258],[13.78945,11.00154],[13.7403,11.00593],[13.70753,10.94451],[13.73434,10.9255],[13.54964,10.61236],[13.5705,10.53183],[13.43644,10.13326],[13.34111,10.12299],[13.25025,10.03647],[13.25323,10.00127],[13.286,9.9822],[13.27409,9.93232],[13.24132,9.91031],[13.25025,9.86042],[13.29941,9.8296],[13.25472,9.76795],[13.22642,9.57266],[13.02385,9.49334],[12.85628,9.36698],[12.91958,9.33905],[12.90022,9.11411],[12.81085,8.91992],[12.79,8.75361],[12.71701,8.7595],[12.68722,8.65938],[12.44146,8.6152],[12.4489,8.52536],[12.26123,8.43696],[12.24782,8.17904],[12.19271,8.10826],[12.20909,7.97553],[11.99908,7.67302],[12.01844,7.52981],[11.93205,7.47812],[11.84864,7.26098],[11.87396,7.09398],[11.63117,6.9905],[11.55818,6.86186],[11.57755,6.74059],[11.51499,6.60892],[11.42264,6.5882],[11.42041,6.53789],[11.09495,6.51717],[11.09644,6.68437],[10.94302,6.69325],[10.8179,6.83377],[10.83727,6.9358],[10.60789,7.06885],[10.59746,7.14719],[10.57214,7.16345],[10.53639,6.93432],[10.21466,6.88996],[10.15135,7.03781],[9.86314,6.77756],[9.77824,6.79088],[9.70674,6.51717],[9.51757,6.43874],[8.84209,5.82562],[8.88156,5.78857],[8.83687,5.68483],[8.92029,5.58403],[8.78027,5.1243],[8.60302,4.87353],[8.34397,4.30689]]]}},{"type":"Feature","properties":{"id":"IN-TG"},"geometry":{"type":"Polygon","coordinates":[[[77.2344,16.47658],[77.28847,16.40595],[77.48451,16.38223],[77.59523,16.34336],[77.60364,16.29657],[77.49893,16.26906],[77.51197,15.92864],[77.65754,15.88869],[78.03108,15.90421],[78.12,15.82892],[78.41766,16.08012],[78.68408,16.04581],[79.21623,16.21665],[79.21485,16.48251],[79.31304,16.57302],[79.79507,16.72137],[79.94476,16.62731],[80.02544,16.71249],[80.0735,16.81242],[79.99248,16.8604],[80.04432,16.96256],[80.18199,17.04628],[80.26027,17.0082],[80.31864,16.87387],[80.40138,16.85613],[80.45391,16.81702],[80.45871,16.7881],[80.58711,16.772],[80.57544,16.91855],[80.36189,16.96683],[80.38867,17.08139],[80.4467,17.01772],[80.50369,17.1083],[80.61733,17.13226],[80.68771,17.06794],[80.83671,17.02494],[80.91499,17.20081],[81.40594,17.3585],[81.77398,17.88596],[81.39221,17.81014],[81.05026,17.77615],[80.51055,18.62802],[80.34782,18.59158],[80.27503,18.72104],[80.11196,18.6869],[79.9094,18.82474],[79.96261,18.86145],[79.97051,19.35358],[79.93858,19.47792],[79.86991,19.5009],[79.804,19.60054],[79.63165,19.57887],[79.47578,19.49928],[79.23408,19.61219],[79.19048,19.46044],[78.94432,19.54943],[78.95839,19.66037],[78.84613,19.65778],[78.82793,19.75749],[78.27449,19.90476],[78.35861,19.75604],[78.26694,19.69544],[78.3011,19.46885],[78.19141,19.42903],[78.14695,19.22898],[78.00653,19.30158],[77.86422,19.3207],[77.84379,19.18359],[77.74251,19.02577],[77.94353,18.82604],[77.85684,18.81905],[77.74251,18.68267],[77.73994,18.55204],[77.60965,18.5522],[77.56785,18.28849],[77.61377,18.10033],[77.56296,18.04925],[77.66166,17.9686],[77.50837,17.79298],[77.57823,17.74378],[77.45841,17.70568],[77.44005,17.57693],[77.52571,17.57693],[77.67608,17.52751],[77.62716,17.44335],[77.53583,17.44007],[77.52244,17.35178],[77.45155,17.3721],[77.46202,17.28607],[77.36623,17.15883],[77.46356,17.11454],[77.50099,17.01657],[77.45532,16.92068],[77.47489,16.77956],[77.44279,16.78712],[77.427,16.72252],[77.47095,16.71216],[77.47198,16.587],[77.37636,16.48481],[77.32366,16.48942],[77.2344,16.47658]]]}},{"type":"Feature","properties":{"id":"CD"},"geometry":{"type":"Polygon","coordinates":[[[11.95767,-5.94705],[12.42245,-6.07585],[13.04371,-5.87078],[16.55507,-5.85631],[16.96282,-7.21787],[17.5828,-8.13784],[18.33635,-8.00126],[19.33698,-7.99743],[19.5469,-7.00195],[20.30218,-6.98955],[20.31846,-6.91953],[20.61689,-6.90876],[20.56263,-7.28566],[21.79824,-7.29628],[21.84856,-9.59871],[22.19039,-9.94628],[22.32604,-10.76291],[22.17954,-10.85884],[22.25951,-11.24911],[22.54205,-11.05784],[23.16602,-11.10577],[23.45631,-10.946],[23.86868,-11.02856],[24.00027,-10.89356],[24.34528,-11.06816],[24.42612,-11.44975],[25.34069,-11.19707],[25.33058,-11.65767],[26.01777,-11.91488],[26.88687,-12.01868],[27.04351,-11.61312],[27.22541,-11.60323],[27.21025,-11.76157],[27.59932,-12.22123],[28.33199,-12.41375],[29.01918,-13.41353],[29.60531,-13.21685],[29.65078,-13.41844],[29.81551,-13.44683],[29.8139,-12.14898],[29.48404,-12.23604],[29.4992,-12.43843],[29.18592,-12.37921],[28.48357,-11.87532],[28.37241,-11.57848],[28.65032,-10.65133],[28.62795,-9.92942],[28.68532,-9.78],[28.56208,-9.49122],[28.51627,-9.44726],[28.52636,-9.35379],[28.36562,-9.30091],[28.38526,-9.23393],[28.9711,-8.66935],[28.88917,-8.4831],[30.79243,-8.27382],[30.2567,-7.14121],[29.52552,-6.2731],[29.43673,-4.44845],[29.23708,-3.75856],[29.21463,-3.3514],[29.25633,-3.05471],[29.17258,-2.99385],[29.16037,-2.95457],[29.09797,-2.91935],[29.09119,-2.87871],[29.0505,-2.81774],[29.00404,-2.81978],[29.00167,-2.78523],[29.04081,-2.7416],[29.00357,-2.70596],[28.94346,-2.69124],[28.89793,-2.66111],[28.90226,-2.62385],[28.89288,-2.55848],[28.87943,-2.55165],[28.86193,-2.53185],[28.86209,-2.5231],[28.87497,-2.50887],[28.88846,-2.50493],[28.89342,-2.49017],[28.89132,-2.47557],[28.86846,-2.44866],[28.86826,-2.41888],[28.89601,-2.37321],[28.95642,-2.37321],[29.00051,-2.29001],[29.105,-2.27043],[29.17562,-2.12278],[29.11847,-1.90576],[29.24458,-1.69663],[29.24323,-1.66826],[29.36322,-1.50887],[29.45038,-1.5054],[29.53062,-1.40499],[29.59061,-1.39016],[29.58388,-0.89821],[29.63006,-0.8997],[29.62708,-0.71055],[29.67176,-0.55714],[29.67474,-0.47969],[29.65091,-0.46777],[29.72687,-0.08051],[29.7224,0.07291],[29.77454,0.16675],[29.81922,0.16824],[29.87284,0.39166],[29.97413,0.52124],[29.95477,0.64486],[29.98307,0.84295],[30.1484,0.89805],[30.22139,0.99635],[30.24671,1.14974],[30.48503,1.21675],[31.30127,2.11006],[31.28042,2.17853],[31.20148,2.2217],[31.1985,2.29462],[31.12104,2.27676],[31.07934,2.30207],[31.06593,2.35862],[30.96911,2.41071],[30.91102,2.33332],[30.83059,2.42559],[30.74271,2.43601],[30.75612,2.5863],[30.8857,2.83923],[30.8574,2.9508],[30.77101,3.04897],[30.84251,3.26908],[30.93486,3.40737],[30.94081,3.50847],[30.85153,3.48867],[30.85997,3.5743],[30.80713,3.60506],[30.78512,3.67097],[30.56277,3.62703],[30.57378,3.74567],[30.55396,3.84451],[30.47691,3.83353],[30.27658,3.95653],[30.22374,3.93896],[30.1621,4.10586],[30.06964,4.13221],[29.79666,4.37809],[29.82087,4.56246],[29.49726,4.7007],[29.43341,4.50101],[29.22207,4.34297],[29.03054,4.48784],[28.8126,4.48784],[28.6651,4.42638],[28.20719,4.35614],[27.79551,4.59976],[27.76469,4.79284],[27.65462,4.89375],[27.56656,4.89375],[27.44012,5.07349],[27.09575,5.22305],[26.93064,5.13535],[26.85579,5.03887],[26.74572,5.10685],[26.48595,5.04984],[26.13371,5.25594],[25.86073,5.19455],[25.53271,5.37431],[25.34558,5.29101],[25.31256,5.03668],[24.71816,4.90509],[24.46719,5.0915],[23.38847,4.60013],[22.94817,4.82392],[22.89094,4.79321],[22.84691,4.69887],[22.78526,4.71423],[22.6928,4.47285],[22.60915,4.48821],[22.5431,4.22041],[22.45504,4.13039],[22.27682,4.11347],[22.10721,4.20723],[21.6405,4.317],[21.55904,4.25553],[21.25744,4.33676],[21.21341,4.29285],[21.11214,4.33895],[21.08793,4.39603],[20.90383,4.44877],[20.60184,4.42394],[18.62755,3.47564],[18.63857,3.19342],[18.10683,2.26876],[18.08034,1.58553],[17.85887,1.04327],[17.86989,0.58873],[17.95255,0.48128],[17.93877,0.32424],[17.81204,0.23884],[17.66051,-0.26535],[17.72112,-0.52707],[17.32438,-0.99265],[16.97999,-1.12762],[16.70724,-1.45815],[16.50336,-1.8795],[16.16173,-2.16586],[16.22785,-2.59528],[16.1755,-3.25014],[16.21407,-3.2969],[15.89448,-3.9513],[15.53081,-4.042],[15.48121,-4.22062],[15.41785,-4.28381],[15.32693,-4.27282],[15.25411,-4.31121],[15.1978,-4.32388],[14.83101,-4.80838],[14.67948,-4.92093],[14.5059,-4.84956],[14.41499,-4.8825],[14.37366,-4.56125],[14.47284,-4.42941],[14.3957,-4.36623],[14.40672,-4.28381],[13.9108,-4.50906],[13.81162,-4.41842],[13.71794,-4.44864],[13.70417,-4.72601],[13.50305,-4.77818],[13.41764,-4.89897],[13.11182,-4.5942],[13.09648,-4.63739],[13.11195,-4.67745],[12.8733,-4.74346],[12.70868,-4.95505],[12.63465,-4.94632],[12.60251,-5.01715],[12.46297,-5.09408],[12.49815,-5.14058],[12.51589,-5.1332],[12.53586,-5.14658],[12.53599,-5.1618],[12.52301,-5.17481],[12.52318,-5.74353],[12.26557,-5.74031],[12.20376,-5.76338],[11.95767,-5.94705]]]}},{"type":"Feature","properties":{"id":"CG"},"geometry":{"type":"Polygon","coordinates":[[[10.75913,-4.39519],[11.50888,-5.33417],[12.00924,-5.02627],[12.16068,-4.90089],[12.20901,-4.75642],[12.25587,-4.79437],[12.32324,-4.78415],[12.40964,-4.60609],[12.64835,-4.55937],[12.76844,-4.38709],[12.87096,-4.40315],[12.91489,-4.47907],[13.09648,-4.63739],[13.11182,-4.5942],[13.41764,-4.89897],[13.50305,-4.77818],[13.70417,-4.72601],[13.71794,-4.44864],[13.81162,-4.41842],[13.9108,-4.50906],[14.40672,-4.28381],[14.3957,-4.36623],[14.47284,-4.42941],[14.37366,-4.56125],[14.41499,-4.8825],[14.5059,-4.84956],[14.67948,-4.92093],[14.83101,-4.80838],[15.1978,-4.32388],[15.25411,-4.31121],[15.32693,-4.27282],[15.41785,-4.28381],[15.48121,-4.22062],[15.53081,-4.042],[15.89448,-3.9513],[16.21407,-3.2969],[16.1755,-3.25014],[16.22785,-2.59528],[16.16173,-2.16586],[16.50336,-1.8795],[16.70724,-1.45815],[16.97999,-1.12762],[17.32438,-0.99265],[17.72112,-0.52707],[17.66051,-0.26535],[17.81204,0.23884],[17.93877,0.32424],[17.95255,0.48128],[17.86989,0.58873],[17.85887,1.04327],[18.08034,1.58553],[18.10683,2.26876],[18.63857,3.19342],[18.62755,3.47564],[18.58711,3.49423],[18.49245,3.63924],[18.39558,3.58212],[18.2723,3.57992],[18.24148,3.50302],[18.17323,3.47665],[18.14902,3.54476],[18.05656,3.56893],[17.85842,3.53378],[17.83421,3.61068],[17.60966,3.63705],[17.46876,3.70515],[17.35649,3.63045],[17.01746,3.55136],[16.68283,3.54257],[16.57598,3.47999],[16.46701,2.92512],[16.50126,2.84739],[16.19357,2.21537],[16.15568,2.18955],[16.08563,2.19733],[16.05294,1.9811],[16.14634,1.70259],[16.02647,1.65591],[16.02959,1.76483],[15.48942,1.98265],[15.34776,1.91264],[15.22634,2.03243],[15.00996,1.98887],[14.61145,2.17866],[13.29457,2.16106],[13.13461,1.57238],[13.25447,1.32339],[13.15519,1.23368],[13.89582,1.4261],[14.25186,1.39842],[14.48179,0.9152],[14.26066,0.57255],[14.10909,0.58563],[13.88648,0.26652],[13.90632,-0.2287],[14.06862,-0.20826],[14.2165,-0.38261],[14.41887,-0.44799],[14.52569,-0.57818],[14.41838,-1.89412],[14.25932,-1.97624],[14.23518,-2.15671],[14.16202,-2.23916],[14.23829,-2.33715],[14.10442,-2.49268],[13.85846,-2.46935],[13.92073,-2.35581],[13.75884,-2.09293],[13.47977,-2.43224],[13.02759,-2.33098],[12.82172,-1.91091],[12.61312,-1.8129],[12.44656,-1.92025],[12.47925,-2.32626],[12.04895,-2.41704],[11.96866,-2.33559],[11.74605,-2.39936],[11.57637,-2.33379],[11.64487,-2.61865],[11.5359,-2.85654],[11.64798,-2.81146],[11.80365,-3.00424],[11.70558,-3.0773],[11.70227,-3.17465],[11.96554,-3.30267],[11.8318,-3.5812],[11.92719,-3.62768],[11.87083,-3.71571],[11.68608,-3.68942],[11.57949,-3.52798],[11.48764,-3.51089],[11.22301,-3.69888],[11.12647,-3.94169],[10.75913,-4.39519]]]}},{"type":"Feature","properties":{"id":"CO"},"geometry":{"type":"Polygon","coordinates":[[[-82.56142,11.91792],[-78.79327,9.93766],[-77.58292,9.22278],[-77.32389,8.81247],[-77.45064,8.49991],[-77.17257,7.97422],[-77.57185,7.51147],[-77.72514,7.72348],[-77.72157,7.47612],[-77.81426,7.48319],[-77.89178,7.22681],[-78.06168,7.07793],[-82.12561,4.00341],[-78.87137,1.47457],[-78.42749,1.15389],[-77.85677,0.80197],[-77.7148,0.85003],[-77.68613,0.83029],[-77.66416,0.81604],[-77.67815,0.73863],[-77.49984,0.64476],[-77.52001,0.40782],[-76.89177,0.24736],[-76.4094,0.24015],[-76.41215,0.38228],[-76.23441,0.42294],[-75.82927,0.09578],[-75.25764,-0.11943],[-75.18513,-0.0308],[-74.42701,-0.50218],[-74.26675,-0.97229],[-73.65312,-1.26222],[-72.92587,-2.44514],[-71.75223,-2.15058],[-70.94377,-2.23142],[-70.04609,-2.73906],[-70.71396,-3.7921],[-70.52393,-3.87553],[-70.3374,-3.79505],[-69.94708,-4.2431],[-69.43395,-1.42219],[-69.4215,-1.01853],[-69.59796,-0.75136],[-69.603,-0.51947],[-70.03658,-0.19681],[-70.04162,0.55437],[-69.47696,0.71065],[-69.20976,0.57958],[-69.14422,0.84172],[-69.26017,1.06856],[-69.82987,1.07864],[-69.83491,1.69353],[-69.53746,1.76408],[-69.38621,1.70865],[-68.18128,1.72881],[-68.26699,1.83463],[-68.18632,2.00091],[-67.9292,1.82455],[-67.40488,2.22258],[-67.299,1.87494],[-67.15784,1.80439],[-67.08222,1.17441],[-66.85795,1.22998],[-67.21967,2.35778],[-67.65696,2.81691],[-67.85862,2.79173],[-67.85862,2.86727],[-67.30945,3.38393],[-67.50067,3.75812],[-67.62671,3.74303],[-67.85358,4.53249],[-67.83341,5.31104],[-67.59141,5.5369],[-67.63914,5.64963],[-67.58558,5.84537],[-67.43513,5.98835],[-67.4625,6.20625],[-67.60654,6.2891],[-69.41843,6.1072],[-70.10716,6.96516],[-70.7596,7.09799],[-71.03941,6.98163],[-71.37234,7.01588],[-71.42212,7.03854],[-71.44118,7.02116],[-71.82441,7.04314],[-72.04895,7.03837],[-72.19437,7.37034],[-72.43132,7.40034],[-72.47415,7.48928],[-72.45321,7.57232],[-72.47827,7.65604],[-72.46763,7.79518],[-72.44454,7.86031],[-72.46183,7.90682],[-72.45806,7.91141],[-72.47042,7.92306],[-72.48183,7.92909],[-72.48801,7.94329],[-72.47213,7.96106],[-72.39137,8.03534],[-72.35163,8.01163],[-72.36987,8.19976],[-72.4042,8.36513],[-72.65474,8.61428],[-72.77415,9.10165],[-72.94052,9.10663],[-73.02119,9.27584],[-73.36905,9.16636],[-72.98085,9.85253],[-72.88002,10.44309],[-72.4767,11.1117],[-72.24983,11.14138],[-71.9675,11.65536],[-71.3275,11.85],[-70.92579,11.96275],[-71.19849,12.65801],[-78.66279,16.62373],[-81.80642,15.86201],[-82.06974,14.49418],[-82.56142,11.91792]]]}},{"type":"Feature","properties":{"id":"CR"},"geometry":{"type":"Polygon","coordinates":[[[-87.41779,5.02401],[-82.94503,7.93865],[-82.89978,8.04083],[-82.89137,8.05755],[-82.88641,8.10219],[-82.9388,8.26634],[-83.05209,8.33394],[-82.93056,8.43465],[-82.8679,8.44042],[-82.8382,8.48117],[-82.83322,8.52464],[-82.83975,8.54755],[-82.82739,8.60153],[-82.8794,8.6981],[-82.92068,8.74832],[-82.91377,8.774],[-82.88253,8.83331],[-82.72126,8.97125],[-82.93516,9.07687],[-82.93516,9.46741],[-82.84871,9.4973],[-82.87919,9.62645],[-82.77206,9.59573],[-82.66667,9.49746],[-82.61345,9.49881],[-82.56507,9.57279],[-82.51044,9.65379],[-83.54024,10.96805],[-83.68276,11.01562],[-83.66597,10.79916],[-83.90838,10.71161],[-84.68197,11.07568],[-84.92439,10.9497],[-85.60529,11.22607],[-85.71223,11.06868],[-86.14524,11.09059],[-87.41779,5.02401]]]}},{"type":"Feature","properties":{"id":"CZ"},"geometry":{"type":"Polygon","coordinates":[[[12.09287,50.25032],[12.19335,50.19997],[12.21484,50.16399],[12.1917,50.13434],[12.2073,50.10315],[12.23709,50.10213],[12.27433,50.0771],[12.26111,50.06331],[12.30798,50.05719],[12.49908,49.97305],[12.47264,49.94222],[12.55197,49.92094],[12.48256,49.83575],[12.46603,49.78882],[12.40489,49.76321],[12.4462,49.70233],[12.52553,49.68415],[12.53544,49.61888],[12.56188,49.6146],[12.60155,49.52887],[12.64782,49.52565],[12.64121,49.47628],[12.669,49.42935],[12.71227,49.42363],[12.75854,49.3989],[12.78168,49.34618],[12.88414,49.33541],[12.88249,49.35479],[12.94859,49.34079],[13.03618,49.30417],[13.02957,49.27399],[13.05883,49.26259],[13.17665,49.16713],[13.17019,49.14339],[13.20405,49.12303],[13.23689,49.11412],[13.28242,49.1228],[13.39479,49.04812],[13.40802,48.98851],[13.50221,48.93752],[13.50552,48.97441],[13.58319,48.96899],[13.61624,48.9462],[13.67739,48.87886],[13.73854,48.88538],[13.76994,48.83537],[13.78977,48.83319],[13.8096,48.77877],[13.84023,48.76988],[14.06151,48.66873],[14.01482,48.63788],[14.09104,48.5943],[14.20691,48.5898],[14.33909,48.55852],[14.43076,48.58855],[14.4587,48.64695],[14.56139,48.60429],[14.60808,48.62881],[14.66762,48.58215],[14.71794,48.59794],[14.72756,48.69502],[14.80584,48.73489],[14.80821,48.77711],[14.81545,48.7874],[14.94773,48.76268],[14.95641,48.75915],[14.9758,48.76857],[14.98112,48.77524],[14.9782,48.7766],[14.98032,48.77959],[14.95072,48.79101],[14.98917,48.90082],[14.97612,48.96983],[14.99878,49.01444],[15.15534,48.99056],[15.16358,48.94278],[15.26177,48.95766],[15.28305,48.98831],[15.34823,48.98444],[15.48027,48.94481],[15.51357,48.91549],[15.61622,48.89541],[15.6921,48.85973],[15.75341,48.8516],[15.78087,48.87644],[15.84404,48.86921],[16.06034,48.75436],[16.37345,48.729],[16.40915,48.74576],[16.46134,48.80865],[16.67008,48.77699],[16.68518,48.7281],[16.71883,48.73806],[16.79779,48.70998],[16.90354,48.71541],[16.93955,48.60371],[17.00215,48.70887],[17.11202,48.82925],[17.19355,48.87602],[17.29054,48.85546],[17.3853,48.80936],[17.45671,48.85004],[17.5295,48.81117],[17.7094,48.86721],[17.73126,48.87885],[17.77944,48.92318],[17.87831,48.92679],[17.91814,49.01784],[18.06885,49.03157],[18.1104,49.08624],[18.15022,49.24518],[18.18456,49.28909],[18.36446,49.3267],[18.4139,49.36517],[18.4084,49.40003],[18.44686,49.39467],[18.54848,49.47059],[18.53063,49.49022],[18.57183,49.51162],[18.6144,49.49824],[18.67757,49.50895],[18.74761,49.492],[18.84521,49.51672],[18.84786,49.5446],[18.80479,49.6815],[18.72838,49.68163],[18.69817,49.70473],[18.62676,49.71983],[18.62943,49.74603],[18.62645,49.75002],[18.61368,49.75426],[18.61278,49.7618],[18.57183,49.83334],[18.60341,49.86256],[18.57045,49.87849],[18.57697,49.91565],[18.54299,49.92537],[18.54495,49.9079],[18.53423,49.89906],[18.41604,49.93498],[18.33562,49.94747],[18.33278,49.92415],[18.31914,49.91565],[18.27794,49.93863],[18.27107,49.96779],[18.21752,49.97309],[18.20241,49.99958],[18.10628,50.00223],[18.07898,50.04535],[18.03212,50.06574],[18.00396,50.04954],[18.04585,50.03311],[18.04585,50.01194],[18.00191,50.01723],[17.86886,49.97452],[17.77669,50.02253],[17.7506,50.07896],[17.6888,50.12037],[17.66683,50.10275],[17.59404,50.16437],[17.70528,50.18812],[17.76296,50.23382],[17.72176,50.25665],[17.74648,50.29966],[17.69292,50.32859],[17.67764,50.28977],[17.58889,50.27837],[17.3702,50.28123],[17.34548,50.2628],[17.34273,50.32947],[17.27681,50.32246],[17.19991,50.3654],[17.19579,50.38817],[17.14498,50.38117],[17.1224,50.39494],[16.89229,50.45117],[16.85933,50.41093],[16.90877,50.38642],[16.94448,50.31281],[16.99803,50.30316],[17.02138,50.27772],[16.99803,50.25753],[17.02825,50.23118],[17.00353,50.21449],[16.98018,50.24172],[16.8456,50.20834],[16.7014,50.09659],[16.63137,50.1142],[16.55446,50.16613],[16.56407,50.21009],[16.42674,50.32509],[16.39379,50.3207],[16.3622,50.34875],[16.36495,50.37679],[16.30289,50.38292],[16.28118,50.36891],[16.22821,50.41054],[16.21585,50.40627],[16.19526,50.43291],[16.31413,50.50274],[16.34572,50.49575],[16.44597,50.58041],[16.33611,50.66579],[16.23174,50.67101],[16.20839,50.63096],[16.10265,50.66405],[16.02437,50.60046],[15.98317,50.61528],[16.0175,50.63009],[15.97219,50.69799],[15.87331,50.67188],[15.81683,50.75666],[15.73186,50.73885],[15.43798,50.80833],[15.3803,50.77187],[15.36656,50.83956],[15.2773,50.8907],[15.27043,50.97724],[15.2361,50.99886],[15.1743,50.9833],[15.16744,51.01959],[15.11937,50.99021],[15.10152,51.01095],[15.06218,51.02269],[15.03895,51.0123],[15.02433,51.0242],[14.96419,50.99108],[15.01088,50.97984],[14.99852,50.86817],[14.82803,50.86966],[14.79139,50.81438],[14.70661,50.84096],[14.61993,50.86049],[14.63434,50.8883],[14.65259,50.90513],[14.64802,50.93241],[14.58024,50.91443],[14.56374,50.922],[14.59702,50.96148],[14.59908,50.98685],[14.58215,50.99306],[14.56432,51.01008],[14.53438,51.00374],[14.53321,51.01679],[14.49873,51.02242],[14.50809,51.0427],[14.49991,51.04692],[14.49154,51.04382],[14.49202,51.02286],[14.45827,51.03712],[14.41335,51.02086],[14.30098,51.05515],[14.25665,50.98935],[14.28776,50.97718],[14.32353,50.98556],[14.32793,50.97379],[14.30251,50.96606],[14.31422,50.95243],[14.39848,50.93866],[14.38691,50.89907],[14.30098,50.88448],[14.27123,50.89386],[14.24314,50.88761],[14.22331,50.86049],[14.02982,50.80662],[13.98864,50.8177],[13.89113,50.78533],[13.89444,50.74142],[13.82942,50.7251],[13.76316,50.73487],[13.70204,50.71771],[13.65977,50.73096],[13.52474,50.70394],[13.53748,50.67654],[13.5226,50.64721],[13.49742,50.63133],[13.46413,50.60102],[13.42189,50.61243],[13.37485,50.64931],[13.37805,50.627],[13.32264,50.60317],[13.32594,50.58009],[13.29454,50.57904],[13.25158,50.59268],[13.19043,50.50237],[13.13424,50.51709],[13.08301,50.50132],[13.0312,50.50944],[13.02038,50.4734],[13.02147,50.44763],[12.98433,50.42016],[12.94058,50.40944],[12.82465,50.45738],[12.73476,50.43237],[12.73044,50.42268],[12.70731,50.39948],[12.67261,50.41949],[12.51356,50.39694],[12.48747,50.37278],[12.49214,50.35228],[12.48256,50.34784],[12.46643,50.35527],[12.43722,50.33774],[12.43371,50.32506],[12.39924,50.32302],[12.40158,50.29521],[12.36594,50.28289],[12.35425,50.23993],[12.33263,50.24367],[12.32445,50.20442],[12.33847,50.19432],[12.32596,50.17146],[12.29232,50.17524],[12.28063,50.19544],[12.28755,50.22429],[12.23943,50.24594],[12.24791,50.25525],[12.26953,50.25189],[12.25119,50.27079],[12.20823,50.2729],[12.18013,50.32146],[12.10907,50.32041],[12.13716,50.27396],[12.09287,50.25032]]]}},{"type":"Feature","properties":{"id":"IN-MZ"},"geometry":{"type":"Polygon","coordinates":[[[92.26541,23.70392],[92.38214,23.28705],[92.37665,22.9435],[92.5181,22.71441],[92.60029,22.1522],[92.56616,22.13554],[92.60949,21.97638],[92.67532,22.03547],[92.70416,22.16017],[92.86208,22.05456],[92.89504,21.95143],[92.93899,22.02656],[92.99804,21.98964],[92.99255,22.05965],[93.04885,22.20595],[93.15734,22.18687],[93.14224,22.24535],[93.19991,22.25425],[93.18206,22.43716],[93.13537,22.45873],[93.11477,22.54374],[93.134,22.59573],[93.09417,22.69459],[93.134,22.92498],[93.12988,23.05772],[93.2878,23.00464],[93.38478,23.13698],[93.36862,23.35426],[93.38781,23.36139],[93.39981,23.38828],[93.38805,23.4728],[93.43475,23.68299],[93.3908,23.7622],[93.3908,23.92925],[93.36059,23.93176],[93.32351,24.04468],[92.98416,24.11354],[93.02329,24.40026],[92.8379,24.39588],[92.76168,24.52088],[92.52685,24.16617],[92.44239,24.14299],[92.42317,24.25635],[92.29819,24.25406],[92.31777,23.86543],[92.26541,23.70392]]]}},{"type":"Feature","properties":{"id":"CN-FJ"},"geometry":{"type":"Polygon","coordinates":[[[115.8625,25.22544],[115.88859,24.94123],[115.96618,24.92193],[116.05201,24.85528],[116.20754,24.84718],[116.29886,24.79857],[116.34761,24.86899],[116.40975,24.84313],[116.37062,24.80231],[116.48735,24.67977],[116.51859,24.60332],[116.81076,24.68352],[116.7517,24.5415],[116.89693,24.40025],[116.93023,24.23256],[116.99512,24.18402],[116.91478,24.09097],[116.97933,24.00099],[116.97589,23.95739],[116.96353,23.90655],[117.05863,23.73884],[117.76968,23.10828],[118.41371,24.06775],[118.179,24.33015],[118.09488,24.38193],[118.28244,24.51231],[118.35291,24.51645],[118.42453,24.54644],[118.56434,24.49266],[120.49232,25.22863],[121.03532,26.8787],[120.43281,27.17219],[120.40603,27.20273],[120.42011,27.26531],[120.25909,27.43089],[120.13275,27.42053],[120.03662,27.34371],[119.76985,27.30741],[119.65999,27.5381],[119.61845,27.67988],[119.25247,27.43425],[119.12132,27.44186],[118.89541,27.47294],[118.89678,27.64643],[118.79722,27.94164],[118.71002,27.98773],[118.79035,28.24572],[118.53973,28.28594],[118.47759,28.23876],[118.42121,28.29239],[118.31245,28.22878],[118.36875,28.19369],[118.35296,28.09409],[118.16413,28.05743],[118.08792,27.99167],[117.85274,27.94891],[117.7518,27.83027],[117.67249,27.82268],[117.56332,27.96408],[117.28523,27.87671],[117.31269,27.76801],[117.01469,27.66163],[117.16918,27.2943],[117.05451,27.1062],[116.60476,26.92513],[116.51412,26.70145],[116.64321,26.49024],[116.38572,26.23368],[116.50039,26.15728],[116.36444,25.971],[116.14265,25.87899],[116.00601,25.32789],[115.8625,25.22544]]]}},{"type":"Feature","properties":{"id":"DJ"},"geometry":{"type":"Polygon","coordinates":[[[41.77727,11.49902],[41.8096,11.33606],[41.80056,10.97127],[42.06302,10.92599],[42.13691,10.97586],[42.42669,10.98493],[42.62989,11.09711],[42.75111,11.06992],[42.79037,10.98493],[42.95776,10.98533],[43.42425,11.70983],[43.90659,12.3823],[43.32909,12.59711],[43.29075,12.79154],[42.86195,12.58747],[42.7996,12.42629],[42.6957,12.36201],[42.46941,12.52661],[42.4037,12.46478],[41.95461,11.81157],[41.82878,11.72361],[41.77727,11.49902]]]}},{"type":"Feature","properties":{"id":"DM"},"geometry":{"type":"Polygon","coordinates":[[[-61.81728,15.58058],[-61.51867,14.96709],[-60.69955,15.22234],[-60.95725,15.70997],[-61.44899,15.79571],[-61.81728,15.58058]]]}},{"type":"Feature","properties":{"id":"DO"},"geometry":{"type":"Polygon","coordinates":[[[-72.29523,17.48026],[-71.87936,17.20162],[-68.20301,17.83927],[-67.99519,18.97186],[-70.39828,20.32236],[-72.17094,20.08703],[-71.77419,19.73128],[-71.75865,19.70231],[-71.7429,19.58445],[-71.71449,19.55364],[-71.71268,19.53374],[-71.6802,19.45008],[-71.69448,19.37866],[-71.77766,19.33823],[-71.73229,19.26686],[-71.62642,19.21212],[-71.65337,19.11759],[-71.69938,19.10916],[-71.71088,19.08353],[-71.74088,19.0437],[-71.88102,18.95007],[-71.77766,18.95007],[-71.72624,18.87802],[-71.71885,18.78423],[-71.82556,18.62551],[-71.95412,18.64939],[-72.00201,18.62312],[-71.88102,18.50125],[-71.90875,18.45821],[-71.69952,18.34101],[-71.78271,18.18302],[-71.75465,18.14405],[-71.74994,18.11115],[-71.73783,18.07177],[-71.75671,18.03456],[-72.29523,17.48026]]]}},{"type":"Feature","properties":{"id":"DZ"},"geometry":{"type":"Polygon","coordinates":[[[-8.6715,28.71194],[-8.66879,27.6666],[-8.66674,27.31569],[-4.83423,24.99935],[1.15698,21.12843],[1.20992,20.73533],[3.24648,19.81703],[3.12501,19.1366],[3.36082,18.9745],[4.26651,19.14224],[5.8153,19.45101],[7.38361,20.79165],[7.48273,20.87258],[11.96886,23.51735],[11.62498,24.26669],[11.41061,24.21456],[10.85323,24.5595],[10.33159,24.5465],[10.02432,24.98124],[10.03146,25.35635],[9.38834,26.19288],[9.51696,26.39148],[9.89569,26.57696],[9.78136,29.40961],[9.3876,30.16738],[9.55544,30.23971],[9.07483,32.07865],[8.35999,32.50101],[8.31895,32.83483],[8.1179,33.05086],[8.11433,33.10175],[7.83028,33.18851],[7.73687,33.42114],[7.54088,33.7726],[7.52851,34.06493],[7.66174,34.20167],[7.74207,34.16492],[7.81242,34.21841],[7.86264,34.3987],[8.20482,34.57575],[8.29655,34.72798],[8.25189,34.92009],[8.30727,34.95378],[8.3555,35.10007],[8.47318,35.23376],[8.30329,35.29884],[8.36086,35.47774],[8.35371,35.66373],[8.26472,35.73669],[8.2626,35.91733],[8.40731,36.42208],[8.18936,36.44939],[8.16167,36.48817],[8.47609,36.66607],[8.46537,36.7706],[8.57613,36.78062],[8.67706,36.8364],[8.62972,36.86499],[8.64044,36.9401],[8.59123,37.14286],[2.46645,37.97429],[-2.27707,35.35051],[-2.21248,35.08532],[-2.21445,35.04378],[-2.04734,34.93218],[-1.97833,34.93218],[-1.97469,34.886],[-1.73707,34.74226],[-1.84569,34.61907],[-1.69788,34.48056],[-1.78042,34.39018],[-1.64666,34.10405],[-1.73494,33.71721],[-1.59508,33.59929],[-1.67067,33.27084],[-1.46249,33.0499],[-1.54244,32.95499],[-1.37794,32.73628],[-0.9912,32.52467],[-1.24998,32.32993],[-1.24453,32.1917],[-1.15735,32.12096],[-1.22829,32.07832],[-2.46166,32.16603],[-2.93873,32.06557],[-2.82784,31.79459],[-3.66314,31.6339],[-3.66386,31.39202],[-3.77647,31.31912],[-3.77103,31.14984],[-3.54944,31.0503],[-3.65418,30.85566],[-3.64735,30.67539],[-4.31774,30.53229],[-4.6058,30.28343],[-5.21671,29.95253],[-5.58831,29.48103],[-5.72121,29.52322],[-5.75616,29.61407],[-6.69965,29.51623],[-6.78351,29.44634],[-6.95824,29.50924],[-7.61585,29.36252],[-8.6715,28.71194]]]}},{"type":"Feature","properties":{"id":"CN-TJ"},"geometry":{"type":"Polygon","coordinates":[[[116.70364,38.93751],[116.75514,38.74176],[116.85539,38.74658],[116.87187,38.68658],[117.02567,38.69837],[117.09297,38.58682],[117.21931,38.63189],[117.24128,38.56373],[117.42736,38.61204],[117.59559,38.61472],[118.33648,38.49229],[118.35983,38.73266],[118.02577,39.21789],[118.06079,39.25166],[117.96363,39.31331],[117.84072,39.32712],[117.84656,39.35421],[117.79197,39.36668],[117.85274,39.38685],[117.92861,39.57499],[117.76245,39.59828],[117.71026,39.52787],[117.65945,39.63848],[117.58872,39.73993],[117.54753,39.7584],[117.50427,39.88971],[117.53517,39.98711],[117.78648,39.96659],[117.75558,40.05389],[117.5537,40.22607],[117.34291,40.23131],[117.40608,40.17362],[117.19425,40.07281],[117.13897,39.8797],[117.26154,39.83385],[117.14755,39.81723],[117.19493,39.75999],[117.14137,39.61203],[116.95083,39.65434],[116.89075,39.69714],[116.80732,39.61415],[116.77848,39.46005],[116.87049,39.43354],[116.81899,39.34491],[116.87942,39.34385],[116.85539,39.16467],[116.91581,39.13112],[116.85882,39.05598],[116.75926,39.04372],[116.70364,38.93751]]]}},{"type":"Feature","properties":{"id":"EC"},"geometry":{"type":"Polygon","coordinates":[[[-93.12365,2.64343],[-92.46744,-2.52874],[-84.52388,-3.36941],[-80.30602,-3.39149],[-80.20647,-3.431],[-80.24123,-3.46124],[-80.24475,-3.47846],[-80.24586,-3.48677],[-80.23651,-3.48652],[-80.22629,-3.501],[-80.20535,-3.51667],[-80.21642,-3.5888],[-80.19848,-3.59249],[-80.18741,-3.63994],[-80.19926,-3.68894],[-80.13232,-3.90317],[-80.46386,-4.01342],[-80.4822,-4.05477],[-80.45023,-4.20938],[-80.32114,-4.21323],[-80.46386,-4.41516],[-80.39256,-4.48269],[-80.13945,-4.29786],[-79.79722,-4.47558],[-79.59402,-4.46848],[-79.26248,-4.95167],[-79.1162,-4.97774],[-79.01659,-5.01481],[-78.85149,-4.66795],[-78.68394,-4.60754],[-78.34362,-3.38633],[-78.24589,-3.39907],[-78.22642,-3.51113],[-78.14324,-3.47653],[-78.19369,-3.36431],[-77.94147,-3.05454],[-76.6324,-2.58397],[-76.05203,-2.12179],[-75.57429,-1.55961],[-75.3872,-0.9374],[-75.22862,-0.95588],[-75.22862,-0.60048],[-75.53615,-0.19213],[-75.60169,-0.18708],[-75.61997,-0.10012],[-75.40192,-0.17196],[-75.25764,-0.11943],[-75.82927,0.09578],[-76.23441,0.42294],[-76.41215,0.38228],[-76.4094,0.24015],[-76.89177,0.24736],[-77.52001,0.40782],[-77.49984,0.64476],[-77.67815,0.73863],[-77.66416,0.81604],[-77.68613,0.83029],[-77.7148,0.85003],[-77.85677,0.80197],[-78.42749,1.15389],[-78.87137,1.47457],[-93.12365,2.64343]]]}},{"type":"Feature","properties":{"id":"EG"},"geometry":{"type":"Polygon","coordinates":[[[24.71117,30.17441],[24.99968,29.24574],[24.99885,21.99535],[33.17563,22.00405],[34.0765,22.00501],[37.8565,22.00903],[34.51305,27.70027],[34.46254,27.99552],[34.88293,29.37455],[34.92298,29.45305],[34.26742,31.21998],[34.24012,31.29591],[34.23572,31.2966],[34.21853,31.32363],[34.052,31.46619],[33.62659,31.82938],[25.63787,31.9359],[25.14001,31.67534],[25.06041,31.57937],[24.83101,31.31921],[25.01077,30.73861],[24.71117,30.17441]]]}},{"type":"Feature","properties":{"id":"CN-JL"},"geometry":{"type":"Polygon","coordinates":[[[121.64337,45.73877],[121.74568,45.68267],[121.95098,45.71097],[121.99836,45.6366],[122.01965,45.48324],[122.16522,45.41484],[122.1453,45.2971],[122.08213,44.91327],[122.11715,44.57237],[122.33001,44.22256],[123.143,44.52294],[123.1327,44.34938],[123.37715,44.15215],[123.3229,44.06045],[123.53301,43.64203],[123.32153,43.48979],[123.68682,43.3756],[123.79806,43.49627],[124.14619,43.2412],[124.28077,43.21268],[124.28832,43.14608],[124.41261,43.06738],[124.35836,42.88854],[124.45793,42.81907],[124.85961,43.17563],[124.84863,42.79086],[124.97909,42.77574],[125.18096,42.29813],[125.25443,42.31387],[125.41854,42.09159],[125.28671,41.9554],[125.32241,41.67034],[125.44738,41.67393],[125.49167,41.53222],[125.54283,41.39767],[125.57544,41.39174],[125.63449,41.33325],[125.63724,41.26877],[125.75225,41.23005],[125.78487,41.16185],[125.63552,40.95086],[125.56892,40.89327],[125.66574,40.91403],[125.71172,40.85223],[125.76869,40.87908],[126.00335,40.92835],[126.242,41.15454],[126.53189,41.35206],[126.60631,41.65565],[126.90729,41.79955],[127.17841,41.59714],[127.29712,41.49473],[127.92943,41.44291],[128.02633,41.42103],[128.03311,41.39232],[128.12967,41.37931],[128.18546,41.41279],[128.20061,41.40895],[128.30716,41.60322],[128.15119,41.74568],[128.04487,42.01769],[128.94007,42.03537],[128.96068,42.06657],[129.15178,42.17224],[129.22285,42.26491],[129.22423,42.3553],[129.28541,42.41574],[129.42882,42.44702],[129.54701,42.37254],[129.60482,42.44461],[129.72541,42.43739],[129.75294,42.59409],[129.77183,42.69435],[129.7835,42.76521],[129.80719,42.79218],[129.83277,42.86746],[129.85261,42.96494],[129.8865,43.00395],[129.95082,43.01051],[129.96409,42.97306],[130.12957,42.98361],[130.09764,42.91425],[130.26095,42.9027],[130.23068,42.80125],[130.2385,42.71127],[130.41826,42.6011],[130.44361,42.54849],[130.50123,42.61636],[130.55143,42.52158],[130.62107,42.58413],[130.56576,42.68925],[130.40213,42.70788],[130.44361,42.76205],[130.66524,42.84753],[131.02438,42.86518],[131.02668,42.91246],[131.135,42.94114],[131.10274,43.04734],[131.20414,43.13654],[131.19031,43.21385],[131.30324,43.39498],[131.29402,43.46695],[130.44616,43.63905],[130.38574,44.03824],[130.07537,43.8048],[129.94903,44.06193],[128.88198,43.50374],[128.43429,44.50825],[127.71331,44.03429],[127.53753,44.55525],[127.03765,44.59242],[127.08846,44.93369],[126.65313,45.23621],[126.00768,45.12296],[125.68771,45.50346],[124.55612,45.41002],[124.13452,45.61692],[123.90655,46.28812],[123.17046,46.2283],[122.79418,45.94064],[122.72003,45.70474],[122.52433,45.7771],[122.39868,45.9139],[122.2586,45.79434],[121.8727,46.04083],[121.75735,45.99505],[121.82121,45.8728],[121.64337,45.73877]]]}},{"type":"Feature","properties":{"id":"ER"},"geometry":{"type":"Polygon","coordinates":[[[36.44337,15.14963],[36.54376,14.25597],[36.56536,14.26177],[36.55659,14.28237],[36.63364,14.31172],[36.85787,14.32201],[37.01622,14.2561],[37.09486,14.27155],[37.13206,14.40746],[37.3106,14.44657],[37.47319,14.2149],[37.528,14.18413],[37.91287,14.89447],[38.0364,14.72745],[38.25562,14.67287],[38.3533,14.51323],[38.45748,14.41445],[38.78306,14.4754],[38.98058,14.54895],[39.02834,14.63717],[39.16074,14.65187],[39.14772,14.61827],[39.19547,14.56996],[39.23888,14.56365],[39.26927,14.48801],[39.2302,14.44598],[39.2519,14.40393],[39.37685,14.54402],[39.52756,14.49011],[39.50585,14.55735],[39.58182,14.60987],[39.76632,14.54264],[39.9443,14.41024],[40.07236,14.54264],[40.14649,14.53969],[40.21128,14.39342],[40.25686,14.41445],[40.9167,14.11152],[41.25097,13.60787],[41.62864,13.38626],[42.05841,12.80912],[42.21469,12.75832],[42.2798,12.6355],[42.4037,12.46478],[42.46941,12.52661],[42.6957,12.36201],[42.7996,12.42629],[42.86195,12.58747],[43.29075,12.79154],[42.63806,13.58268],[41.29956,15.565],[41.37609,16.19728],[39.63762,18.37348],[38.57727,17.98125],[38.45916,17.87167],[38.37133,17.66269],[38.13362,17.53906],[37.50967,17.32199],[37.42694,17.04041],[36.99777,17.07172],[36.92193,16.23451],[36.76371,15.80831],[36.69761,15.75323],[36.54276,15.23478],[36.44337,15.14963]]]}},{"type":"Feature","properties":{"id":"EE"},"geometry":{"type":"Polygon","coordinates":[[[19.84909,57.57876],[22.80496,57.87798],[23.20055,57.56697],[24.26221,57.91787],[24.3579,57.87471],[25.19484,58.0831],[25.28237,57.98539],[25.29581,58.08288],[25.73499,57.90193],[26.05949,57.84744],[26.0324,57.79037],[26.02456,57.78342],[26.027,57.78158],[26.0266,57.77441],[26.02069,57.77169],[26.02415,57.76865],[26.03332,57.7718],[26.0543,57.76105],[26.08098,57.76619],[26.2029,57.7206],[26.1866,57.6849],[26.29253,57.59244],[26.46527,57.56885],[26.54675,57.51813],[26.90364,57.62823],[27.34698,57.52242],[27.31919,57.57672],[27.40393,57.62125],[27.3746,57.66834],[27.52615,57.72843],[27.50171,57.78842],[27.56689,57.83356],[27.78526,57.83963],[27.81841,57.89244],[27.67282,57.92627],[27.62393,58.09462],[27.48541,58.22615],[27.55489,58.39525],[27.36366,58.78381],[27.74429,58.98351],[27.80482,59.1116],[27.87978,59.18097],[27.90911,59.24353],[28.00689,59.28351],[28.14215,59.28934],[28.19284,59.35791],[28.20537,59.36491],[28.21137,59.38058],[28.19061,59.39962],[28.04187,59.47017],[27.85643,59.58538],[26.90044,59.63819],[26.32936,60.00121],[20.5104,59.15546],[19.84909,57.57876]]]}},{"type":"Feature","properties":{"id":"ET"},"geometry":{"type":"Polygon","coordinates":[[[33.0006,7.90333],[33.04944,7.78989],[33.24637,7.77939],[33.32531,7.71297],[33.44745,7.7543],[33.71407,7.65983],[33.87642,7.5491],[34.02984,7.36449],[34.03878,7.27437],[34.01495,7.25664],[34.19369,7.12807],[34.19369,7.04382],[34.35753,6.91963],[34.47669,6.91076],[34.53925,6.82794],[34.53776,6.74808],[34.65096,6.72589],[34.77459,6.5957],[34.87736,6.60161],[35.01738,6.46991],[34.96227,6.26415],[35.00546,5.89387],[35.12611,5.68937],[35.13058,5.62118],[35.31188,5.50106],[35.29938,5.34042],[35.50792,5.42431],[35.8576,5.33413],[35.81968,5.10757],[35.82118,4.77382],[35.9419,4.61933],[35.95449,4.53244],[36.03924,4.44406],[36.84474,4.44518],[37.07724,4.33503],[38.14168,3.62487],[38.45812,3.60445],[38.52336,3.62551],[38.91938,3.51198],[39.07736,3.5267],[39.19954,3.47834],[39.49444,3.45521],[39.51551,3.40895],[39.55132,3.39634],[39.58339,3.47434],[39.76808,3.67058],[39.86043,3.86974],[40.77498,4.27683],[41.1754,3.94079],[41.89488,3.97375],[42.07619,4.17667],[42.55853,4.20518],[42.84526,4.28357],[42.97746,4.44032],[43.04177,4.57923],[43.40263,4.79289],[44.02436,4.9451],[44.98104,4.91821],[47.97917,8.00124],[47.92477,8.00111],[46.99339,7.9989],[44.19222,8.93028],[43.32613,9.59205],[43.23518,9.84605],[43.0937,9.90579],[42.87643,10.18441],[42.69452,10.62672],[42.95776,10.98533],[42.79037,10.98493],[42.75111,11.06992],[42.62989,11.09711],[42.42669,10.98493],[42.13691,10.97586],[42.06302,10.92599],[41.80056,10.97127],[41.8096,11.33606],[41.77727,11.49902],[41.82878,11.72361],[41.95461,11.81157],[42.4037,12.46478],[42.2798,12.6355],[42.21469,12.75832],[42.05841,12.80912],[41.62864,13.38626],[41.25097,13.60787],[40.9167,14.11152],[40.25686,14.41445],[40.21128,14.39342],[40.14649,14.53969],[40.07236,14.54264],[39.9443,14.41024],[39.76632,14.54264],[39.58182,14.60987],[39.50585,14.55735],[39.52756,14.49011],[39.37685,14.54402],[39.2519,14.40393],[39.2302,14.44598],[39.26927,14.48801],[39.23888,14.56365],[39.19547,14.56996],[39.14772,14.61827],[39.16074,14.65187],[39.02834,14.63717],[38.98058,14.54895],[38.78306,14.4754],[38.45748,14.41445],[38.3533,14.51323],[38.25562,14.67287],[38.0364,14.72745],[37.91287,14.89447],[37.528,14.18413],[37.47319,14.2149],[37.3106,14.44657],[37.13206,14.40746],[37.09486,14.27155],[37.01622,14.2561],[36.85787,14.32201],[36.63364,14.31172],[36.55659,14.28237],[36.56536,14.26177],[36.54376,14.25597],[36.44653,13.95666],[36.48824,13.83954],[36.38993,13.56459],[36.24545,13.36759],[36.13374,12.92665],[36.16651,12.88019],[36.14268,12.70879],[36.01458,12.72478],[35.70476,12.67101],[35.24302,11.91132],[35.11492,11.85156],[35.05832,11.71158],[35.09556,11.56278],[34.95704,11.24448],[35.01215,11.19626],[34.93172,10.95946],[34.97789,10.91559],[34.97491,10.86147],[34.86916,10.78832],[34.86618,10.74588],[34.77532,10.69027],[34.77383,10.74588],[34.59062,10.89072],[34.4372,10.781],[34.2823,10.53508],[34.34783,10.23914],[34.32102,10.11599],[34.22718,10.02506],[34.20484,9.9033],[34.13186,9.7492],[34.08717,9.55243],[34.10229,9.50238],[34.14304,9.04654],[34.14453,8.60204],[34.01346,8.50041],[33.89579,8.4842],[33.87195,8.41938],[33.71407,8.3678],[33.66938,8.44442],[33.54575,8.47094],[33.3119,8.45474],[33.19721,8.40317],[33.1853,8.29264],[33.18083,8.13047],[33.08401,8.05822],[33.0006,7.90333]]]}},{"type":"Feature","properties":{"id":"FI"},"geometry":{"type":"Polygon","coordinates":[[[19.08191,60.19152],[20.5104,59.15546],[26.32936,60.00121],[27.44953,60.22766],[27.71177,60.3893],[27.77352,60.52722],[28.47974,60.93365],[28.82816,61.1233],[29.01829,61.17448],[31.10136,62.43042],[31.38369,62.66284],[31.58535,62.91642],[31.29294,63.09035],[31.23244,63.22239],[30.49637,63.46666],[29.98213,63.75795],[30.25437,63.83364],[30.55687,64.09036],[30.4762,64.25728],[30.06279,64.35782],[30.01238,64.57513],[30.12329,64.64862],[30.05271,64.79072],[29.68972,64.80789],[29.61914,65.05993],[29.84096,65.1109],[29.8813,65.22101],[29.61914,65.23791],[29.68972,65.31803],[29.84096,65.56945],[29.74013,65.64025],[29.97205,65.70256],[30.16363,65.66935],[29.91155,66.13863],[28.9839,66.94139],[29.91155,67.51507],[30.02041,67.67523],[29.66955,67.79872],[29.34179,68.06655],[28.62982,68.19816],[28.43941,68.53366],[28.78224,68.86696],[28.45957,68.91417],[28.91738,69.04774],[28.81248,69.11997],[28.8629,69.22395],[29.31664,69.47994],[29.12697,69.69193],[28.36883,69.81658],[28.32849,69.88605],[27.97558,69.99671],[27.95542,70.0965],[27.57226,70.06215],[27.05802,69.92069],[26.64461,69.96565],[26.40261,69.91377],[25.96904,69.68397],[25.69679,69.27039],[25.75729,68.99383],[25.61613,68.89602],[25.42455,68.90328],[25.12206,68.78684],[25.10189,68.63307],[24.93048,68.61102],[24.90023,68.55579],[24.74898,68.65143],[24.18432,68.73936],[24.02299,68.81601],[23.781,68.84514],[23.68017,68.70276],[23.13064,68.64684],[22.53321,68.74393],[22.38367,68.71561],[22.27276,68.89514],[21.63833,69.27485],[21.27827,69.31281],[21.00732,69.22755],[20.98641,69.18809],[21.11099,69.10291],[21.05775,69.0356],[20.72171,69.11874],[20.55258,69.06069],[20.78802,69.03087],[20.91658,68.96764],[20.85104,68.93142],[20.90649,68.89696],[21.03001,68.88969],[22.00429,68.50692],[22.73028,68.40881],[23.10336,68.26551],[23.15377,68.14759],[23.26469,68.15134],[23.40081,68.05545],[23.65793,67.9497],[23.45627,67.85297],[23.54701,67.59306],[23.39577,67.46974],[23.75372,67.43688],[23.75372,67.29914],[23.54701,67.25435],[23.58735,67.20752],[23.56214,67.17038],[23.98563,66.84149],[23.98059,66.79585],[23.89488,66.772],[23.85959,66.56434],[23.63776,66.43568],[23.67591,66.3862],[23.64982,66.30603],[23.71339,66.21299],[23.90497,66.15802],[24.15791,65.85385],[24.14798,65.83466],[24.15107,65.81427],[24.14112,65.39731],[20.15877,63.06556],[19.23413,60.61414],[19.08191,60.19152]]]}},{"type":"Feature","properties":{"id":"NC"},"geometry":{"type":"Polygon","coordinates":[[[157.46481,-18.93777],[158.4748,-21.86428],[166.93331,-23.49588],[173.07304,-22.54607],[162.93363,-17.28904],[157.46481,-18.93777]]]}},{"type":"Feature","properties":{"id":"GF"},"geometry":{"type":"Polygon","coordinates":[[[-54.6084,2.32856],[-54.16286,2.10779],[-53.78743,2.34412],[-52.96539,2.1881],[-52.6906,2.37298],[-52.31787,3.17896],[-51.85573,3.83427],[-51.82312,3.85825],[-51.79599,3.89336],[-51.61983,4.14596],[-51.63798,4.51394],[-51.35485,4.8383],[-53.7094,6.2264],[-54.01074,5.68785],[-54.01877,5.52789],[-54.26916,5.26909],[-54.4717,4.91964],[-54.38444,4.13222],[-54.19367,3.84387],[-54.05128,3.63557],[-53.98914,3.627],[-53.9849,3.58697],[-54.28534,2.67798],[-54.42864,2.42442],[-54.6084,2.32856]]]}},{"type":"Feature","properties":{"id":"MQ"},"geometry":{"type":"Polygon","coordinates":[[[-61.51867,14.96709],[-61.26561,14.25664],[-60.5958,14.23076],[-60.69955,15.22234],[-61.51867,14.96709]]]}},{"type":"Feature","properties":{"id":"GP"},"geometry":{"type":"Polygon","coordinates":[[[-62.17275,16.35721],[-61.81728,15.58058],[-61.44899,15.79571],[-60.95725,15.70997],[-60.71337,16.48911],[-61.44461,16.81958],[-61.83929,16.66647],[-62.17275,16.35721]]]}},{"type":"Feature","properties":{"id":"FX"},"geometry":{"type":"Polygon","coordinates":[[[-5.81385,48.52441],[-1.81005,43.59738],[-1.77289,43.38957],[-1.79319,43.37497],[-1.78332,43.36399],[-1.78714,43.35476],[-1.77068,43.34396],[-1.75334,43.34107],[-1.75079,43.3317],[-1.7397,43.32979],[-1.73074,43.29481],[-1.69407,43.31378],[-1.62481,43.30726],[-1.63052,43.28591],[-1.61341,43.25269],[-1.57674,43.25269],[-1.55963,43.28828],[-1.50992,43.29481],[-1.45289,43.27049],[-1.40942,43.27272],[-1.3758,43.24511],[-1.41562,43.12815],[-1.47555,43.08372],[-1.44067,43.047],[-1.35272,43.02658],[-1.34419,43.09665],[-1.32209,43.1127],[-1.27118,43.11961],[-1.30052,43.09581],[-1.30531,43.06859],[-1.25244,43.04164],[-1.22881,43.05534],[-1.10333,43.0059],[-1.00963,42.99279],[-0.97133,42.96239],[-0.81652,42.95166],[-0.75478,42.96916],[-0.72037,42.92541],[-0.73422,42.91228],[-0.72608,42.89318],[-0.69837,42.87945],[-0.67637,42.88303],[-0.55497,42.77846],[-0.50863,42.82713],[-0.44334,42.79939],[-0.41319,42.80776],[-0.38833,42.80132],[-0.3122,42.84788],[-0.17939,42.78974],[-0.16141,42.79535],[-0.10519,42.72761],[-0.02468,42.68513],[0.17569,42.73424],[0.25336,42.7174],[0.29407,42.67431],[0.36251,42.72282],[0.40214,42.69779],[0.67873,42.69458],[0.65421,42.75872],[0.66121,42.84021],[0.711,42.86372],[0.93089,42.79154],[0.96166,42.80629],[0.98292,42.78754],[1.0804,42.78569],[1.15928,42.71407],[1.35562,42.71944],[1.44197,42.60217],[1.47986,42.61346],[1.46718,42.63296],[1.48043,42.65203],[1.50867,42.64483],[1.55418,42.65669],[1.60085,42.62703],[1.63485,42.62957],[1.6625,42.61982],[1.68267,42.62533],[1.73452,42.61515],[1.72588,42.59098],[1.7858,42.57698],[1.73683,42.55492],[1.72515,42.50338],[1.76335,42.48863],[1.83037,42.48395],[1.88853,42.4501],[1.93663,42.45439],[1.94292,42.44316],[1.94061,42.43333],[1.94084,42.43039],[1.9574,42.42401],[1.96482,42.37787],[2.00488,42.35399],[2.06241,42.35906],[2.11621,42.38393],[2.12789,42.41291],[2.16599,42.42314],[2.20578,42.41633],[2.25551,42.43757],[2.38504,42.39977],[2.43299,42.39423],[2.43508,42.37568],[2.48457,42.33933],[2.54382,42.33406],[2.55516,42.35351],[2.57934,42.35808],[2.6747,42.33974],[2.65311,42.38771],[2.72056,42.42298],[2.75497,42.42578],[2.77464,42.41046],[2.84335,42.45724],[2.85675,42.45444],[2.86983,42.46843],[2.88413,42.45938],[2.92107,42.4573],[2.94283,42.48174],[2.96518,42.46692],[3.03734,42.47363],[3.08167,42.42748],[3.10027,42.42621],[3.11379,42.43646],[3.17156,42.43545],[3.4481,42.4358],[7.52234,41.54558],[8.80584,41.26173],[9.28609,41.32097],[9.62656,41.44198],[9.86526,42.21008],[9.56115,43.20816],[7.50102,43.51859],[7.41855,43.72479],[7.40903,43.7296],[7.41113,43.73156],[7.41291,43.73168],[7.41298,43.73311],[7.41233,43.73439],[7.42062,43.73977],[7.42299,43.74176],[7.42443,43.74087],[7.42809,43.74396],[7.43013,43.74895],[7.43624,43.75014],[7.43708,43.75197],[7.4389,43.75151],[7.4379,43.74963],[7.45448,43.7432],[7.53358,43.53609],[7.63035,43.57419],[7.5289,43.78887],[7.50423,43.84345],[7.49355,43.86551],[7.51162,43.88301],[7.56075,43.89932],[7.56858,43.94506],[7.60771,43.95772],[7.65266,43.9763],[7.66848,43.99943],[7.6597,44.03009],[7.72508,44.07578],[7.66878,44.12795],[7.68694,44.17487],[7.63245,44.17877],[7.62155,44.14881],[7.36364,44.11882],[7.34547,44.14359],[7.27827,44.1462],[7.16929,44.20352],[7.00764,44.23736],[6.98221,44.28289],[6.89171,44.36637],[6.88784,44.42043],[6.94504,44.43112],[6.86233,44.49834],[6.85507,44.53072],[6.96042,44.62129],[6.95133,44.66264],[7.00582,44.69364],[7.07484,44.68073],[7.00401,44.78782],[7.02217,44.82519],[6.93499,44.8664],[6.90774,44.84322],[6.75518,44.89915],[6.74519,44.93661],[6.74791,45.01939],[6.66981,45.02324],[6.62803,45.11175],[6.7697,45.16044],[6.85144,45.13226],[6.96706,45.20841],[7.07074,45.21228],[7.13115,45.25386],[7.10572,45.32924],[7.18019,45.40071],[7.00037,45.509],[6.98948,45.63869],[6.80785,45.71864],[6.80785,45.83265],[6.95315,45.85163],[7.04151,45.92435],[7.00946,45.9944],[6.93862,46.06502],[6.87868,46.03855],[6.89321,46.12548],[6.78968,46.14058],[6.86052,46.28512],[6.77152,46.34784],[6.8024,46.39171],[6.82312,46.42661],[6.53358,46.45431],[6.25432,46.3632],[6.21981,46.31304],[6.24826,46.30175],[6.25137,46.29014],[6.23775,46.27822],[6.24952,46.26255],[6.26749,46.24745],[6.29474,46.26221],[6.31041,46.24417],[6.29663,46.22688],[6.27694,46.21566],[6.26007,46.21165],[6.24821,46.20531],[6.23913,46.20511],[6.23544,46.20714],[6.22175,46.20045],[6.22222,46.19888],[6.21844,46.19837],[6.21603,46.19507],[6.21273,46.19409],[6.21114,46.1927],[6.20539,46.19163],[6.19807,46.18369],[6.19552,46.18401],[6.18707,46.17999],[6.18871,46.16644],[6.18116,46.16187],[6.15305,46.15194],[6.13397,46.1406],[6.09926,46.14373],[6.09199,46.15191],[6.07491,46.14879],[6.05203,46.15191],[6.04564,46.14031],[6.03614,46.13712],[6.01791,46.14228],[5.9871,46.14499],[5.97893,46.13303],[5.95781,46.12925],[5.9641,46.14412],[5.97508,46.15863],[5.98188,46.17392],[5.98846,46.17046],[5.99573,46.18587],[5.96515,46.19638],[5.97542,46.21525],[6.02461,46.23313],[6.03342,46.2383],[6.04602,46.23127],[6.05029,46.23518],[6.0633,46.24583],[6.07072,46.24085],[6.08563,46.24651],[6.10071,46.23772],[6.12446,46.25059],[6.11926,46.2634],[6.1013,46.28512],[6.11697,46.29547],[6.1198,46.31157],[6.13876,46.33844],[6.15738,46.3491],[6.16987,46.36759],[6.15985,46.37721],[6.15016,46.3778],[6.09926,46.40768],[6.06407,46.41676],[6.08427,46.44305],[6.07269,46.46244],[6.1567,46.54402],[6.11084,46.57649],[6.27135,46.68251],[6.38351,46.73171],[6.45209,46.77502],[6.43216,46.80336],[6.46456,46.88865],[6.43341,46.92703],[6.71531,47.0494],[6.68823,47.06616],[6.76788,47.1208],[6.8489,47.15933],[6.9508,47.24338],[6.95108,47.26428],[6.94316,47.28747],[7.05305,47.33304],[7.0564,47.35134],[7.03125,47.36996],[6.87959,47.35335],[6.88542,47.37262],[6.93744,47.40714],[6.93953,47.43388],[7.0024,47.45264],[6.98425,47.49432],[7.0231,47.50522],[7.07425,47.48863],[7.12781,47.50371],[7.16249,47.49025],[7.19583,47.49455],[7.17026,47.44312],[7.24669,47.4205],[7.33526,47.44186],[7.35603,47.43432],[7.40308,47.43638],[7.43088,47.45846],[7.4462,47.46264],[7.4583,47.47216],[7.42923,47.48628],[7.43356,47.49712],[7.47534,47.47932],[7.51076,47.49651],[7.49804,47.51798],[7.5229,47.51644],[7.53199,47.5284],[7.51904,47.53515],[7.50588,47.52856],[7.49691,47.53821],[7.50873,47.54546],[7.51723,47.54578],[7.52831,47.55347],[7.53634,47.55553],[7.55652,47.56779],[7.55689,47.57232],[7.56548,47.57617],[7.56684,47.57785],[7.58386,47.57536],[7.58945,47.59017],[7.59301,47.60058],[7.58851,47.60794],[7.57423,47.61628],[7.5591,47.63849],[7.53384,47.65115],[7.52067,47.66437],[7.51915,47.68335],[7.51266,47.70197],[7.53722,47.71635],[7.54761,47.72912],[7.52921,47.77747],[7.55673,47.87371],[7.62302,47.97898],[7.56966,48.03265],[7.57137,48.12292],[7.6648,48.22219],[7.69022,48.30018],[7.74562,48.32736],[7.73109,48.39192],[7.76833,48.48945],[7.80647,48.51239],[7.80167,48.54758],[7.80057,48.5857],[7.84098,48.64217],[7.89002,48.66317],[7.96812,48.72491],[7.96994,48.75606],[8.01534,48.76085],[8.0326,48.79017],[8.06802,48.78957],[8.10253,48.81829],[8.12813,48.87985],[8.19989,48.95825],[8.20031,48.95856],[8.22604,48.97352],[8.14189,48.97833],[7.97783,49.03161],[7.93641,49.05544],[7.86386,49.03499],[7.79557,49.06583],[7.75948,49.04562],[7.63618,49.05428],[7.62575,49.07654],[7.56416,49.08136],[7.53012,49.09818],[7.49172,49.13915],[7.49473,49.17],[7.44455,49.16765],[7.44052,49.18354],[7.3662,49.17308],[7.35995,49.14399],[7.3195,49.14231],[7.29514,49.11426],[7.23473,49.12971],[7.1593,49.1204],[7.1358,49.1282],[7.12504,49.14253],[7.10384,49.13787],[7.10715,49.15631],[7.07859,49.15031],[7.09007,49.13094],[7.07162,49.1255],[7.06642,49.11415],[7.05548,49.11185],[7.04843,49.11422],[7.04409,49.12123],[7.04662,49.13724],[7.03178,49.15734],[7.0274,49.17042],[7.03459,49.19096],[7.01318,49.19018],[6.97273,49.2099],[6.95963,49.203],[6.94028,49.21641],[6.93831,49.2223],[6.91875,49.22261],[6.89298,49.20863],[6.85939,49.22376],[6.83555,49.21249],[6.85119,49.20038],[6.85016,49.19354],[6.86225,49.18185],[6.84703,49.15734],[6.83385,49.15162],[6.78265,49.16793],[6.73765,49.16375],[6.71137,49.18808],[6.73256,49.20486],[6.71843,49.2208],[6.69274,49.21661],[6.66583,49.28065],[6.60186,49.31055],[6.572,49.35027],[6.58807,49.35358],[6.60091,49.36864],[6.533,49.40748],[6.55404,49.42464],[6.42432,49.47683],[6.40274,49.46546],[6.39168,49.4667],[6.38352,49.46463],[6.36778,49.46937],[6.3687,49.4593],[6.28818,49.48465],[6.27875,49.503],[6.25029,49.50609],[6.2409,49.51408],[6.19543,49.50536],[6.17386,49.50934],[6.15366,49.50226],[6.16115,49.49297],[6.14321,49.48796],[6.12814,49.49365],[6.12346,49.4735],[6.10325,49.4707],[6.09845,49.46351],[6.10072,49.45268],[6.08373,49.45594],[6.07887,49.46399],[6.05553,49.46663],[6.04176,49.44801],[6.02743,49.44845],[6.02648,49.45451],[5.97693,49.45513],[5.96876,49.49053],[5.94224,49.49608],[5.94128,49.50034],[5.86571,49.50015],[5.83389,49.52152],[5.83467,49.52717],[5.84466,49.53027],[5.83648,49.5425],[5.81664,49.53775],[5.80871,49.5425],[5.81838,49.54777],[5.79195,49.55228],[5.77435,49.56298],[5.7577,49.55915],[5.75649,49.54321],[5.64505,49.55146],[5.60909,49.51228],[5.55001,49.52729],[5.46541,49.49825],[5.46734,49.52648],[5.43713,49.5707],[5.3974,49.61596],[5.34837,49.62889],[5.33851,49.61599],[5.3137,49.61225],[5.30214,49.63055],[5.33039,49.6555],[5.31465,49.66846],[5.26232,49.69456],[5.14545,49.70287],[5.09249,49.76193],[4.96714,49.79872],[4.85464,49.78995],[4.86965,49.82271],[4.85134,49.86457],[4.88529,49.9236],[4.78827,49.95609],[4.8382,50.06738],[4.88602,50.15182],[4.83279,50.15331],[4.82438,50.16878],[4.75237,50.11314],[4.70064,50.09384],[4.68695,49.99685],[4.5414,49.96911],[4.51098,49.94659],[4.43488,49.94122],[4.35051,49.95315],[4.31963,49.97043],[4.20532,49.95803],[4.14239,49.98034],[4.13508,50.01976],[4.16294,50.04719],[4.23101,50.06945],[4.20147,50.13535],[4.13561,50.13078],[4.16014,50.19239],[4.15524,50.21103],[4.21945,50.25539],[4.20651,50.27333],[4.17861,50.27443],[4.17347,50.28838],[4.15524,50.2833],[4.16808,50.25786],[4.13665,50.25609],[4.11954,50.30425],[4.10957,50.30234],[4.10237,50.31247],[4.0689,50.3254],[4.0268,50.35793],[3.96771,50.34989],[3.90781,50.32814],[3.84314,50.35219],[3.73911,50.34809],[3.70987,50.3191],[3.71009,50.30305],[3.66976,50.34563],[3.65709,50.36873],[3.67262,50.38663],[3.67494,50.40239],[3.66153,50.45165],[3.64426,50.46275],[3.61014,50.49568],[3.58361,50.49049],[3.5683,50.50192],[3.49509,50.48885],[3.51564,50.5256],[3.47385,50.53397],[3.44629,50.51009],[3.37693,50.49538],[3.28575,50.52724],[3.2729,50.60718],[3.23951,50.6585],[3.264,50.67668],[3.2536,50.68977],[3.26141,50.69151],[3.26063,50.70086],[3.24593,50.71389],[3.22042,50.71019],[3.20845,50.71662],[3.19017,50.72569],[3.20064,50.73547],[3.18811,50.74025],[3.18339,50.74981],[3.16476,50.76843],[3.15017,50.79031],[3.1257,50.78603],[3.11987,50.79188],[3.11206,50.79416],[3.10614,50.78303],[3.09163,50.77717],[3.04314,50.77674],[3.00537,50.76588],[2.96778,50.75242],[2.95019,50.75138],[2.90873,50.702],[2.91036,50.6939],[2.90069,50.69263],[2.88504,50.70656],[2.87937,50.70298],[2.86985,50.7033],[2.8483,50.72276],[2.81056,50.71773],[2.71165,50.81295],[2.63331,50.81457],[2.59093,50.91751],[2.63074,50.94746],[2.57551,51.00326],[2.55904,51.07014],[2.18458,51.52087],[1.17405,50.74239],[-2.02963,49.91866],[-2.09454,49.46288],[-1.83944,49.23037],[-2.00491,48.86706],[-2.5234,48.91595],[-2.56423,49.22209],[-2.9511,49.31141],[-5.81385,48.52441]]]}},{"type":"Feature","properties":{"id":"GA"},"geometry":{"type":"Polygon","coordinates":[[[7.24416,-0.64092],[10.75913,-4.39519],[11.12647,-3.94169],[11.22301,-3.69888],[11.48764,-3.51089],[11.57949,-3.52798],[11.68608,-3.68942],[11.87083,-3.71571],[11.92719,-3.62768],[11.8318,-3.5812],[11.96554,-3.30267],[11.70227,-3.17465],[11.70558,-3.0773],[11.80365,-3.00424],[11.64798,-2.81146],[11.5359,-2.85654],[11.64487,-2.61865],[11.57637,-2.33379],[11.74605,-2.39936],[11.96866,-2.33559],[12.04895,-2.41704],[12.47925,-2.32626],[12.44656,-1.92025],[12.61312,-1.8129],[12.82172,-1.91091],[13.02759,-2.33098],[13.47977,-2.43224],[13.75884,-2.09293],[13.92073,-2.35581],[13.85846,-2.46935],[14.10442,-2.49268],[14.23829,-2.33715],[14.16202,-2.23916],[14.23518,-2.15671],[14.25932,-1.97624],[14.41838,-1.89412],[14.52569,-0.57818],[14.41887,-0.44799],[14.2165,-0.38261],[14.06862,-0.20826],[13.90632,-0.2287],[13.88648,0.26652],[14.10909,0.58563],[14.26066,0.57255],[14.48179,0.9152],[14.25186,1.39842],[13.89582,1.4261],[13.15519,1.23368],[13.25447,1.32339],[13.13461,1.57238],[13.29457,2.16106],[13.28534,2.25716],[11.37116,2.29975],[11.3561,2.17217],[11.35307,1.00251],[9.79648,1.0019],[9.78058,1.03996],[9.76085,1.05949],[9.73014,1.06721],[9.68638,1.06836],[9.66092,1.05865],[9.62096,1.03039],[9.54793,1.0185],[9.51998,0.96418],[9.35563,0.84865],[9.00916,0.69445],[7.24416,-0.64092]]]}},{"type":"Feature","properties":{"id":"MS"},"geometry":{"type":"Polygon","coordinates":[[[-62.52079,16.69392],[-62.17275,16.35721],[-61.83929,16.66647],[-62.14123,17.02632],[-62.52079,16.69392]]]}},{"type":"Feature","properties":{"id":"BM"},"geometry":{"type":"Polygon","coordinates":[[[-65.23529,32.66274],[-65.22652,31.98296],[-64.37503,31.99084],[-64.3838,32.67056],[-65.23529,32.66274]]]}},{"type":"Feature","properties":{"id":"GI"},"geometry":{"type":"Polygon","coordinates":[[[-5.40134,36.14896],[-5.39074,36.10278],[-5.36503,36.06205],[-5.32837,36.05935],[-5.3004,36.07439],[-5.28217,36.09907],[-5.27801,36.14942],[-5.33822,36.15272],[-5.34536,36.15501],[-5.36494,36.15496],[-5.38545,36.15481],[-5.40134,36.14896]]]}},{"type":"Feature","properties":{"id":"GE"},"geometry":{"type":"Polygon","coordinates":[[[39.81147,43.06294],[40.89217,41.72528],[41.54366,41.52185],[41.7148,41.4932],[41.7124,41.47417],[41.81939,41.43621],[41.95134,41.52466],[42.26387,41.49346],[42.51772,41.43606],[42.59202,41.58183],[42.72794,41.59714],[42.84471,41.58912],[42.78995,41.50126],[42.84899,41.47265],[42.8785,41.50516],[43.02956,41.37891],[43.21707,41.30331],[43.13373,41.25503],[43.1945,41.25242],[43.23096,41.17536],[43.36118,41.2028],[43.44973,41.17666],[43.4717,41.12611],[43.67712,41.13398],[43.74717,41.1117],[43.84835,41.16329],[44.16591,41.19141],[44.18148,41.24644],[44.32139,41.2079],[44.34337,41.20312],[44.34417,41.2382],[44.46791,41.18204],[44.59322,41.1933],[44.61462,41.24018],[44.72814,41.20338],[44.82084,41.21513],[44.87887,41.20195],[44.89911,41.21366],[44.84358,41.23088],[44.81749,41.23488],[44.80053,41.25949],[44.81437,41.30371],[44.93493,41.25685],[45.0133,41.29747],[45.09867,41.34065],[45.1797,41.42231],[45.26285,41.46433],[45.31352,41.47168],[45.4006,41.42402],[45.45973,41.45898],[45.68389,41.3539],[45.71035,41.36208],[45.75705,41.35157],[45.69946,41.29545],[45.80842,41.2229],[45.95786,41.17956],[46.13221,41.19479],[46.27698,41.19011],[46.37661,41.10805],[46.456,41.09984],[46.48558,41.0576],[46.55096,41.1104],[46.63969,41.09515],[46.66148,41.20533],[46.72375,41.28609],[46.63658,41.37727],[46.4669,41.43331],[46.40307,41.48464],[46.33925,41.4963],[46.29794,41.5724],[46.34126,41.57454],[46.34039,41.5947],[46.3253,41.60912],[46.28182,41.60089],[46.26531,41.63339],[46.24429,41.59883],[46.19759,41.62327],[46.17891,41.72094],[46.20538,41.77205],[46.23962,41.75811],[46.30863,41.79133],[46.3984,41.84399],[46.42738,41.91323],[45.61676,42.20768],[45.78692,42.48358],[45.36501,42.55268],[45.15318,42.70598],[44.88754,42.74934],[44.80941,42.61277],[44.70002,42.74679],[44.54202,42.75699],[43.95517,42.55396],[43.73119,42.62043],[43.81453,42.74297],[43.0419,43.02413],[43.03322,43.08883],[42.75889,43.19651],[42.66667,43.13917],[42.40563,43.23226],[41.64935,43.22331],[40.65957,43.56212],[40.10657,43.57344],[40.04445,43.47776],[40.03312,43.44262],[40.01007,43.42411],[40.01552,43.42025],[40.00853,43.40578],[40.0078,43.38551],[39.81147,43.06294]]]}},{"type":"Feature","properties":{"id":"GH"},"geometry":{"type":"Polygon","coordinates":[[[-3.34019,4.17519],[1.07031,5.15655],[1.27574,5.93551],[1.19771,6.11522],[1.19966,6.17069],[1.09187,6.17074],[1.05969,6.22998],[1.03108,6.24064],[0.99652,6.33779],[0.89283,6.33779],[0.71048,6.53083],[0.74862,6.56517],[0.63659,6.63857],[0.6497,6.73682],[0.58176,6.76049],[0.57406,6.80348],[0.52853,6.82921],[0.56508,6.92971],[0.52098,6.94391],[0.52217,6.9723],[0.59606,7.01252],[0.65327,7.31643],[0.62943,7.41099],[0.57223,7.39326],[0.52455,7.45354],[0.51979,7.58706],[0.58295,7.62368],[0.62943,7.85751],[0.58891,8.12779],[0.6056,8.13959],[0.61156,8.18324],[0.5913,8.19622],[0.63897,8.25873],[0.73432,8.29529],[0.64731,8.48866],[0.47211,8.59945],[0.37319,8.75262],[0.52455,8.87746],[0.45424,9.04581],[0.56388,9.40697],[0.49118,9.48339],[0.36485,9.49749],[0.33148,9.44812],[0.25758,9.42696],[0.2254,9.47869],[0.31241,9.50337],[0.30406,9.521],[0.2409,9.52335],[0.23851,9.57389],[0.38153,9.58682],[0.36008,9.6256],[0.29334,9.59387],[0.26712,9.66437],[0.28261,9.69022],[0.32313,9.6491],[0.34816,9.66907],[0.34816,9.71607],[0.32075,9.72781],[0.36366,10.03309],[0.41252,10.02018],[0.41371,10.06361],[0.35293,10.09412],[0.39584,10.31112],[0.33028,10.30408],[0.29453,10.41546],[0.18846,10.4096],[0.12886,10.53149],[-0.05945,10.63458],[-0.09141,10.7147],[-0.07327,10.71845],[-0.07183,10.76794],[-0.0228,10.81916],[-0.02685,10.8783],[-0.00908,10.91644],[-0.0063,10.96417],[0.03355,10.9807],[0.02395,11.06229],[0.00342,11.08317],[-0.00514,11.10763],[-0.0275,11.11202],[-0.05733,11.08628],[-0.14462,11.10811],[-0.13493,11.14075],[-0.27374,11.17157],[-0.28566,11.12713],[-0.35955,11.07801],[-0.38219,11.12596],[-0.42391,11.11661],[-0.44298,11.04292],[-0.61937,10.91305],[-0.67143,10.99811],[-2.83373,11.0067],[-2.94232,10.64281],[-2.83108,10.40252],[-2.74174,9.83172],[-2.76534,9.56589],[-2.68802,9.49343],[-2.69814,9.22717],[-2.77799,9.04949],[-2.66357,9.01771],[-2.58243,8.7789],[-2.49037,8.20872],[-2.62901,8.11495],[-2.61232,8.02645],[-2.67787,8.02055],[-2.74819,7.92613],[-2.78395,7.94974],[-2.79467,7.86002],[-2.92339,7.60847],[-2.97822,7.27165],[-2.95438,7.23737],[-3.23327,6.81744],[-3.21954,6.74407],[-3.25999,6.62521],[-3.01896,5.71697],[-2.95323,5.71865],[-2.96671,5.6415],[-2.93132,5.62137],[-2.85378,5.65156],[-2.76614,5.60963],[-2.72737,5.34789],[-2.77625,5.34621],[-2.73074,5.1364],[-2.75502,5.10657],[-2.95261,5.12477],[-2.96554,5.10397],[-3.063,5.13665],[-3.11073,5.12675],[-3.10675,5.08515],[-3.34019,4.17519]]]}},{"type":"Feature","properties":{"id":"GN"},"geometry":{"type":"Polygon","coordinates":[[[-15.96748,10.162],[-14.36218,8.64107],[-13.29911,9.04245],[-13.18586,9.0925],[-13.08953,9.0409],[-12.94095,9.26335],[-12.76788,9.3133],[-12.47254,9.86834],[-12.24262,9.92386],[-12.12634,9.87203],[-11.91023,9.93927],[-11.89624,9.99763],[-11.2118,10.00098],[-10.6534,9.29919],[-10.74484,9.07998],[-10.5783,9.06386],[-10.56197,8.81225],[-10.47707,8.67669],[-10.61422,8.5314],[-10.70565,8.29235],[-10.63934,8.35326],[-10.54891,8.31174],[-10.37257,8.48941],[-10.27575,8.48711],[-10.203,8.47991],[-10.14579,8.52665],[-10.05375,8.50697],[-10.05873,8.42578],[-9.77763,8.54633],[-9.47415,8.35195],[-9.50898,8.18455],[-9.41445,8.02448],[-9.44928,7.9284],[-9.35724,7.74111],[-9.37465,7.62032],[-9.48161,7.37122],[-9.41943,7.41809],[-9.305,7.42056],[-9.20798,7.38109],[-9.18311,7.30461],[-9.09107,7.1985],[-8.93435,7.2824],[-8.85724,7.26019],[-8.8448,7.35149],[-8.72789,7.51429],[-8.67814,7.69428],[-8.55874,7.70167],[-8.55874,7.62525],[-8.47114,7.55676],[-8.4003,7.6285],[-8.21374,7.54466],[-8.09931,7.78626],[-8.13414,7.87991],[-8.06449,8.04989],[-7.94695,8.00925],[-7.99919,8.11023],[-7.98675,8.20134],[-8.062,8.16071],[-8.2411,8.24196],[-8.22991,8.48438],[-7.92518,8.50652],[-7.65653,8.36873],[-7.69882,8.66148],[-7.95503,8.81146],[-7.92518,8.99332],[-7.73862,9.08422],[-7.90777,9.20456],[-7.85056,9.41812],[-8.03463,9.39604],[-8.14657,9.55062],[-8.09434,9.86936],[-8.15652,9.94288],[-8.11921,10.04577],[-8.01225,10.1021],[-7.97971,10.17117],[-7.9578,10.2703],[-8.10207,10.44649],[-8.22711,10.41722],[-8.32614,10.69273],[-8.2667,10.91762],[-8.35083,11.06234],[-8.66923,10.99397],[-8.40058,11.37466],[-8.80854,11.66715],[-8.94784,12.34842],[-9.13689,12.50875],[-9.38067,12.48446],[-9.32097,12.29009],[-9.63938,12.18312],[-9.714,12.0226],[-10.30604,12.24634],[-10.71897,11.91552],[-10.80355,12.1053],[-10.99758,12.24634],[-11.24136,12.01286],[-11.50006,12.17826],[-11.37536,12.40788],[-11.46267,12.44559],[-11.91331,12.42008],[-12.35415,12.32758],[-12.87336,12.51892],[-13.06603,12.49342],[-13.05296,12.64003],[-13.70523,12.68013],[-13.7039,12.60313],[-13.65089,12.49515],[-13.64168,12.42764],[-13.70851,12.24978],[-13.92745,12.24077],[-13.94589,12.16869],[-13.7039,12.00869],[-13.7039,11.70195],[-14.09799,11.63649],[-14.26623,11.67486],[-14.31513,11.60713],[-14.51173,11.49708],[-14.66677,11.51188],[-14.77786,11.36323],[-14.95993,10.99244],[-15.07174,10.89557],[-15.96748,10.162]]]}},{"type":"Feature","properties":{"id":"GM"},"geometry":{"type":"Polygon","coordinates":[[[-17.43966,13.04579],[-16.74676,13.06025],[-16.69343,13.16791],[-15.80355,13.16729],[-15.80478,13.34832],[-15.26908,13.37768],[-15.14917,13.57989],[-14.36795,13.23033],[-13.79409,13.34472],[-13.8955,13.59126],[-14.34721,13.46578],[-14.93719,13.80173],[-15.36504,13.79313],[-15.47902,13.58758],[-17.43598,13.59273],[-17.43966,13.04579]]]}},{"type":"Feature","properties":{"id":"GW"},"geometry":{"type":"Polygon","coordinates":[[[-17.4623,11.92379],[-15.96748,10.162],[-15.07174,10.89557],[-14.95993,10.99244],[-14.77786,11.36323],[-14.66677,11.51188],[-14.51173,11.49708],[-14.31513,11.60713],[-14.26623,11.67486],[-14.09799,11.63649],[-13.7039,11.70195],[-13.7039,12.00869],[-13.94589,12.16869],[-13.92745,12.24077],[-13.70851,12.24978],[-13.64168,12.42764],[-13.65089,12.49515],[-13.7039,12.60313],[-13.70523,12.68013],[-15.17582,12.6847],[-15.67302,12.42974],[-16.20591,12.46157],[-16.38191,12.36449],[-16.70562,12.34803],[-17.4623,11.92379]]]}},{"type":"Feature","properties":{"id":"IN-UP"},"geometry":{"type":"Polygon","coordinates":[[[77.10411,29.50415],[77.14187,29.09577],[77.22358,28.89939],[77.21157,28.8573],[77.2229,28.82091],[77.20985,28.81429],[77.20418,28.80527],[77.20659,28.78451],[77.23839,28.75908],[77.24886,28.75524],[77.25512,28.7558],[77.26006,28.75039],[77.25658,28.7444],[77.25547,28.73861],[77.26057,28.73564],[77.27667,28.73564],[77.28688,28.7248],[77.29083,28.72273],[77.2865,28.7143],[77.29036,28.70602],[77.29628,28.70526],[77.29971,28.71008],[77.3134,28.71366],[77.33207,28.71317],[77.32409,28.69864],[77.33345,28.68147],[77.32997,28.67861],[77.32551,28.67827],[77.32027,28.66234],[77.31988,28.65188],[77.31594,28.64152],[77.34087,28.62299],[77.3419,28.60524],[77.33645,28.60174],[77.33066,28.60098],[77.3246,28.59789],[77.31332,28.59661],[77.31049,28.59085],[77.30422,28.58587],[77.29916,28.58772],[77.29293,28.57634],[77.29886,28.55723],[77.34615,28.51651],[77.37447,28.47035],[77.38752,28.46506],[77.41447,28.47465],[77.42477,28.46122],[77.41584,28.44039],[77.43086,28.4259],[77.45704,28.43586],[77.47361,28.41918],[77.46811,28.3997],[77.4979,28.40891],[77.48828,28.35515],[77.46528,28.30831],[77.53532,28.23725],[77.48794,28.19792],[77.54699,28.17613],[77.47283,28.08409],[77.53635,27.99167],[77.52468,27.9204],[77.28126,27.80689],[77.44331,27.3931],[77.6493,27.24303],[77.67127,27.17341],[77.49412,27.08297],[77.75848,27.00469],[77.41722,26.85776],[77.45704,26.74315],[77.78045,26.92819],[78.04275,26.87001],[78.14575,26.94961],[78.26042,26.92941],[78.21029,26.82407],[78.74999,26.77994],[79.01779,26.63518],[79.12902,26.32665],[78.88458,25.90864],[78.74914,25.73589],[78.82003,25.6375],[78.52752,25.57031],[78.44066,25.56133],[78.30642,25.3707],[78.44306,25.12787],[78.34178,25.08155],[78.30642,24.97454],[78.16635,24.87647],[78.28033,24.5671],[78.2666,24.45277],[78.38882,24.26511],[78.51516,24.39025],[78.80767,24.15677],[78.9759,24.35397],[78.98483,24.44527],[78.87634,24.64483],[78.75686,24.60519],[78.77471,24.86027],[78.65558,24.91197],[78.63395,25.0887],[78.55464,25.26984],[78.42109,25.28164],[78.5464,25.3133],[78.52306,25.36419],[78.57868,25.34961],[78.56975,25.39676],[78.71051,25.45102],[78.66588,25.38807],[78.75789,25.33968],[78.81214,25.43056],[78.73146,25.47086],[78.92681,25.56753],[79.04182,25.1459],[79.31373,25.14404],[79.2794,25.35395],[79.47303,25.27512],[79.39269,25.11731],[79.86442,25.1086],[79.86854,25.23786],[80.29014,25.42281],[80.41648,25.1633],[80.25649,25.02401],[80.61561,25.10425],[80.77972,25.05823],[80.71723,25.14342],[80.88958,25.19375],[80.84426,24.99788],[80.80375,24.94154],[81.22741,24.95431],[81.27822,25.16703],[81.4904,25.07938],[81.56112,25.19748],[81.69605,25.03863],[81.73587,25.05574],[81.79389,25.00783],[81.90376,24.99943],[81.90101,24.88768],[81.95869,24.83301],[82.20691,24.78392],[82.30854,24.60831],[82.42561,24.59458],[82.41634,24.70504],[82.55195,24.65575],[82.69374,24.69755],[82.80361,24.55274],[82.71846,24.52713],[82.71606,24.37305],[82.76618,24.3754],[82.77854,24.29312],[82.71915,24.13876],[82.6752,24.16695],[82.66216,24.12513],[82.70267,24.09693],[82.79708,24.00319],[82.80876,23.96492],[82.95226,23.87642],[83.15963,23.90467],[83.32134,24.10131],[83.4027,24.26793],[83.37387,24.3155],[83.46347,24.36992],[83.39275,24.50027],[83.49883,24.52651],[83.5033,24.73747],[83.39824,24.78735],[83.34125,25.0125],[83.34897,25.17744],[83.41335,25.24966],[83.4621,25.25152],[83.49077,25.28614],[83.64852,25.34464],[83.65917,25.36745],[83.74242,25.40792],[83.76422,25.3859],[83.84593,25.43645],[83.81452,25.45459],[83.871,25.49333],[83.88061,25.51765],[84.05845,25.64833],[84.0921,25.72908],[84.32899,25.70588],[84.38804,25.76959],[84.4749,25.68485],[84.66613,25.74022],[84.54082,25.85737],[84.27955,25.94538],[84.15183,26.03334],[84.02309,26.13848],[84.01245,26.23676],[84.17106,26.26139],[84.17243,26.37495],[83.90258,26.44905],[83.87992,26.5225],[84.05296,26.54891],[84.1254,26.6318],[84.39594,26.61554],[84.30599,26.75082],[84.23973,26.86511],[84.02687,27.08847],[83.83048,27.29552],[83.85595,27.35797],[83.61288,27.47013],[83.39495,27.4798],[83.38872,27.39276],[83.35136,27.33885],[83.29999,27.32778],[83.2673,27.36235],[83.27197,27.38309],[83.19413,27.45632],[82.94938,27.46036],[82.93261,27.50328],[82.74119,27.49838],[82.70378,27.72122],[82.46405,27.6716],[82.06554,27.92222],[81.97214,27.93322],[81.91223,27.84995],[81.47867,28.08303],[81.48179,28.12148],[81.38683,28.17638],[81.32923,28.13521],[81.19847,28.36284],[81.08507,28.38346],[80.89648,28.47237],[80.55142,28.69182],[80.50575,28.6706],[80.52443,28.54897],[80.44504,28.63098],[80.37188,28.63371],[80.12125,28.82346],[80.06957,28.82763],[79.96673,28.70233],[79.79026,28.88736],[79.66529,28.85369],[79.41089,28.85339],[79.40711,28.92854],[79.29416,28.95858],[79.0686,29.15276],[79.00268,29.12127],[78.71429,29.32053],[78.91067,29.45335],[78.52683,29.6248],[78.49044,29.73874],[78.33732,29.79536],[77.98507,29.5418],[77.94181,29.71489],[77.80792,29.67075],[77.70629,29.8722],[77.92945,30.24661],[77.5542,30.40278],[77.58613,30.31006],[77.34443,30.06315],[77.12882,29.75305],[77.10411,29.50415]]]}},{"type":"Feature","properties":{"id":"GQ"},"geometry":{"type":"Polygon","coordinates":[[[5.37613,-1.68343],[5.85762,-1.69667],[7.24416,-0.64092],[9.00916,0.69445],[9.35563,0.84865],[9.51998,0.96418],[9.54793,1.0185],[9.62096,1.03039],[9.66092,1.05865],[9.68638,1.06836],[9.73014,1.06721],[9.76085,1.05949],[9.78058,1.03996],[9.79648,1.0019],[11.35307,1.00251],[11.3561,2.17217],[9.991,2.16561],[9.90749,2.20049],[9.89012,2.20457],[9.84716,2.24676],[9.83238,2.29079],[9.83754,2.32428],[9.82123,2.35097],[9.81162,2.33797],[9.6225,2.44901],[9.22018,3.72052],[8.6479,4.06346],[8.05799,3.48284],[8.0168,1.79377],[6.69416,-0.53945],[5.87114,-1.20569],[5.38965,-1.19244],[5.37613,-1.68343]]]}},{"type":"Feature","properties":{"id":"GT"},"geometry":{"type":"Polygon","coordinates":[[[-92.37213,14.39277],[-90.55276,12.8866],[-90.11344,13.73679],[-90.10505,13.85104],[-89.88937,14.0396],[-89.81807,14.07073],[-89.76103,14.02923],[-89.73251,14.04133],[-89.75569,14.07073],[-89.70756,14.1537],[-89.61844,14.21937],[-89.52397,14.22628],[-89.50614,14.26084],[-89.58814,14.33165],[-89.57441,14.41637],[-89.39028,14.44561],[-89.34776,14.43013],[-89.35189,14.47553],[-89.23719,14.58046],[-89.15653,14.57802],[-89.13132,14.71582],[-89.23467,14.85596],[-89.15149,14.97775],[-89.18048,14.99967],[-89.15149,15.07392],[-88.97343,15.14039],[-88.32467,15.63665],[-88.31459,15.66942],[-88.24022,15.69247],[-88.22552,15.72294],[-88.20359,16.03858],[-88.40779,16.09624],[-88.95358,15.88698],[-89.02415,15.9063],[-89.17418,15.90898],[-89.22683,15.88619],[-89.15025,17.04813],[-89.14985,17.81563],[-90.98678,17.81655],[-90.99199,17.25192],[-91.43809,17.25373],[-91.04436,16.92175],[-90.69064,16.70697],[-90.61212,16.49832],[-90.40499,16.40524],[-90.44567,16.07573],[-91.73182,16.07371],[-92.20983,15.26077],[-92.0621,15.07406],[-92.1454,14.98143],[-92.1423,14.88647],[-92.18161,14.84147],[-92.1454,14.6804],[-92.2261,14.53423],[-92.37213,14.39277]]]}},{"type":"Feature","properties":{"id":"GY"},"geometry":{"type":"Polygon","coordinates":[[[-61.4041,5.95304],[-60.73204,5.20931],[-60.32352,5.21299],[-60.20944,5.28754],[-59.98129,5.07097],[-60.04189,4.69801],[-60.15953,4.53456],[-59.78878,4.45637],[-59.69361,4.34069],[-59.73353,4.20399],[-59.51963,3.91951],[-59.86899,3.57089],[-59.79769,3.37162],[-59.99733,2.92312],[-59.91177,2.36759],[-59.7264,2.27497],[-59.74066,1.87596],[-59.25583,1.40559],[-58.92072,1.31293],[-58.84229,1.17749],[-58.53571,1.29154],[-58.4858,1.48399],[-58.33887,1.58014],[-58.01873,1.51966],[-57.97336,1.64566],[-57.77281,1.73344],[-57.55743,1.69605],[-57.35073,1.98327],[-57.23981,1.95808],[-57.09109,2.01854],[-57.07092,1.95304],[-56.7659,1.89509],[-56.47045,1.95135],[-56.55439,2.02003],[-56.70519,2.02964],[-57.35891,3.32121],[-58.0307,3.95513],[-57.8699,4.89394],[-57.37442,5.0208],[-57.22536,5.15605],[-57.31629,5.33714],[-56.84822,6.73257],[-59.54058,8.6862],[-59.98508,8.53046],[-59.85562,8.35213],[-59.80661,8.28906],[-59.83156,8.23261],[-59.97059,8.20791],[-60.02407,8.04557],[-60.38056,7.8302],[-60.51959,7.83373],[-60.64793,7.56877],[-60.71923,7.55817],[-60.59802,7.33194],[-60.63367,7.25061],[-60.54098,7.14804],[-60.44116,7.20817],[-60.28074,7.1162],[-60.39419,6.94847],[-60.54873,6.8631],[-61.13632,6.70922],[-61.20762,6.58174],[-61.15058,6.19558],[-61.4041,5.95304]]]}},{"type":"Feature","properties":{"id":"HN"},"geometry":{"type":"Polygon","coordinates":[[[-89.35189,14.47553],[-89.34776,14.43013],[-89.04187,14.33644],[-88.94608,14.20207],[-88.85785,14.17763],[-88.815,14.11652],[-88.73182,14.10919],[-88.70661,14.04317],[-88.49738,13.97224],[-88.48982,13.86458],[-88.25791,13.91108],[-88.23018,13.99915],[-88.07641,13.98447],[-88.00331,13.86948],[-87.7966,13.91353],[-87.68821,13.80829],[-87.73106,13.75443],[-87.78148,13.52906],[-87.71657,13.50577],[-87.72115,13.46083],[-87.73841,13.44169],[-87.77354,13.45767],[-87.83467,13.44655],[-87.84675,13.41078],[-87.80177,13.35689],[-87.73714,13.32715],[-87.69751,13.25228],[-87.55124,13.12523],[-87.37107,12.98646],[-87.06306,13.00892],[-87.03785,12.98682],[-86.93197,13.05313],[-86.93383,13.18677],[-86.87066,13.30641],[-86.71267,13.30348],[-86.76812,13.79605],[-86.35219,13.77157],[-86.14801,14.04317],[-86.00685,14.08474],[-86.03458,13.99181],[-85.75477,13.8499],[-85.73964,13.9698],[-85.45762,14.11304],[-85.32149,14.2562],[-85.18602,14.24929],[-85.1575,14.53934],[-84.90082,14.80489],[-84.82596,14.82212],[-84.70119,14.68078],[-84.48373,14.63249],[-84.10584,14.76353],[-83.89551,14.76697],[-83.62101,14.89448],[-83.49268,15.01158],[-83.13724,15.00002],[-83.04763,15.03256],[-82.06974,14.49418],[-81.80642,15.86201],[-83.86109,17.73736],[-88.20359,16.03858],[-88.22552,15.72294],[-88.24022,15.69247],[-88.31459,15.66942],[-88.32467,15.63665],[-88.97343,15.14039],[-89.15149,15.07392],[-89.18048,14.99967],[-89.15149,14.97775],[-89.23467,14.85596],[-89.13132,14.71582],[-89.15653,14.57802],[-89.23719,14.58046],[-89.35189,14.47553]]]}},{"type":"Feature","properties":{"id":"HR"},"geometry":{"type":"Polygon","coordinates":[[[13.05142,45.33128],[13.12821,44.48877],[16.15283,42.18525],[18.45131,42.21682],[18.54128,42.39171],[18.52152,42.42302],[18.43588,42.48556],[18.44307,42.51077],[18.43735,42.55921],[18.36197,42.61423],[18.24318,42.6112],[17.88201,42.83668],[17.80854,42.9182],[17.7948,42.89556],[17.68151,42.92725],[17.6444,42.88641],[17.5392,42.92787],[17.70879,42.97223],[17.64268,43.08595],[17.46986,43.16559],[17.286,43.33065],[17.25579,43.40353],[17.29699,43.44542],[17.24411,43.49376],[17.15828,43.49376],[17.00585,43.58037],[16.80736,43.76011],[16.75316,43.77157],[16.70922,43.84887],[16.55472,43.95326],[16.50528,44.0244],[16.43629,44.02826],[16.43662,44.07523],[16.36864,44.08263],[16.18688,44.27012],[16.21346,44.35231],[16.12969,44.38275],[16.16814,44.40679],[16.10566,44.52586],[16.03012,44.55572],[16.00884,44.58605],[16.05828,44.61538],[15.89348,44.74964],[15.8255,44.71501],[15.72584,44.82334],[15.79472,44.8455],[15.76096,44.87045],[15.74723,44.96818],[15.78568,44.97401],[15.74585,45.0638],[15.78842,45.11519],[15.76371,45.16508],[15.83512,45.22459],[15.98412,45.23088],[16.12153,45.09616],[16.29036,44.99732],[16.35404,45.00241],[16.35863,45.03529],[16.3749,45.05206],[16.38219,45.05139],[16.38309,45.05955],[16.40023,45.1147],[16.4634,45.14522],[16.49155,45.21153],[16.52982,45.22713],[16.5501,45.2212],[16.56559,45.22307],[16.60194,45.23042],[16.64962,45.20714],[16.74845,45.20393],[16.78219,45.19002],[16.81137,45.18434],[16.83804,45.18951],[16.92405,45.27607],[16.9385,45.22742],[17.0415,45.20759],[17.18438,45.14764],[17.24325,45.146],[17.25131,45.14957],[17.26815,45.18444],[17.32092,45.16246],[17.33573,45.14521],[17.41229,45.13335],[17.4498,45.16119],[17.45615,45.12523],[17.47589,45.12656],[17.51469,45.10791],[17.59104,45.10816],[17.66571,45.13408],[17.84826,45.04489],[17.87148,45.04645],[17.93706,45.08016],[17.97336,45.12245],[17.97834,45.13831],[17.99479,45.14958],[18.01594,45.15163],[18.03121,45.12632],[18.1624,45.07654],[18.24387,45.13699],[18.32077,45.1021],[18.41896,45.11083],[18.47939,45.05871],[18.65723,45.07544],[18.78357,44.97741],[18.80661,44.93561],[18.76369,44.93707],[18.76347,44.90669],[18.8704,44.85097],[19.01994,44.85493],[18.98957,44.90645],[19.02871,44.92541],[19.06853,44.89915],[19.15573,44.95409],[19.05205,44.97692],[19.1011,45.01191],[19.07952,45.14668],[19.14063,45.12972],[19.19144,45.17863],[19.43589,45.17137],[19.41941,45.23475],[19.28208,45.23813],[19.10774,45.29547],[18.97446,45.37528],[18.99918,45.49333],[19.08364,45.48804],[19.07471,45.53086],[18.94562,45.53712],[18.88776,45.57253],[18.96691,45.66731],[18.90305,45.71863],[18.85783,45.85493],[18.81394,45.91329],[18.80211,45.87995],[18.6792,45.92057],[18.57483,45.80772],[18.44368,45.73972],[18.12439,45.78905],[18.08869,45.76511],[17.99805,45.79671],[17.87377,45.78522],[17.66545,45.84207],[17.56821,45.93728],[17.35672,45.95209],[17.14592,46.16697],[16.8903,46.28122],[16.8541,46.36255],[16.7154,46.39523],[16.6639,46.45203],[16.59527,46.47524],[16.52604,46.47831],[16.5007,46.49644],[16.44036,46.5171],[16.38771,46.53608],[16.37193,46.55008],[16.29793,46.5121],[16.26733,46.51505],[16.26759,46.50566],[16.23961,46.49653],[16.25124,46.48067],[16.27398,46.42875],[16.27329,46.41467],[16.30162,46.40437],[16.30233,46.37837],[16.18824,46.38282],[16.14859,46.40547],[16.05281,46.39141],[16.05065,46.3833],[16.07314,46.36458],[16.07616,46.3463],[15.97965,46.30652],[15.79284,46.25811],[15.78817,46.21719],[15.75479,46.20336],[15.75436,46.21969],[15.67395,46.22478],[15.6434,46.21396],[15.64904,46.19229],[15.59909,46.14761],[15.6083,46.11992],[15.62317,46.09103],[15.72977,46.04682],[15.71246,46.01196],[15.70327,46.00015],[15.70636,45.92116],[15.67967,45.90455],[15.68383,45.88867],[15.68232,45.86819],[15.70411,45.8465],[15.66662,45.84085],[15.64185,45.82915],[15.57952,45.84953],[15.52234,45.82195],[15.47325,45.8253],[15.47531,45.79802],[15.40836,45.79491],[15.25423,45.72275],[15.30872,45.69014],[15.34919,45.71623],[15.4057,45.64727],[15.38952,45.63682],[15.34214,45.64702],[15.34695,45.63382],[15.31027,45.6303],[15.27747,45.60504],[15.29837,45.5841],[15.30249,45.53224],[15.38188,45.48752],[15.33051,45.45258],[15.27758,45.46678],[15.16862,45.42309],[15.05187,45.49079],[15.02385,45.48533],[14.92266,45.52788],[14.90554,45.47769],[14.81992,45.45913],[14.80124,45.49515],[14.71718,45.53442],[14.68605,45.53006],[14.69694,45.57366],[14.59576,45.62812],[14.60977,45.66403],[14.57397,45.67165],[14.53816,45.6205],[14.5008,45.60852],[14.49769,45.54424],[14.36693,45.48642],[14.32487,45.47142],[14.27681,45.4902],[14.26611,45.48239],[14.24239,45.50607],[14.22371,45.50388],[14.20348,45.46896],[14.07116,45.48752],[14.00578,45.52352],[13.96063,45.50825],[13.99488,45.47551],[13.97309,45.45258],[13.90771,45.45149],[13.88124,45.42637],[13.81742,45.43729],[13.7785,45.46787],[13.67398,45.4436],[13.62902,45.45898],[13.56979,45.4895],[13.45644,45.59464],[13.05142,45.33128]]]}},{"type":"Feature","properties":{"id":"HU"},"geometry":{"type":"Polygon","coordinates":[[[16.10983,46.867],[16.14365,46.8547],[16.15711,46.85434],[16.21892,46.86961],[16.2365,46.87775],[16.2941,46.87137],[16.34547,46.83836],[16.3408,46.80641],[16.31303,46.79838],[16.30966,46.7787],[16.37816,46.69975],[16.42641,46.69228],[16.41863,46.66238],[16.38594,46.6549],[16.39217,46.63673],[16.50139,46.56684],[16.52885,46.53303],[16.52604,46.5051],[16.59527,46.47524],[16.6639,46.45203],[16.7154,46.39523],[16.8541,46.36255],[16.8903,46.28122],[17.14592,46.16697],[17.35672,45.95209],[17.56821,45.93728],[17.66545,45.84207],[17.87377,45.78522],[17.99805,45.79671],[18.08869,45.76511],[18.12439,45.78905],[18.44368,45.73972],[18.57483,45.80772],[18.6792,45.92057],[18.80211,45.87995],[18.81394,45.91329],[18.99712,45.93537],[19.01284,45.96529],[19.0791,45.96458],[19.10388,46.04015],[19.14543,45.9998],[19.28826,45.99694],[19.52473,46.1171],[19.56113,46.16824],[19.66007,46.19005],[19.81491,46.1313],[19.93508,46.17553],[20.01816,46.17696],[20.03533,46.14509],[20.09713,46.17315],[20.26068,46.12332],[20.28324,46.1438],[20.35573,46.16629],[20.45377,46.14405],[20.49718,46.18721],[20.63863,46.12728],[20.76085,46.21002],[20.74574,46.25467],[20.86797,46.28884],[21.06572,46.24897],[21.16872,46.30118],[21.28061,46.44941],[21.26929,46.4993],[21.33214,46.63035],[21.43926,46.65109],[21.5151,46.72147],[21.48935,46.7577],[21.52028,46.84118],[21.59307,46.86935],[21.59581,46.91628],[21.68645,46.99595],[21.648,47.03902],[21.78395,47.11104],[21.94463,47.38046],[22.01055,47.37767],[22.03389,47.42508],[22.00917,47.50492],[22.31816,47.76126],[22.41979,47.7391],[22.46559,47.76583],[22.67247,47.7871],[22.76617,47.8417],[22.77991,47.87211],[22.89849,47.95851],[22.84276,47.98602],[22.87847,48.04665],[22.81804,48.11363],[22.73427,48.12005],[22.66835,48.09162],[22.58733,48.10813],[22.59007,48.15121],[22.49806,48.25189],[22.38133,48.23726],[22.2083,48.42534],[22.14689,48.4005],[21.83339,48.36242],[21.8279,48.33321],[21.72525,48.34628],[21.67134,48.3989],[21.6068,48.50365],[21.44063,48.58456],[21.11516,48.49546],[20.83248,48.5824],[20.5215,48.53336],[20.29943,48.26104],[20.24312,48.2784],[19.92452,48.1283],[19.63338,48.25006],[19.52489,48.19791],[19.47957,48.09437],[19.28182,48.08336],[19.23924,48.0595],[19.01952,48.07052],[18.82176,48.04206],[18.76134,47.97499],[18.76821,47.87469],[18.8506,47.82308],[18.74074,47.8157],[18.66521,47.76772],[18.56496,47.76588],[18.29305,47.73541],[18.02938,47.75665],[17.71215,47.7548],[17.23699,48.02094],[17.16001,48.00636],[17.09786,47.97336],[17.11022,47.92461],[17.08275,47.87719],[17.00997,47.86245],[17.07039,47.81129],[17.05048,47.79377],[17.08893,47.70928],[16.87538,47.68895],[16.86509,47.72268],[16.82938,47.68432],[16.7511,47.67878],[16.72089,47.73469],[16.65679,47.74197],[16.61183,47.76171],[16.54779,47.75074],[16.53514,47.73837],[16.55129,47.72268],[16.4222,47.66537],[16.58699,47.61772],[16.64193,47.63114],[16.71059,47.52692],[16.64821,47.50155],[16.6718,47.46139],[16.57152,47.40868],[16.52414,47.41007],[16.49908,47.39416],[16.45104,47.41181],[16.47782,47.25918],[16.44142,47.25079],[16.43663,47.21127],[16.41739,47.20649],[16.42801,47.18422],[16.4523,47.18812],[16.46442,47.16845],[16.44932,47.14418],[16.52863,47.13974],[16.46134,47.09395],[16.52176,47.05747],[16.43936,47.03548],[16.51369,47.00084],[16.28202,47.00159],[16.27594,46.9643],[16.22403,46.939],[16.19904,46.94134],[16.10983,46.867]]]}},{"type":"Feature","properties":{"id":"IN"},"geometry":{"type":"Polygon","coordinates":[[[68.11329,23.53945],[68.83233,21.42207],[71.96044,11.88348],[72.15131,7.6285],[74.03744,7.70688],[76.72283,7.82138],[77.66782,7.85769],[79.37385,8.98767],[79.45362,9.159],[79.42124,9.80115],[80.48418,10.20786],[93.82619,5.95573],[94.98735,6.60903],[94.6395,14.00732],[93.69443,13.6468],[92.61282,13.95915],[89.13606,21.42955],[89.13927,21.60785],[89.03553,21.77397],[89.07114,22.15335],[88.9367,22.58527],[88.94614,22.66941],[88.9151,22.75228],[88.96713,22.83346],[88.87063,22.95235],[88.88327,23.03885],[88.86377,23.08759],[88.99148,23.21134],[88.71133,23.2492],[88.79254,23.46028],[88.79351,23.50535],[88.74841,23.47361],[88.56507,23.64044],[88.58087,23.87105],[88.66189,23.87607],[88.73743,23.91751],[88.6976,24.14703],[88.74841,24.1959],[88.68801,24.31464],[88.50934,24.32474],[88.12296,24.51301],[88.08786,24.63232],[88.00683,24.66477],[88.15515,24.85806],[88.14004,24.93529],[88.21832,24.96642],[88.27325,24.88796],[88.33917,24.86803],[88.46277,25.07468],[88.44766,25.20149],[88.94067,25.18534],[89.00463,25.26583],[89.01105,25.30303],[88.85278,25.34679],[88.81296,25.51546],[88.677,25.46959],[88.4559,25.59227],[88.45103,25.66245],[88.242,25.80811],[88.13138,25.78773],[88.08804,25.91334],[88.16581,26.0238],[88.1844,26.14417],[88.34757,26.22216],[88.35153,26.29123],[88.51649,26.35923],[88.48749,26.45855],[88.36938,26.48683],[88.35153,26.45241],[88.33093,26.48929],[88.41196,26.63837],[88.4298,26.54489],[88.62144,26.46783],[88.69485,26.38353],[88.67837,26.26291],[88.78961,26.31093],[88.85004,26.23211],[89.05328,26.2469],[88.91321,26.37984],[88.92357,26.40711],[88.95612,26.4564],[89.08899,26.38845],[89.15869,26.13708],[89.35953,26.0077],[89.53515,26.00382],[89.57101,25.9682],[89.63968,26.22595],[89.70201,26.15138],[89.73581,26.15818],[89.77865,26.08387],[89.77728,26.04254],[89.86592,25.93115],[89.80585,25.82489],[89.84388,25.70042],[89.86129,25.61714],[89.81208,25.37244],[89.84086,25.31854],[89.83371,25.29548],[89.87629,25.28337],[89.90478,25.31038],[90.1155,25.22686],[90.40034,25.1534],[90.65042,25.17788],[90.87427,25.15799],[91.25517,25.20677],[91.63648,25.12846],[92.0316,25.1834],[92.33957,25.07593],[92.39147,25.01471],[92.49887,24.88796],[92.38626,24.86055],[92.25854,24.9191],[92.15796,24.54435],[92.11662,24.38997],[91.96603,24.3799],[91.89258,24.14674],[91.82596,24.22345],[91.76004,24.23848],[91.73257,24.14703],[91.65292,24.22095],[91.63782,24.1132],[91.55542,24.08687],[91.37414,24.10693],[91.35741,23.99072],[91.29587,24.0041],[91.22308,23.89616],[91.25192,23.83463],[91.15579,23.6599],[91.28293,23.37538],[91.36453,23.06612],[91.40848,23.07117],[91.4035,23.27522],[91.46615,23.2328],[91.54993,23.01051],[91.61571,22.93929],[91.7324,23.00043],[91.81634,23.08001],[91.76417,23.26619],[91.84789,23.42235],[91.95642,23.47361],[91.95093,23.73284],[92.04706,23.64229],[92.15417,23.73409],[92.26541,23.70392],[92.38214,23.28705],[92.37665,22.9435],[92.5181,22.71441],[92.60029,22.1522],[92.56616,22.13554],[92.60949,21.97638],[92.67532,22.03547],[92.70416,22.16017],[92.86208,22.05456],[92.89504,21.95143],[92.93899,22.02656],[92.99804,21.98964],[92.99255,22.05965],[93.04885,22.20595],[93.15734,22.18687],[93.14224,22.24535],[93.19991,22.25425],[93.18206,22.43716],[93.13537,22.45873],[93.11477,22.54374],[93.134,22.59573],[93.09417,22.69459],[93.134,22.92498],[93.12988,23.05772],[93.2878,23.00464],[93.38478,23.13698],[93.36862,23.35426],[93.38781,23.36139],[93.39981,23.38828],[93.38805,23.4728],[93.43475,23.68299],[93.3908,23.7622],[93.3908,23.92925],[93.36059,23.93176],[93.32351,24.04468],[93.34735,24.10151],[93.41415,24.07854],[93.46633,23.97067],[93.50616,23.94432],[93.62871,24.00922],[93.75952,24.0003],[93.80279,23.92549],[93.92089,23.95812],[94.14081,23.83333],[94.30215,24.23752],[94.32362,24.27692],[94.45279,24.56656],[94.50729,24.59281],[94.5526,24.70764],[94.60204,24.70889],[94.73937,25.00545],[94.74212,25.13606],[94.57458,25.20318],[94.68032,25.47003],[94.80117,25.49359],[95.18556,26.07338],[95.11428,26.1019],[95.12801,26.38397],[95.05798,26.45408],[95.23513,26.68499],[95.30339,26.65372],[95.437,26.7083],[95.81603,27.01335],[95.93002,27.04149],[96.04949,27.19428],[96.15591,27.24572],[96.40779,27.29818],[96.55761,27.29928],[96.73888,27.36638],[96.88445,27.25046],[96.85287,27.2065],[96.89132,27.17474],[97.14675,27.09041],[97.17422,27.14052],[96.91431,27.45752],[96.90112,27.62149],[97.29919,27.92233],[97.35824,27.87256],[97.38845,28.01329],[97.35412,28.06663],[97.31292,28.06784],[97.34547,28.21385],[97.1289,28.3619],[96.98882,28.32564],[96.88445,28.39452],[96.85561,28.4875],[96.6455,28.61657],[96.48895,28.42955],[96.40929,28.51526],[96.61391,28.72742],[96.3626,29.10607],[96.20467,29.02325],[96.18682,29.11087],[96.31316,29.18643],[96.05361,29.38167],[95.84899,29.31464],[95.75149,29.32063],[95.72086,29.20797],[95.50842,29.13487],[95.41091,29.13007],[95.3038,29.13847],[95.26122,29.07727],[95.2214,29.10727],[95.11291,29.09527],[95.0978,29.14446],[94.81353,29.17804],[94.69318,29.31739],[94.2752,29.11687],[94.35897,29.01965],[93.72797,28.68821],[93.44621,28.67189],[93.18069,28.50319],[93.14635,28.37035],[92.93075,28.25671],[92.67486,28.15018],[92.65472,28.07632],[92.73025,28.05814],[92.7275,27.98662],[92.42538,27.80092],[92.32101,27.79363],[92.27432,27.89077],[91.87057,27.7195],[91.84722,27.76325],[91.6469,27.76358],[91.55819,27.6144],[91.65007,27.48287],[92.01132,27.47352],[92.12019,27.27829],[92.04702,27.26861],[92.03457,27.07334],[92.11863,26.893],[92.05523,26.8692],[91.83181,26.87318],[91.50067,26.79223],[90.67715,26.77215],[90.48504,26.8594],[90.39271,26.90704],[90.30402,26.85098],[90.04535,26.72422],[89.86124,26.73307],[89.63369,26.74402],[89.42349,26.83727],[89.3901,26.84225],[89.38319,26.85963],[89.37913,26.86224],[89.1926,26.81329],[89.12825,26.81661],[89.09554,26.89089],[88.95807,26.92668],[88.92301,26.99286],[88.8714,26.97488],[88.86984,27.10937],[88.74219,27.144],[88.91901,27.32483],[88.82981,27.38814],[88.77517,27.45415],[88.88091,27.85192],[88.83559,28.01936],[88.63235,28.12356],[88.54858,28.06057],[88.25332,27.9478],[88.1278,27.95417],[88.13378,27.88015],[88.1973,27.85067],[88.19107,27.79285],[88.04008,27.49223],[88.07277,27.43007],[88.01646,27.21612],[88.01587,27.21388],[87.9887,27.11045],[88.11719,26.98758],[88.13422,26.98705],[88.12302,26.95324],[88.19107,26.75516],[88.1659,26.68177],[88.16452,26.64111],[88.09963,26.54195],[88.09414,26.43732],[88.00895,26.36029],[87.90115,26.44923],[87.89085,26.48565],[87.84193,26.43663],[87.7918,26.46737],[87.76004,26.40711],[87.67893,26.43501],[87.66803,26.40294],[87.59175,26.38342],[87.55274,26.40596],[87.51571,26.43106],[87.46566,26.44058],[87.37314,26.40815],[87.34568,26.34787],[87.26568,26.37294],[87.26587,26.40592],[87.24682,26.4143],[87.18863,26.40558],[87.14751,26.40542],[87.09147,26.45039],[87.0707,26.58571],[87.04691,26.58685],[87.01559,26.53228],[86.95912,26.52076],[86.94543,26.52076],[86.82898,26.43919],[86.76797,26.45892],[86.74025,26.42386],[86.69124,26.45169],[86.62686,26.46891],[86.61313,26.48658],[86.57073,26.49825],[86.54258,26.53819],[86.49726,26.54218],[86.31564,26.61925],[86.26235,26.61886],[86.22513,26.58863],[86.13596,26.60651],[86.02729,26.66756],[85.8492,26.56667],[85.85126,26.60866],[85.83126,26.61134],[85.76907,26.63076],[85.72315,26.67471],[85.73483,26.79613],[85.66239,26.84822],[85.61621,26.86721],[85.59461,26.85161],[85.5757,26.85955],[85.56471,26.84133],[85.47752,26.79292],[85.34302,26.74954],[85.21159,26.75933],[85.18046,26.80519],[85.19291,26.86909],[85.15883,26.86966],[85.02635,26.85381],[85.05592,26.88991],[85.00536,26.89523],[84.97186,26.9149],[84.96687,26.95599],[84.85754,26.98984],[84.82913,27.01989],[84.793,26.9968],[84.64496,27.04669],[84.69166,27.21294],[84.62161,27.33885],[84.29315,27.39],[84.25735,27.44941],[84.21376,27.45218],[84.10791,27.52399],[84.02229,27.43836],[83.93306,27.44939],[83.86182,27.4241],[83.85595,27.35797],[83.61288,27.47013],[83.39495,27.4798],[83.38872,27.39276],[83.35136,27.33885],[83.29999,27.32778],[83.2673,27.36235],[83.27197,27.38309],[83.19413,27.45632],[82.94938,27.46036],[82.93261,27.50328],[82.74119,27.49838],[82.70378,27.72122],[82.46405,27.6716],[82.06554,27.92222],[81.97214,27.93322],[81.91223,27.84995],[81.47867,28.08303],[81.48179,28.12148],[81.38683,28.17638],[81.32923,28.13521],[81.19847,28.36284],[81.08507,28.38346],[80.89648,28.47237],[80.55142,28.69182],[80.50575,28.6706],[80.52443,28.54897],[80.44504,28.63098],[80.37188,28.63371],[80.12125,28.82346],[80.06957,28.82763],[80.05743,28.91479],[80.18085,29.13649],[80.23178,29.11626],[80.26602,29.13938],[80.24112,29.21414],[80.28626,29.20327],[80.31428,29.30784],[80.24322,29.44299],[80.37939,29.57098],[80.41858,29.63581],[80.38428,29.68513],[80.36803,29.73865],[80.41554,29.79451],[80.43458,29.80466],[80.48997,29.79566],[80.56247,29.86661],[80.56957,29.88176],[80.60226,29.95732],[80.67076,29.95732],[80.8778,30.13384],[80.93695,30.18229],[81.03953,30.20059],[80.83343,30.32023],[80.54504,30.44936],[80.20721,30.58541],[79.93255,30.88288],[79.59884,30.93943],[79.22805,31.34963],[79.14016,31.43403],[79.01931,31.42817],[78.77898,31.31209],[78.71032,31.50197],[78.84516,31.60631],[78.69933,31.78723],[78.78036,31.99478],[78.74404,32.00384],[78.68754,32.10256],[78.49609,32.2762],[78.4645,32.45367],[78.38897,32.53938],[78.73916,32.69438],[78.7831,32.46873],[78.96713,32.33655],[78.99322,32.37948],[79.0979,32.38051],[79.13174,32.47766],[79.26768,32.53277],[79.46562,32.69668],[79.14016,33.02545],[79.15252,33.17156],[78.73636,33.56521],[78.67599,33.66445],[78.77349,33.73871],[78.73367,34.01121],[78.65657,34.03195],[78.66225,34.08858],[78.91769,34.15452],[78.99802,34.3027],[79.05364,34.32482],[78.74465,34.45174],[78.56475,34.50835],[78.54964,34.57283],[78.27781,34.61484],[78.18435,34.7998],[78.22692,34.88771],[78.00033,35.23954],[78.03466,35.3785],[78.11664,35.48022],[77.80532,35.52058],[77.70232,35.46244],[77.44277,35.46132],[76.96624,35.5932],[76.84539,35.67356],[76.77323,35.66062],[76.75475,35.52617],[76.85088,35.39754],[76.93465,35.39866],[77.11796,35.05419],[76.99251,34.93349],[76.87193,34.96906],[76.74514,34.92488],[76.74377,34.84039],[76.67648,34.76371],[76.47186,34.78965],[76.15463,34.6429],[76.04614,34.67566],[75.75438,34.51827],[75.38009,34.55021],[75.01479,34.64629],[74.6663,34.703],[74.58083,34.77386],[74.31239,34.79626],[74.12897,34.70073],[73.96423,34.68244],[73.93401,34.63386],[73.93951,34.57169],[73.89419,34.54568],[73.88732,34.48911],[73.74999,34.3781],[73.74862,34.34183],[73.8475,34.32935],[73.90517,34.35317],[73.98208,34.2522],[73.90677,34.10504],[73.88732,34.05105],[73.91341,34.01235],[74.21554,34.03853],[74.25262,34.01577],[74.26086,33.92237],[74.14001,33.83002],[74.05898,33.82089],[74.00891,33.75437],[73.96423,33.73071],[73.98968,33.66155],[73.97367,33.64061],[74.03576,33.56718],[74.10115,33.56392],[74.18121,33.4745],[74.17983,33.3679],[74.08782,33.26232],[74.01366,33.25199],[74.02144,33.18908],[74.15374,33.13477],[74.17571,33.07495],[74.31854,33.02891],[74.34875,32.97823],[74.31227,32.92795],[74.41467,32.90563],[74.45312,32.77755],[74.6289,32.75561],[74.64675,32.82604],[74.7113,32.84219],[74.65345,32.71225],[74.69542,32.66792],[74.64424,32.60985],[74.65251,32.56416],[74.67431,32.56676],[74.68362,32.49298],[74.84725,32.49075],[74.97634,32.45367],[75.03265,32.49538],[75.28259,32.36556],[75.38046,32.26836],[75.25649,32.10187],[75.00793,32.03786],[74.9269,32.0658],[74.86236,32.04485],[74.79919,31.95983],[74.58907,31.87824],[74.47771,31.72227],[74.57498,31.60382],[74.61517,31.55698],[74.59319,31.50197],[74.64713,31.45605],[74.59773,31.4136],[74.53223,31.30321],[74.51629,31.13829],[74.56023,31.08303],[74.60281,31.10419],[74.60006,31.13711],[74.6852,31.12771],[74.67971,31.05479],[74.5616,31.04153],[73.88993,30.36305],[73.95736,30.28466],[73.97225,30.19829],[73.80299,30.06969],[73.58665,30.01848],[73.3962,29.94707],[73.28094,29.56646],[73.05886,29.1878],[73.01337,29.16422],[72.94272,29.02487],[72.40402,28.78283],[72.29495,28.66367],[72.20329,28.3869],[71.9244,28.11555],[71.89921,27.96035],[70.79054,27.68423],[70.60927,28.02178],[70.37307,28.01208],[70.12502,27.8057],[70.03136,27.56627],[69.58519,27.18109],[69.50904,26.74892],[69.88555,26.56836],[70.05584,26.60398],[70.17532,26.55362],[70.17532,26.24118],[70.08193,26.08094],[70.0985,25.93238],[70.2687,25.71156],[70.37444,25.67443],[70.53649,25.68928],[70.60378,25.71898],[70.67382,25.68186],[70.66695,25.39314],[70.89148,25.15064],[70.94002,24.92843],[71.09405,24.69017],[70.97594,24.60904],[71.00341,24.46038],[71.12838,24.42662],[71.04461,24.34657],[70.94985,24.3791],[70.85784,24.30903],[70.88393,24.27398],[70.71502,24.23517],[70.57906,24.27774],[70.5667,24.43787],[70.11712,24.30915],[70.03428,24.172],[69.73335,24.17007],[69.59579,24.29777],[69.29778,24.28712],[69.19341,24.25646],[69.07806,24.29777],[68.97781,24.26021],[68.90914,24.33156],[68.7416,24.31904],[68.74643,23.97027],[68.39339,23.96838],[68.20763,23.85849],[68.11329,23.53945]]]}},{"type":"Feature","properties":{"id":"IR"},"geometry":{"type":"Polygon","coordinates":[[[44.03667,39.39223],[44.1043,39.19842],[44.20946,39.13975],[44.18863,38.93881],[44.30322,38.81581],[44.26155,38.71427],[44.28065,38.6465],[44.32058,38.62752],[44.3207,38.49799],[44.3119,38.37887],[44.38309,38.36117],[44.44386,38.38295],[44.50115,38.33939],[44.42476,38.25763],[44.22509,37.88859],[44.3883,37.85433],[44.45948,37.77065],[44.55498,37.783],[44.62096,37.71985],[44.56887,37.6429],[44.61401,37.60165],[44.58449,37.45018],[44.81021,37.2915],[44.75986,37.21549],[44.7868,37.16644],[44.78319,37.1431],[44.75229,37.11958],[44.81611,37.04383],[44.89862,37.01897],[44.91199,36.91468],[44.90173,36.86096],[44.83479,36.81362],[44.84725,36.77622],[45.01537,36.75128],[45.06985,36.6814],[45.06985,36.62645],[45.00759,36.5402],[45.11811,36.40751],[45.23953,36.43257],[45.27394,36.35846],[45.26261,36.3001],[45.30038,36.27769],[45.32235,36.17383],[45.37312,36.09917],[45.37652,36.06222],[45.33916,35.99424],[45.38275,35.97156],[45.46594,36.00042],[45.55245,35.99943],[45.60018,35.96069],[45.6645,35.92872],[45.76145,35.79898],[45.81442,35.82107],[45.89784,35.83708],[45.94711,35.82218],[46.08325,35.8581],[46.17198,35.8013],[46.32921,35.82655],[46.34166,35.78363],[46.23736,35.71414],[46.01631,35.69139],[46.0117,35.65059],[45.99452,35.63574],[46.0165,35.61501],[46.01307,35.59756],[46.03028,35.57416],[45.97584,35.58132],[46.01518,35.52012],[45.98453,35.49848],[46.05358,35.38568],[46.13152,35.32548],[46.15474,35.2883],[46.11367,35.23729],[46.18457,35.22561],[46.19738,35.18536],[46.16229,35.16984],[46.15642,35.1268],[46.19116,35.11097],[46.11763,35.07551],[46.07747,35.0838],[46.06508,35.03699],[45.94756,35.09188],[45.93108,35.08148],[45.92203,35.09538],[45.92173,35.0465],[45.87864,35.03441],[45.89477,34.95805],[45.86532,34.89858],[45.78904,34.91135],[45.79682,34.85133],[45.73641,34.83975],[45.70031,34.82322],[45.68284,34.76624],[45.65672,34.7222],[45.70031,34.69277],[45.73923,34.54416],[45.60224,34.55057],[45.59074,34.55558],[45.53219,34.60441],[45.51883,34.47692],[45.43879,34.45949],[45.46697,34.38221],[45.49171,34.3439],[45.53552,34.35148],[45.58667,34.30147],[45.56176,34.15088],[45.47264,34.03099],[45.41077,33.97421],[45.42789,33.9458],[45.50261,33.94968],[45.77814,33.60938],[45.89801,33.63661],[45.96183,33.55751],[45.86687,33.49263],[45.99919,33.5082],[46.20623,33.20395],[46.11905,33.11924],[46.05367,33.13097],[46.03966,33.09577],[46.15175,33.07229],[46.09103,32.98354],[46.17198,32.95612],[46.32298,32.9731],[46.46788,32.91992],[47.17218,32.45393],[47.37529,32.47808],[47.57144,32.20583],[47.52474,32.15972],[47.64771,32.07666],[47.86337,31.78422],[47.6804,31.39086],[47.68219,31.00004],[48.03221,30.9967],[48.02443,30.4789],[48.14585,30.44133],[48.18321,30.39703],[48.19425,30.32796],[48.21279,30.31644],[48.24385,30.33846],[48.26393,30.3408],[48.41117,30.19846],[48.41671,30.17254],[48.38714,30.13485],[48.38869,30.11062],[48.43384,30.08233],[48.4494,30.04456],[48.44785,30.00148],[48.51011,29.96238],[48.61441,29.93675],[48.83867,29.78572],[49.98877,27.87827],[50.37726,27.89227],[54.39838,25.68383],[55.14145,25.62624],[55.81777,26.18798],[56.2644,26.58649],[56.68954,26.76645],[56.79239,26.41236],[56.82555,25.7713],[56.86325,25.03856],[61.5251,24.57287],[61.57592,25.0492],[61.6433,25.27541],[61.683,25.66638],[61.83968,25.7538],[61.83831,26.07249],[61.89391,26.26251],[62.05117,26.31647],[62.21304,26.26601],[62.31484,26.528],[62.77352,26.64099],[63.1889,26.65072],[63.18688,26.83844],[63.25005,26.84212],[63.25005,27.08692],[63.32283,27.14437],[63.19649,27.25674],[62.80604,27.22412],[62.79684,27.34381],[62.84905,27.47627],[62.7638,28.02992],[62.79412,28.28108],[62.59499,28.24842],[62.40259,28.42703],[61.93581,28.55284],[61.65978,28.77937],[61.53765,29.00507],[61.31508,29.38903],[60.87231,29.86514],[61.80829,30.84224],[61.78268,30.92724],[61.8335,30.97669],[61.83257,31.0452],[61.80957,31.12576],[61.80569,31.16167],[61.70929,31.37391],[60.84541,31.49561],[60.86191,32.22565],[60.56485,33.12944],[60.88908,33.50219],[60.91133,33.55596],[60.69573,33.56054],[60.57762,33.59772],[60.5485,33.73422],[60.5838,33.80793],[60.50209,34.13992],[60.66502,34.31539],[60.91321,34.30411],[60.72316,34.52857],[60.99922,34.63064],[61.00197,34.70631],[61.06926,34.82139],[61.12831,35.09938],[61.0991,35.27845],[61.18187,35.30249],[61.27371,35.61482],[61.22719,35.67038],[61.26152,35.80749],[61.22444,35.92879],[61.12007,35.95992],[61.22719,36.12759],[61.1393,36.38782],[61.18187,36.55348],[61.14516,36.64644],[60.34767,36.63214],[60.00768,37.04102],[59.74678,37.12499],[59.55178,37.13594],[59.39385,37.34257],[59.39797,37.47892],[59.33507,37.53146],[59.22905,37.51161],[58.9338,37.67374],[58.6921,37.64548],[58.5479,37.70526],[58.47786,37.6433],[58.39959,37.63134],[58.22999,37.6856],[58.21399,37.77281],[57.79534,37.89299],[57.35042,37.98546],[57.37236,38.09321],[57.21169,38.28965],[57.03453,38.18717],[56.73928,38.27887],[56.62255,38.24005],[56.43303,38.26054],[56.32454,38.18502],[56.33278,38.08132],[55.97847,38.08024],[55.76561,38.12238],[55.44152,38.08564],[55.13412,37.94705],[54.851,37.75739],[54.77684,37.62264],[54.81804,37.61285],[54.77822,37.51597],[54.67247,37.43532],[54.58664,37.45809],[54.36211,37.34912],[54.24565,37.32047],[53.89734,37.3464],[49.20805,38.40869],[48.88288,38.43975],[48.84969,38.45015],[48.81072,38.44853],[48.78979,38.45026],[48.70001,38.40564],[48.62217,38.40198],[48.58793,38.45076],[48.45084,38.61013],[48.3146,38.59958],[48.24773,38.71883],[48.02581,38.82705],[48.01409,38.90333],[48.07734,38.91616],[48.08627,38.94434],[48.28437,38.97186],[48.33884,39.03022],[48.31239,39.09278],[48.15361,39.19419],[48.12404,39.25208],[48.15984,39.30028],[48.37385,39.37584],[48.34264,39.42935],[47.98977,39.70999],[47.84774,39.66285],[47.50099,39.49615],[47.38978,39.45999],[47.31301,39.37492],[47.05927,39.24846],[47.05771,39.20143],[46.95341,39.13505],[46.92539,39.16644],[46.83822,39.13143],[46.75752,39.03231],[46.53497,38.86548],[46.34059,38.92076],[46.20601,38.85262],[46.14785,38.84206],[46.06766,38.87861],[46.00228,38.87376],[45.94624,38.89072],[45.90266,38.87739],[45.83883,38.90768],[45.65172,38.95199],[45.6155,38.94304],[45.6131,38.964],[45.44966,38.99243],[45.44811,39.04927],[45.40452,39.07224],[45.40148,39.09007],[45.30489,39.18333],[45.16168,39.21952],[45.08751,39.35052],[45.05932,39.36435],[44.96746,39.42998],[44.88916,39.59653],[44.81043,39.62677],[44.71806,39.71124],[44.65422,39.72163],[44.6137,39.78393],[44.47298,39.68788],[44.48111,39.61579],[44.41849,39.56659],[44.42832,39.4131],[44.37921,39.4131],[44.29818,39.378],[44.22452,39.4169],[44.03667,39.39223]]]}},{"type":"Feature","properties":{"id":"IQ"},"geometry":{"type":"Polygon","coordinates":[[[38.79171,33.37328],[39.08202,32.50304],[38.98762,32.47694],[39.04251,32.30203],[39.26157,32.35555],[39.29903,32.23259],[40.01521,32.05667],[42.97601,30.72204],[42.97796,30.48295],[44.72255,29.19736],[46.42415,29.05947],[46.5527,29.10283],[46.89695,29.50584],[47.15166,30.01044],[47.37192,30.10421],[47.7095,30.10453],[48.01114,29.98906],[48.06782,30.02906],[48.17332,30.02448],[48.40479,29.85763],[48.59531,29.66815],[48.83867,29.78572],[48.61441,29.93675],[48.51011,29.96238],[48.44785,30.00148],[48.4494,30.04456],[48.43384,30.08233],[48.38869,30.11062],[48.38714,30.13485],[48.41671,30.17254],[48.41117,30.19846],[48.26393,30.3408],[48.24385,30.33846],[48.21279,30.31644],[48.19425,30.32796],[48.18321,30.39703],[48.14585,30.44133],[48.02443,30.4789],[48.03221,30.9967],[47.68219,31.00004],[47.6804,31.39086],[47.86337,31.78422],[47.64771,32.07666],[47.52474,32.15972],[47.57144,32.20583],[47.37529,32.47808],[47.17218,32.45393],[46.46788,32.91992],[46.32298,32.9731],[46.17198,32.95612],[46.09103,32.98354],[46.15175,33.07229],[46.03966,33.09577],[46.05367,33.13097],[46.11905,33.11924],[46.20623,33.20395],[45.99919,33.5082],[45.86687,33.49263],[45.96183,33.55751],[45.89801,33.63661],[45.77814,33.60938],[45.50261,33.94968],[45.42789,33.9458],[45.41077,33.97421],[45.47264,34.03099],[45.56176,34.15088],[45.58667,34.30147],[45.53552,34.35148],[45.49171,34.3439],[45.46697,34.38221],[45.43879,34.45949],[45.51883,34.47692],[45.53219,34.60441],[45.59074,34.55558],[45.60224,34.55057],[45.73923,34.54416],[45.70031,34.69277],[45.65672,34.7222],[45.68284,34.76624],[45.70031,34.82322],[45.73641,34.83975],[45.79682,34.85133],[45.78904,34.91135],[45.86532,34.89858],[45.89477,34.95805],[45.87864,35.03441],[45.92173,35.0465],[45.92203,35.09538],[45.93108,35.08148],[45.94756,35.09188],[46.06508,35.03699],[46.07747,35.0838],[46.11763,35.07551],[46.19116,35.11097],[46.15642,35.1268],[46.16229,35.16984],[46.19738,35.18536],[46.18457,35.22561],[46.11367,35.23729],[46.15474,35.2883],[46.13152,35.32548],[46.05358,35.38568],[45.98453,35.49848],[46.01518,35.52012],[45.97584,35.58132],[46.03028,35.57416],[46.01307,35.59756],[46.0165,35.61501],[45.99452,35.63574],[46.0117,35.65059],[46.01631,35.69139],[46.23736,35.71414],[46.34166,35.78363],[46.32921,35.82655],[46.17198,35.8013],[46.08325,35.8581],[45.94711,35.82218],[45.89784,35.83708],[45.81442,35.82107],[45.76145,35.79898],[45.6645,35.92872],[45.60018,35.96069],[45.55245,35.99943],[45.46594,36.00042],[45.38275,35.97156],[45.33916,35.99424],[45.37652,36.06222],[45.37312,36.09917],[45.32235,36.17383],[45.30038,36.27769],[45.26261,36.3001],[45.27394,36.35846],[45.23953,36.43257],[45.11811,36.40751],[45.00759,36.5402],[45.06985,36.62645],[45.06985,36.6814],[45.01537,36.75128],[44.84725,36.77622],[44.83479,36.81362],[44.90173,36.86096],[44.91199,36.91468],[44.89862,37.01897],[44.81611,37.04383],[44.75229,37.11958],[44.78319,37.1431],[44.76698,37.16162],[44.63179,37.19229],[44.42631,37.05825],[44.38117,37.05825],[44.35315,37.04955],[44.35937,37.02843],[44.30645,36.97373],[44.25975,36.98119],[44.18503,37.09551],[44.22239,37.15756],[44.27998,37.16501],[44.2613,37.25055],[44.13521,37.32486],[44.02002,37.33229],[43.90949,37.22453],[43.84878,37.22205],[43.82699,37.19477],[43.8052,37.22825],[43.7009,37.23692],[43.63085,37.21957],[43.56702,37.25675],[43.50787,37.24436],[43.33508,37.33105],[43.30083,37.30629],[43.11403,37.37436],[42.93705,37.32015],[42.78887,37.38615],[42.56725,37.14878],[42.35724,37.10998],[42.36697,37.0627],[41.81736,36.58782],[41.40058,36.52502],[41.28864,36.35368],[41.2564,36.06012],[41.37027,35.84095],[41.38184,35.62502],[41.26569,35.42708],[41.21654,35.1508],[41.2345,34.80049],[41.12388,34.65742],[40.97676,34.39788],[40.64314,34.31604],[38.79171,33.37328]]]}},{"type":"Feature","properties":{"id":"IL"},"geometry":{"type":"Polygon","coordinates":[[[33.62659,31.82938],[34.052,31.46619],[34.29262,31.70393],[34.48681,31.59711],[34.56797,31.54197],[34.48892,31.48365],[34.40077,31.40926],[34.36505,31.36404],[34.37381,31.30598],[34.36523,31.28963],[34.29417,31.24194],[34.26742,31.21998],[34.92298,29.45305],[34.97718,29.54294],[34.98207,29.58147],[35.02147,29.66343],[35.14108,30.07374],[35.19183,30.34636],[35.16218,30.43535],[35.19595,30.50297],[35.21379,30.60401],[35.29311,30.71365],[35.33456,30.81224],[35.33984,30.8802],[35.41371,30.95565],[35.43658,31.12444],[35.40316,31.25535],[35.47672,31.49578],[35.39675,31.49572],[35.22921,31.37445],[35.13033,31.3551],[35.02459,31.35979],[34.92571,31.34337],[34.88932,31.37093],[34.87833,31.39321],[34.89756,31.43891],[34.93258,31.47816],[34.94356,31.50743],[34.9415,31.55601],[34.95249,31.59813],[35.00879,31.65426],[35.08226,31.69107],[35.10782,31.71594],[35.11895,31.71454],[35.12933,31.7325],[35.13931,31.73012],[35.15119,31.73634],[35.15474,31.73352],[35.16478,31.73242],[35.18023,31.72067],[35.20538,31.72388],[35.21937,31.71578],[35.22392,31.71899],[35.23972,31.70896],[35.24315,31.71244],[35.2438,31.7201],[35.24981,31.72543],[35.25182,31.73945],[35.26319,31.74846],[35.25225,31.7678],[35.26058,31.79064],[35.25573,31.81362],[35.26404,31.82567],[35.251,31.83085],[35.25753,31.8387],[35.24816,31.8458],[35.2304,31.84222],[35.2249,31.85433],[35.22817,31.8638],[35.22567,31.86745],[35.22294,31.87889],[35.22014,31.88264],[35.2136,31.88241],[35.21276,31.88153],[35.21016,31.88237],[35.20945,31.8815],[35.20791,31.8821],[35.20673,31.88151],[35.20381,31.86716],[35.21128,31.863],[35.216,31.83894],[35.21469,31.81835],[35.19461,31.82687],[35.18169,31.82542],[35.18603,31.80901],[35.14174,31.81325],[35.07677,31.85627],[35.05617,31.85685],[35.01978,31.82944],[34.9724,31.83352],[34.99712,31.85569],[35.03489,31.85919],[35.03978,31.89276],[35.03489,31.92448],[35.00124,31.93264],[34.98682,31.96935],[35.00261,32.027],[34.9863,32.09551],[34.99437,32.10962],[34.98507,32.12606],[34.99039,32.14626],[34.96009,32.17503],[34.95703,32.19522],[34.98885,32.20758],[35.01841,32.23981],[35.02939,32.2671],[35.01119,32.28684],[35.01772,32.33863],[35.04243,32.35008],[35.05142,32.3667],[35.0421,32.38242],[35.05311,32.4024],[35.05423,32.41754],[35.07059,32.4585],[35.08564,32.46948],[35.09236,32.47614],[35.10024,32.47856],[35.10882,32.4757],[35.15937,32.50466],[35.2244,32.55289],[35.25049,32.52453],[35.29306,32.50947],[35.30685,32.51024],[35.35212,32.52047],[35.40224,32.50136],[35.42034,32.46009],[35.41598,32.45593],[35.41048,32.43706],[35.42078,32.41562],[35.55807,32.38674],[35.55494,32.42687],[35.57485,32.48669],[35.56614,32.64393],[35.59813,32.65159],[35.61669,32.67999],[35.66527,32.681],[35.68467,32.70715],[35.75983,32.74803],[35.78745,32.77938],[35.83758,32.82817],[35.84021,32.8725],[35.87012,32.91976],[35.89298,32.9456],[35.87188,32.98028],[35.84802,33.1031],[35.81911,33.11077],[35.81911,33.1336],[35.84285,33.16673],[35.83846,33.19397],[35.81647,33.2028],[35.81295,33.24841],[35.77513,33.27342],[35.813,33.3172],[35.77477,33.33609],[35.62019,33.27278],[35.62283,33.24226],[35.58502,33.26653],[35.58326,33.28381],[35.56523,33.28969],[35.55555,33.25844],[35.54544,33.25513],[35.54808,33.236],[35.5362,33.23196],[35.54228,33.19865],[35.52573,33.11921],[35.50335,33.114],[35.50272,33.09056],[35.448,33.09264],[35.43059,33.06659],[35.35223,33.05617],[35.31429,33.10515],[35.1924,33.08743],[35.10645,33.09318],[34.78515,33.20368],[33.62659,31.82938]]]}},{"type":"Feature","properties":{"id":"JM"},"geometry":{"type":"Polygon","coordinates":[[[-78.75694,18.78765],[-78.34606,16.57862],[-75.50728,17.08879],[-76.34192,18.86145],[-78.75694,18.78765]]]}},{"type":"Feature","properties":{"id":"CN-SN"},"geometry":{"type":"Polygon","coordinates":[[[105.47878,32.89406],[105.62461,32.70526],[106.04484,32.86805],[106.11007,32.72144],[106.43073,32.6307],[106.85851,32.71855],[107.1009,32.67174],[107.1215,32.48543],[107.25677,32.40779],[107.42362,32.55433],[107.97569,32.13782],[108.25309,32.28365],[108.50234,32.20641],[109.20066,31.85248],[109.27619,31.71823],[109.58381,31.72933],[109.62364,32.10177],[109.49901,32.3028],[109.55703,32.48543],[109.71393,32.61508],[110.04524,32.55144],[110.19218,32.62029],[110.13725,32.81238],[110.03391,32.86041],[110.02481,32.87482],[109.86259,32.911],[109.79049,32.87929],[109.75925,32.91273],[109.79324,33.0691],[109.42794,33.15479],[109.61299,33.2783],[110.03974,33.19158],[110.16197,33.20996],[110.22926,33.15882],[110.46958,33.17721],[110.55713,33.26653],[110.7003,33.09384],[110.9801,33.2605],[111.0189,33.56771],[110.84106,33.67978],[110.587,33.89093],[110.6385,34.18056],[110.35388,34.519],[110.41122,34.58432],[110.23921,34.62784],[110.2272,34.90733],[110.36041,35.139],[110.40572,35.30728],[110.61035,35.64948],[110.43594,36.1606],[110.4895,36.56039],[110.39474,36.99816],[110.74665,37.45169],[110.77377,37.62946],[110.58357,37.92578],[110.49636,38.02862],[110.5149,38.20905],[110.80192,38.44713],[110.87333,38.45842],[110.88775,38.65522],[110.95745,38.75836],[111.00963,38.90706],[110.97495,38.97836],[111.04637,39.02158],[111.09134,39.02985],[111.1576,39.10741],[111.23897,39.30003],[111.19228,39.30508],[111.11881,39.36403],[111.09289,39.35859],[111.04808,39.43022],[111.15074,39.58478],[110.88363,39.50854],[110.68759,39.26522],[110.59661,39.27744],[110.52005,39.3834],[110.43662,39.38261],[110.38135,39.30826],[110.23063,39.4566],[110.10944,39.42876],[110.20866,39.28488],[109.89795,39.14683],[109.70809,39.05011],[109.55428,38.80119],[108.9624,38.35673],[108.93356,38.17883],[109.18521,38.03592],[109.02831,38.01618],[108.93836,37.9182],[108.83193,38.0662],[108.78936,37.68436],[108.01174,37.66561],[107.96607,37.79269],[107.66139,37.87999],[107.2705,37.47921],[107.30346,37.0979],[107.34878,36.90378],[108.6589,36.41023],[108.64654,35.95021],[108.58886,35.31176],[107.84248,35.26524],[107.7079,35.30896],[107.8466,34.97487],[107.18604,34.9062],[107.05833,35.02887],[106.76788,35.09238],[106.48635,35.05754],[106.5612,34.74443],[106.33186,34.55972],[106.3322,34.51532],[106.47399,34.52183],[106.6175,34.44797],[106.64188,34.38651],[106.71295,34.37092],[106.41357,33.89777],[106.58523,33.57],[106.51519,33.50246],[106.36688,33.61347],[105.95764,33.61061],[105.72624,33.36322],[105.96244,33.15652],[105.85945,32.93896],[105.47878,32.89406]]]}},{"type":"Feature","properties":{"id":"JO"},"geometry":{"type":"Polygon","coordinates":[[[34.88293,29.37455],[34.95987,29.35727],[36.07081,29.18469],[36.50005,29.49696],[36.75083,29.86903],[37.4971,29.99949],[37.66395,30.33245],[37.99354,30.49998],[36.99791,31.50081],[38.99233,31.99721],[39.29903,32.23259],[39.26157,32.35555],[39.04251,32.30203],[38.98762,32.47694],[39.08202,32.50304],[38.79171,33.37328],[36.83946,32.31293],[36.40959,32.37908],[36.23948,32.50108],[36.20875,32.49529],[36.20379,32.52751],[36.08074,32.51463],[36.02239,32.65911],[35.96633,32.66237],[35.93307,32.71966],[35.88405,32.71321],[35.75983,32.74803],[35.68467,32.70715],[35.66527,32.681],[35.61669,32.67999],[35.59813,32.65159],[35.56614,32.64393],[35.57485,32.48669],[35.55494,32.42687],[35.55807,32.38674],[35.57111,32.21877],[35.52012,32.04076],[35.54375,31.96587],[35.52758,31.9131],[35.55941,31.76535],[35.47672,31.49578],[35.40316,31.25535],[35.43658,31.12444],[35.41371,30.95565],[35.33984,30.8802],[35.33456,30.81224],[35.29311,30.71365],[35.21379,30.60401],[35.19595,30.50297],[35.16218,30.43535],[35.19183,30.34636],[35.14108,30.07374],[35.02147,29.66343],[34.98207,29.58147],[34.97718,29.54294],[34.92298,29.45305],[34.88293,29.37455]]]}},{"type":"Feature","properties":{"id":"CN-BJ"},"geometry":{"type":"Polygon","coordinates":[[[115.41755,39.77925],[115.47042,39.74177],[115.51574,39.6041],[115.66268,39.60833],[115.74851,39.51251],[115.81031,39.50933],[115.91365,39.57552],[116.22058,39.5787],[116.23809,39.51649],[116.32942,39.4566],[116.43722,39.44494],[116.46366,39.52946],[116.52065,39.59193],[116.80732,39.61415],[116.89075,39.69714],[116.95083,39.65434],[117.14137,39.61203],[117.19493,39.75999],[117.14755,39.81723],[117.26154,39.83385],[117.13897,39.8797],[117.19425,40.07281],[117.40608,40.17362],[117.34291,40.23131],[117.32814,40.2879],[117.29587,40.27612],[117.21811,40.36616],[117.22824,40.41245],[117.25913,40.43701],[117.20626,40.50126],[117.25227,40.50857],[117.24781,40.54902],[117.3072,40.57719],[117.40882,40.56206],[117.41432,40.63701],[117.42977,40.61851],[117.44419,40.65042],[117.47749,40.63167],[117.51663,40.65069],[117.43972,40.68532],[117.30789,40.65199],[117.21794,40.69938],[116.98379,40.68896],[116.62605,41.06485],[116.6209,40.97989],[116.44306,40.98145],[116.47602,40.89586],[116.31465,40.93037],[116.46537,40.7652],[116.24565,40.79067],[116.11278,40.62646],[115.82267,40.5871],[115.81615,40.5575],[115.77907,40.56128],[115.73272,40.51145],[115.77632,40.46196],[115.76568,40.44668],[115.857,40.36145],[115.90541,40.35544],[115.95382,40.27481],[115.8467,40.14633],[115.76808,40.15736],[115.50338,40.07649],[115.42098,39.96449],[115.56518,39.81011],[115.41755,39.77925]]]}},{"type":"Feature","properties":{"id":"KZ"},"geometry":{"type":"Polygon","coordinates":[[[46.49011,48.43019],[47.11516,48.27188],[47.12107,47.83687],[47.38731,47.68176],[47.41689,47.83687],[47.64973,47.76559],[48.15348,47.74545],[48.45173,47.40818],[48.52326,47.4102],[49.01136,46.72716],[48.51142,46.69268],[48.54988,46.56267],[49.16518,46.38542],[49.32259,46.26944],[49.88945,46.04554],[49.2134,44.84989],[52.26048,41.69249],[52.47884,41.78034],[52.97575,42.1308],[54.20635,42.38477],[54.95182,41.92424],[55.45471,41.25609],[56.00314,41.32584],[55.97584,44.99322],[55.97584,44.99328],[55.97584,44.99338],[55.97584,44.99343],[55.97584,44.99348],[55.97584,44.99353],[55.97584,44.99359],[55.97584,44.99369],[55.97584,44.99374],[55.97584,44.99384],[55.97584,44.9939],[55.97584,44.994],[55.97584,44.99405],[55.97584,44.99415],[55.97584,44.99421],[55.97584,44.99426],[55.97584,44.99431],[55.97584,44.99436],[55.97584,44.99441],[55.97594,44.99446],[55.97605,44.99452],[55.97605,44.99457],[55.97605,44.99462],[55.97605,44.99467],[55.97605,44.99477],[55.97615,44.99477],[55.97615,44.99483],[55.97615,44.99493],[55.97615,44.99498],[55.97615,44.99503],[55.97615,44.99508],[55.97625,44.99514],[55.97636,44.99519],[55.97636,44.99524],[55.97646,44.99529],[55.97646,44.99534],[55.97656,44.99539],[55.97667,44.99545],[55.97677,44.9955],[55.97677,44.99555],[55.97677,44.9956],[55.97687,44.9956],[55.97698,44.99565],[55.97698,44.9957],[55.97708,44.99576],[55.97718,44.99581],[55.97729,44.99586],[55.97739,44.99586],[55.97739,44.99591],[55.97749,44.99591],[55.9776,44.99591],[55.9777,44.99596],[55.9777,44.99601],[55.9778,44.99607],[55.97791,44.99607],[55.97801,44.99607],[55.97801,44.99612],[55.97811,44.99617],[55.97822,44.99617],[55.97832,44.99622],[55.97842,44.99622],[58.59711,45.58671],[61.01475,44.41383],[62.01711,43.51008],[63.34656,43.64003],[64.53885,43.56941],[64.96464,43.74748],[65.18666,43.48835],[65.53277,43.31856],[65.85194,42.85481],[66.09482,42.93426],[66.00546,41.94455],[66.53302,41.87388],[66.69129,41.1311],[67.9644,41.14611],[67.98511,41.02794],[68.08273,41.08148],[68.1271,41.0324],[67.96736,40.83798],[68.49983,40.56437],[68.63,40.59358],[68.58444,40.91447],[68.49983,40.99669],[68.62221,41.03019],[68.65662,40.93861],[68.73945,40.96989],[68.7217,41.05025],[69.01308,41.22804],[69.05006,41.36183],[69.15137,41.43078],[69.17701,41.43769],[69.18528,41.45175],[69.20439,41.45391],[69.22671,41.46298],[69.23332,41.45847],[69.25059,41.46693],[69.29778,41.43673],[69.35554,41.47211],[69.37468,41.46555],[69.45081,41.46246],[69.39485,41.51518],[69.45751,41.56863],[69.49545,41.545],[70.94483,42.26238],[70.85973,42.30188],[70.97717,42.50147],[71.15232,42.60486],[71.17807,42.67381],[71.22785,42.69248],[71.2724,42.77853],[71.53272,42.8014],[71.62405,42.76613],[71.88792,42.83578],[73.44393,42.43098],[73.50992,42.82356],[73.55634,43.03071],[74.22489,43.24657],[74.57491,43.13702],[74.64615,43.05881],[74.70331,43.02519],[74.75,42.99029],[74.88756,42.98612],[75.22619,42.85528],[75.29966,42.86183],[75.72174,42.79672],[75.82823,42.94848],[78.48469,42.89649],[78.91502,42.76839],[79.19763,42.804],[79.52921,42.44778],[79.97364,42.42816],[80.17807,42.21166],[80.26841,42.23797],[80.16892,42.61137],[80.26886,42.8366],[80.38169,42.83142],[80.58999,42.9011],[80.3735,43.01557],[80.62913,43.141],[80.78817,43.14235],[80.77771,43.30065],[80.69718,43.32589],[80.75156,43.44948],[80.40031,44.10986],[80.40229,44.23319],[80.38384,44.63073],[79.8987,44.89957],[80.11169,45.03352],[81.73278,45.3504],[82.51374,45.1755],[82.58474,45.40027],[82.21792,45.56619],[83.04622,47.19053],[83.92184,46.98912],[84.73077,47.01394],[84.93995,46.87399],[85.22443,47.04816],[85.54294,47.06171],[85.69696,47.2898],[85.61067,47.49753],[85.5169,48.05493],[85.73581,48.3939],[86.38069,48.46064],[86.75343,48.70331],[86.73568,48.99918],[86.87238,49.12432],[87.28386,49.11626],[87.31465,49.23603],[87.03071,49.25142],[86.82606,49.51796],[86.61307,49.60239],[86.79056,49.74787],[86.63674,49.80136],[86.18709,49.50259],[85.24047,49.60239],[84.99198,50.06793],[84.29385,50.27257],[83.8442,50.87375],[83.14607,51.00796],[82.55443,50.75412],[81.94999,50.79307],[81.46581,50.77658],[81.41248,50.97524],[81.06091,50.94833],[81.16999,51.15662],[80.80318,51.28262],[80.44819,51.20855],[80.4127,50.95581],[80.08138,50.77658],[79.11255,52.01171],[77.90383,53.29807],[76.54243,53.99329],[76.44076,54.16017],[76.82266,54.1798],[76.91052,54.4677],[75.3668,54.07439],[75.43398,53.98652],[75.07405,53.80831],[73.39218,53.44623],[73.25412,53.61532],[73.68921,53.86522],[73.74778,54.07194],[73.37963,53.96132],[72.71026,54.1161],[72.43415,53.92685],[72.17477,54.36303],[71.96141,54.17736],[71.10379,54.13326],[71.08706,54.33376],[71.24185,54.64965],[71.08288,54.71253],[70.96009,55.10558],[70.76493,55.3027],[70.19179,55.1476],[69.74917,55.35545],[69.34224,55.36344],[68.90865,55.38148],[68.19206,55.18823],[68.26661,55.09226],[68.21308,54.98645],[65.20174,54.55216],[65.24663,54.35721],[65.11033,54.33028],[64.97216,54.4212],[63.97686,54.29763],[64.02715,54.22679],[63.91224,54.20013],[63.80604,54.27079],[62.58651,54.05871],[62.56876,53.94047],[62.45931,53.90737],[62.38535,54.03961],[62.00966,54.04134],[62.03913,53.94768],[61.65318,54.02445],[61.56941,53.95703],[61.47603,54.08048],[61.3706,54.08464],[61.26863,53.92797],[60.99796,53.93699],[61.14283,53.90063],[61.22574,53.80268],[60.90626,53.62937],[61.55706,53.57144],[61.57185,53.50112],[61.37957,53.45887],[61.29082,53.50992],[61.14291,53.41481],[61.19024,53.30536],[62.14574,53.09626],[62.12799,52.99133],[62.0422,52.96105],[61.23462,53.03227],[61.05842,52.92217],[60.71989,52.75923],[60.71693,52.66245],[60.84118,52.63912],[60.84709,52.52228],[60.98021,52.50068],[61.05417,52.35096],[60.78201,52.22067],[60.72581,52.15538],[60.48915,52.15175],[60.19925,51.99173],[59.99809,51.98263],[60.09867,51.87135],[60.50986,51.7964],[60.36787,51.66815],[60.5424,51.61675],[60.92401,51.61124],[60.95655,51.48615],[61.50677,51.40687],[61.55114,51.32746],[61.6813,51.25716],[61.56889,51.23679],[61.4431,50.80679],[60.81833,50.6629],[60.31914,50.67705],[60.17262,50.83312],[60.01288,50.8163],[59.81172,50.54451],[59.51886,50.49937],[59.48928,50.64216],[58.87974,50.70852],[58.3208,51.15151],[57.75578,51.13852],[57.74986,50.93017],[57.44221,50.88354],[57.17302,51.11253],[56.17906,50.93204],[56.11398,50.7471],[55.67774,50.54508],[54.72067,51.03261],[54.56685,51.01958],[54.71476,50.61214],[54.55797,50.52006],[54.41894,50.61214],[54.46331,50.85554],[54.12248,51.11542],[53.69299,51.23466],[53.46165,51.49445],[52.54329,51.48444],[52.36119,51.74161],[51.8246,51.67916],[51.77431,51.49536],[51.301,51.48799],[51.26254,51.68466],[50.59695,51.61859],[50.26859,51.28677],[49.97277,51.2405],[49.76866,51.11067],[49.39001,51.09396],[49.41959,50.85927],[49.12673,50.78639],[48.86936,50.61589],[48.57946,50.63278],[48.90782,50.02281],[48.68352,49.89546],[48.42564,49.82283],[48.24519,49.86099],[48.10044,50.09242],[47.58551,50.47867],[47.30448,50.30894],[47.34589,50.09308],[47.18319,49.93721],[46.9078,49.86707],[46.78398,49.34026],[46.98795,49.23531],[47.04416,49.17152],[47.01458,49.07085],[46.91104,48.99715],[46.78392,48.95352],[46.49011,48.43019]]]}},{"type":"Feature","properties":{"id":"KE"},"geometry":{"type":"Polygon","coordinates":[[[33.90936,0.10581],[33.98449,-0.13079],[33.9264,-0.54188],[33.93107,-0.99298],[34.02286,-1.00779],[34.03084,-1.05101],[34.0824,-1.02264],[37.67199,-3.06222],[37.71745,-3.304],[37.5903,-3.42735],[37.63099,-3.50723],[37.75036,-3.54243],[37.81321,-3.69179],[39.21631,-4.67835],[39.44306,-4.93877],[39.62121,-4.68136],[41.75542,-1.85308],[41.56362,-1.66375],[41.56,-1.59812],[41.00099,-0.83068],[40.98767,2.82959],[41.31368,3.14314],[41.89488,3.97375],[41.1754,3.94079],[40.77498,4.27683],[39.86043,3.86974],[39.76808,3.67058],[39.58339,3.47434],[39.55132,3.39634],[39.51551,3.40895],[39.49444,3.45521],[39.19954,3.47834],[39.07736,3.5267],[38.91938,3.51198],[38.52336,3.62551],[38.45812,3.60445],[38.14168,3.62487],[37.07724,4.33503],[36.84474,4.44518],[36.03924,4.44406],[35.95449,4.53244],[35.9419,4.61933],[35.51424,4.61643],[35.42366,4.76969],[35.47843,4.91872],[35.30992,4.90402],[35.34151,5.02364],[34.47601,4.72162],[33.9873,4.23316],[34.06046,4.15235],[34.15429,3.80464],[34.45815,3.67385],[34.44922,3.51627],[34.39112,3.48802],[34.41794,3.44342],[34.40006,3.37949],[34.45815,3.18319],[34.56242,3.11478],[34.60114,2.93034],[34.65774,2.8753],[34.73967,2.85447],[34.78137,2.76223],[34.77244,2.70272],[34.95267,2.47209],[34.90947,2.42447],[34.98692,1.97348],[34.9899,1.6668],[34.92734,1.56109],[34.87819,1.5596],[34.7918,1.36752],[34.82606,1.30944],[34.82606,1.26626],[34.80223,1.22754],[34.67562,1.21265],[34.58029,1.14712],[34.57427,1.09868],[34.52369,1.10692],[34.43349,0.85254],[34.40041,0.80266],[34.31516,0.75693],[34.27345,0.63182],[34.20196,0.62289],[34.13493,0.58118],[34.11408,0.48884],[34.08727,0.44713],[34.10067,0.36372],[33.90936,0.10581]]]}},{"type":"Feature","properties":{"id":"KH"},"geometry":{"type":"Polygon","coordinates":[[[102.33828,13.55613],[102.361,13.50551],[102.35563,13.47307],[102.35692,13.38274],[102.34611,13.35618],[102.36001,13.31142],[102.36146,13.26006],[102.43422,13.09061],[102.46011,13.08057],[102.52275,12.99813],[102.48694,12.97537],[102.49335,12.92711],[102.53053,12.77506],[102.4994,12.71736],[102.51963,12.66117],[102.57567,12.65358],[102.7796,12.43781],[102.78116,12.40284],[102.73134,12.37091],[102.70176,12.1686],[102.77026,12.06815],[102.78427,11.98746],[102.83957,11.8519],[102.90973,11.75613],[102.91449,11.65512],[102.52395,11.25257],[102.47649,9.66162],[103.99198,10.48391],[104.43778,10.42386],[104.47963,10.43046],[104.49869,10.4057],[104.59018,10.53073],[104.87933,10.52833],[104.95094,10.64003],[105.09571,10.72722],[105.02722,10.89236],[105.08326,10.95656],[105.11449,10.96332],[105.34011,10.86179],[105.42884,10.96878],[105.50045,10.94586],[105.77751,11.03671],[105.86376,10.89839],[105.84603,10.85873],[105.93403,10.83853],[105.94535,10.9168],[106.06708,10.8098],[106.18539,10.79451],[106.14301,10.98176],[106.20095,10.97795],[106.1757,11.07301],[106.1527,11.10476],[106.10444,11.07879],[105.86782,11.28343],[105.88962,11.43605],[105.87328,11.55953],[105.81645,11.56876],[105.80867,11.60536],[105.8507,11.66635],[105.88962,11.67854],[105.95188,11.63738],[106.00792,11.7197],[106.02038,11.77457],[106.06708,11.77761],[106.13158,11.73283],[106.18539,11.75171],[106.26478,11.72122],[106.30525,11.67549],[106.37219,11.69836],[106.44691,11.66787],[106.45158,11.68616],[106.41577,11.76999],[106.44535,11.8279],[106.44068,11.86294],[106.4687,11.86751],[106.4111,11.97413],[106.70687,11.96956],[106.79405,12.0807],[106.92325,12.06548],[106.99953,12.08983],[107.15831,12.27547],[107.34511,12.33327],[107.42917,12.24657],[107.4463,12.29373],[107.55059,12.36824],[107.5755,12.52177],[107.55993,12.7982],[107.49611,12.88926],[107.49144,13.01215],[107.62843,13.3668],[107.61909,13.52577],[107.53503,13.73908],[107.45252,13.78897],[107.46498,13.91593],[107.44318,13.99751],[107.38247,13.99147],[107.35757,14.02319],[107.37158,14.07906],[107.33577,14.11832],[107.40427,14.24509],[107.39493,14.32655],[107.44941,14.41552],[107.48521,14.40346],[107.52569,14.54665],[107.52102,14.59034],[107.55371,14.628],[107.54361,14.69092],[107.47238,14.61523],[107.44435,14.52785],[107.37897,14.54443],[107.3276,14.58812],[107.29803,14.58963],[107.26534,14.54292],[107.256,14.48716],[107.21241,14.48716],[107.17038,14.41782],[107.09722,14.3937],[107.03962,14.45099],[107.04585,14.41782],[106.98825,14.36806],[106.9649,14.3198],[106.90574,14.33639],[106.8497,14.29416],[106.80767,14.31226],[106.73762,14.42687],[106.63333,14.44194],[106.59908,14.50977],[106.57106,14.50525],[106.54148,14.59565],[106.50723,14.58963],[106.45898,14.55045],[106.47766,14.50977],[106.43874,14.52032],[106.40916,14.45249],[106.32355,14.44043],[106.25194,14.48415],[106.21302,14.36203],[106.00131,14.36957],[105.99509,14.32734],[106.02311,14.30623],[106.04801,14.20363],[106.10872,14.18401],[106.11962,14.11307],[106.18656,14.06324],[106.16632,14.01794],[106.10094,13.98471],[106.10405,13.9137],[105.90791,13.92881],[105.78182,14.02247],[105.78338,14.08438],[105.5561,14.15684],[105.44869,14.10703],[105.36775,14.09948],[105.2759,14.17496],[105.20894,14.34967],[105.17748,14.34432],[105.14012,14.23873],[105.08408,14.20402],[105.02804,14.23722],[104.97667,14.38806],[104.69335,14.42726],[104.55014,14.36091],[104.27616,14.39861],[103.93836,14.3398],[103.70175,14.38052],[103.71109,14.4348],[103.53518,14.42575],[103.39353,14.35639],[103.16469,14.33075],[102.93275,14.19044],[102.91251,14.01531],[102.77864,13.93374],[102.72727,13.77806],[102.56848,13.69366],[102.5481,13.6589],[102.58635,13.6286],[102.62483,13.60883],[102.57573,13.60461],[102.5358,13.56933],[102.44601,13.5637],[102.36859,13.57488],[102.33828,13.55613]]]}},{"type":"Feature","properties":{"id":"KN"},"geometry":{"type":"Polygon","coordinates":[[[-63.11114,17.23125],[-62.62949,16.82364],[-62.27053,17.22145],[-62.76692,17.64353],[-63.11114,17.23125]]]}},{"type":"Feature","properties":{"id":"KR"},"geometry":{"type":"Polygon","coordinates":[[[122.80525,33.30571],[127.42045,32.33183],[129.2669,34.87122],[133.61399,37.41],[128.65655,38.61914],[128.37487,38.62345],[128.31105,38.58462],[128.27652,38.41657],[128.02917,38.31861],[127.55013,38.32257],[127.49672,38.30647],[127.38727,38.33227],[127.15749,38.30722],[127.04479,38.25518],[126.95338,38.17735],[126.95887,38.1347],[126.88106,38.10246],[126.84961,38.0344],[126.67023,37.95852],[126.68793,37.9175],[126.68793,37.83728],[126.66067,37.7897],[126.59918,37.76364],[126.56709,37.76857],[126.46818,37.80873],[126.43239,37.84095],[126.24402,37.83113],[126.19097,37.81462],[126.18776,37.74728],[126.13074,37.70512],[125.81159,37.72949],[125.37112,37.62643],[125.06408,37.66334],[124.87921,37.80827],[124.84224,37.977],[124.67666,38.05679],[123.85601,37.49093],[122.80525,33.30571]]]}},{"type":"Feature","properties":{"id":"XK"},"geometry":{"type":"Polygon","coordinates":[[[20.02088,42.74789],[20.02915,42.71147],[20.0969,42.65559],[20.07761,42.55582],[20.17127,42.50469],[20.21797,42.41237],[20.24399,42.32168],[20.34479,42.32656],[20.3819,42.3029],[20.48857,42.25444],[20.56955,42.12097],[20.55633,42.08173],[20.59434,42.03879],[20.63069,41.94913],[20.57946,41.91593],[20.59524,41.8818],[20.68523,41.85318],[20.76786,41.91839],[20.75464,42.05229],[21.11491,42.20794],[21.16614,42.19815],[21.22728,42.08909],[21.31983,42.10993],[21.29913,42.13954],[21.30496,42.1418],[21.38428,42.24465],[21.43882,42.23609],[21.43882,42.2789],[21.50823,42.27156],[21.52145,42.24465],[21.58992,42.25915],[21.56772,42.30946],[21.5264,42.33634],[21.53467,42.36809],[21.57021,42.3647],[21.59029,42.38042],[21.62887,42.37664],[21.64209,42.41081],[21.62556,42.45106],[21.7035,42.51899],[21.70522,42.54176],[21.7327,42.55041],[21.75672,42.62695],[21.79413,42.65923],[21.75025,42.70125],[21.6626,42.67813],[21.58755,42.70418],[21.59154,42.72643],[21.47498,42.74695],[21.39045,42.74888],[21.44047,42.87276],[21.36941,42.87397],[21.32974,42.90424],[21.2719,42.8994],[21.23534,42.95523],[21.23877,43.00848],[21.2041,43.02277],[21.16734,42.99694],[21.14465,43.11089],[21.08952,43.13471],[21.05378,43.10707],[21.00749,43.13984],[20.96287,43.12416],[20.83727,43.17842],[20.88685,43.21697],[20.82145,43.26769],[20.73811,43.25068],[20.68688,43.21335],[20.59929,43.20492],[20.69515,43.09641],[20.64557,43.00826],[20.59929,43.01067],[20.48692,42.93208],[20.53484,42.8885],[20.43734,42.83157],[20.40594,42.84853],[20.35692,42.8335],[20.27869,42.81945],[20.2539,42.76245],[20.04898,42.77701],[20.02088,42.74789]]]}},{"type":"Feature","properties":{"id":"KW"},"geometry":{"type":"Polygon","coordinates":[[[46.5527,29.10283],[47.46202,29.0014],[47.58376,28.83382],[47.59863,28.66798],[47.70561,28.5221],[48.42991,28.53628],[49.00421,28.81495],[48.59531,29.66815],[48.40479,29.85763],[48.17332,30.02448],[48.06782,30.02906],[48.01114,29.98906],[47.7095,30.10453],[47.37192,30.10421],[47.15166,30.01044],[46.89695,29.50584],[46.5527,29.10283]]]}},{"type":"Feature","properties":{"id":"IN-JK"},"geometry":{"type":"Polygon","coordinates":[[[73.74862,34.34183],[73.8475,34.32935],[73.90517,34.35317],[73.98208,34.2522],[73.90677,34.10504],[73.88732,34.05105],[73.91341,34.01235],[74.21554,34.03853],[74.25262,34.01577],[74.26086,33.92237],[74.14001,33.83002],[74.05898,33.82089],[74.00891,33.75437],[73.96423,33.73071],[73.98968,33.66155],[73.97367,33.64061],[74.03576,33.56718],[74.10115,33.56392],[74.18121,33.4745],[74.17983,33.3679],[74.08782,33.26232],[74.01366,33.25199],[74.02144,33.18908],[74.15374,33.13477],[74.17571,33.07495],[74.31854,33.02891],[74.34875,32.97823],[74.31227,32.92795],[74.41467,32.90563],[74.45312,32.77755],[74.6289,32.75561],[74.64675,32.82604],[74.7113,32.84219],[74.65345,32.71225],[74.69542,32.66792],[74.64424,32.60985],[74.65251,32.56416],[74.67431,32.56676],[74.68362,32.49298],[74.84725,32.49075],[74.97634,32.45367],[75.03265,32.49538],[75.28259,32.36556],[75.49941,32.28074],[75.57083,32.36836],[75.78506,32.47095],[75.82936,32.52664],[75.92513,32.64689],[75.77888,32.9355],[75.95329,32.88362],[76.31584,33.1341],[76.76902,33.26337],[77.32795,32.82305],[77.66784,32.97007],[77.88345,32.7942],[77.97477,32.58905],[78.32565,32.75263],[78.38897,32.53938],[78.73916,32.69438],[78.7831,32.46873],[78.96713,32.33655],[78.99322,32.37948],[79.0979,32.38051],[79.13174,32.47766],[79.26768,32.53277],[79.46562,32.69668],[79.14016,33.02545],[79.15252,33.17156],[78.73636,33.56521],[78.67599,33.66445],[78.77349,33.73871],[78.73367,34.01121],[78.65657,34.03195],[78.66225,34.08858],[78.91769,34.15452],[78.99802,34.3027],[79.05364,34.32482],[78.74465,34.45174],[78.56475,34.50835],[78.54964,34.57283],[78.27781,34.61484],[78.18435,34.7998],[78.22692,34.88771],[78.00033,35.23954],[78.03466,35.3785],[78.11664,35.48022],[77.80532,35.52058],[77.70232,35.46244],[77.44277,35.46132],[76.96624,35.5932],[76.84539,35.67356],[76.77323,35.66062],[76.75475,35.52617],[76.85088,35.39754],[76.93465,35.39866],[77.11796,35.05419],[76.99251,34.93349],[76.87193,34.96906],[76.74514,34.92488],[76.74377,34.84039],[76.67648,34.76371],[76.47186,34.78965],[76.15463,34.6429],[76.04614,34.67566],[75.75438,34.51827],[75.38009,34.55021],[75.01479,34.64629],[74.6663,34.703],[74.58083,34.77386],[74.31239,34.79626],[74.12897,34.70073],[73.96423,34.68244],[73.93401,34.63386],[73.93951,34.57169],[73.89419,34.54568],[73.88732,34.48911],[73.74999,34.3781],[73.74862,34.34183]]]}},{"type":"Feature","properties":{"id":"LA"},"geometry":{"type":"Polygon","coordinates":[[[100.08404,20.36626],[100.09999,20.31614],[100.09337,20.26293],[100.11785,20.24787],[100.1712,20.24324],[100.16668,20.2986],[100.22076,20.31598],[100.25769,20.3992],[100.33383,20.4028],[100.37439,20.35156],[100.41473,20.25625],[100.44992,20.23644],[100.4537,20.19971],[100.47567,20.19133],[100.51052,20.14928],[100.55218,20.17741],[100.58808,20.15791],[100.5094,19.87904],[100.398,19.75047],[100.49604,19.53504],[100.58219,19.49164],[100.64606,19.55884],[100.77231,19.48324],[100.90302,19.61901],[101.08928,19.59748],[101.26545,19.59242],[101.26991,19.48324],[101.21347,19.46223],[101.20604,19.35296],[101.24911,19.33334],[101.261,19.12717],[101.35606,19.04716],[101.25803,18.89545],[101.22832,18.73377],[101.27585,18.68875],[101.06047,18.43247],[101.18227,18.34367],[101.15108,18.25624],[101.19118,18.2125],[101.1793,18.0544],[101.02185,17.87637],[100.96541,17.57926],[101.15108,17.47586],[101.44667,17.7392],[101.72294,17.92867],[101.78087,18.07559],[101.88485,18.02474],[102.11359,18.21532],[102.45523,17.97106],[102.59234,17.96127],[102.60971,17.95411],[102.61432,17.92273],[102.5896,17.84889],[102.59485,17.83537],[102.68194,17.80151],[102.69946,17.81686],[102.67543,17.84529],[102.68538,17.86653],[102.75954,17.89561],[102.79044,17.93612],[102.81988,17.94233],[102.86323,17.97531],[102.95812,18.0054],[102.9912,17.9949],[103.01998,17.97095],[103.0566,18.00144],[103.07823,18.03833],[103.07343,18.12351],[103.1493,18.17799],[103.14994,18.23172],[103.17093,18.2618],[103.29757,18.30475],[103.23818,18.34875],[103.24779,18.37807],[103.30977,18.4341],[103.41044,18.4486],[103.47773,18.42841],[103.60957,18.40528],[103.699,18.34125],[103.82449,18.33979],[103.85642,18.28666],[103.93916,18.33914],[103.97725,18.33631],[104.06533,18.21656],[104.10927,18.10826],[104.21776,17.99335],[104.2757,17.86139],[104.35432,17.82871],[104.45404,17.66788],[104.69867,17.53038],[104.80061,17.39367],[104.80716,17.19025],[104.73712,17.01404],[104.7373,16.91125],[104.76442,16.84752],[104.7397,16.81005],[104.76099,16.69302],[104.73349,16.565],[104.88057,16.37311],[105.00262,16.25627],[105.06204,16.09792],[105.42001,16.00657],[105.38508,15.987],[105.34115,15.92737],[105.37959,15.84074],[105.42285,15.76971],[105.46573,15.74742],[105.61756,15.68792],[105.60446,15.53301],[105.58191,15.41031],[105.47635,15.3796],[105.4692,15.33709],[105.50662,15.32054],[105.58043,15.32724],[105.46661,15.13132],[105.61162,15.00037],[105.5121,14.80802],[105.53864,14.55731],[105.43783,14.43865],[105.20894,14.34967],[105.2759,14.17496],[105.36775,14.09948],[105.44869,14.10703],[105.5561,14.15684],[105.78338,14.08438],[105.78182,14.02247],[105.90791,13.92881],[106.10405,13.9137],[106.10094,13.98471],[106.16632,14.01794],[106.18656,14.06324],[106.11962,14.11307],[106.10872,14.18401],[106.04801,14.20363],[106.02311,14.30623],[105.99509,14.32734],[106.00131,14.36957],[106.21302,14.36203],[106.25194,14.48415],[106.32355,14.44043],[106.40916,14.45249],[106.43874,14.52032],[106.47766,14.50977],[106.45898,14.55045],[106.50723,14.58963],[106.54148,14.59565],[106.57106,14.50525],[106.59908,14.50977],[106.63333,14.44194],[106.73762,14.42687],[106.80767,14.31226],[106.8497,14.29416],[106.90574,14.33639],[106.9649,14.3198],[106.98825,14.36806],[107.04585,14.41782],[107.03962,14.45099],[107.09722,14.3937],[107.17038,14.41782],[107.21241,14.48716],[107.256,14.48716],[107.26534,14.54292],[107.29803,14.58963],[107.3276,14.58812],[107.37897,14.54443],[107.44435,14.52785],[107.47238,14.61523],[107.54361,14.69092],[107.51579,14.79282],[107.59285,14.87795],[107.48277,14.93751],[107.46516,15.00982],[107.61486,15.0566],[107.61926,15.13949],[107.58844,15.20111],[107.62587,15.2266],[107.60605,15.37524],[107.62367,15.42193],[107.53341,15.40496],[107.50699,15.48771],[107.3815,15.49832],[107.34408,15.62345],[107.27583,15.62769],[107.27143,15.71459],[107.21859,15.74638],[107.21419,15.83747],[107.34188,15.89464],[107.39471,15.88829],[107.46296,16.01106],[107.44975,16.08511],[107.33968,16.05549],[107.25822,16.13587],[107.14595,16.17816],[107.15035,16.26271],[107.09091,16.3092],[107.02597,16.31132],[106.97385,16.30204],[106.96638,16.34938],[106.88067,16.43594],[106.88727,16.52671],[106.84104,16.55415],[106.74418,16.41904],[106.65832,16.47816],[106.66052,16.56892],[106.61477,16.60713],[106.58267,16.6012],[106.59013,16.62259],[106.55485,16.68704],[106.55265,16.86831],[106.52183,16.87884],[106.51963,16.92097],[106.54824,16.92729],[106.55045,17.0031],[106.50862,16.9673],[106.43597,17.01362],[106.31929,17.20509],[106.29287,17.3018],[106.24444,17.24714],[106.18991,17.28227],[106.09019,17.36399],[105.85744,17.63221],[105.76612,17.67147],[105.60381,17.89356],[105.64784,17.96687],[105.46292,18.22008],[105.38366,18.15315],[105.15942,18.38691],[105.10408,18.43533],[105.1327,18.58355],[105.19654,18.64196],[105.12829,18.70453],[104.64617,18.85668],[104.5361,18.97747],[103.87125,19.31854],[104.06058,19.43484],[104.10832,19.51575],[104.05617,19.61743],[104.06498,19.66926],[104.23229,19.70242],[104.41281,19.70035],[104.53169,19.61743],[104.64837,19.62365],[104.68359,19.72729],[104.8355,19.80395],[104.8465,19.91783],[104.9874,20.09573],[104.91695,20.15567],[104.86852,20.14121],[104.61315,20.24452],[104.62195,20.36633],[104.72102,20.40554],[104.66158,20.47774],[104.47886,20.37459],[104.40621,20.3849],[104.38199,20.47155],[104.63957,20.6653],[104.27412,20.91433],[104.11121,20.96779],[103.98024,20.91531],[103.82282,20.8732],[103.73478,20.6669],[103.68633,20.66324],[103.45737,20.82382],[103.38032,20.79501],[103.21497,20.89832],[103.12055,20.89994],[103.03469,21.05821],[102.97745,21.05821],[102.89825,21.24707],[102.80794,21.25736],[102.88939,21.3107],[102.94223,21.46034],[102.86297,21.4255],[102.98846,21.58936],[102.97965,21.74076],[102.86077,21.71213],[102.85637,21.84501],[102.81894,21.83888],[102.82115,21.73667],[102.74189,21.66713],[102.67145,21.65894],[102.62301,21.91447],[102.49092,21.99002],[102.51734,22.02676],[102.18712,22.30403],[102.14099,22.40092],[102.1245,22.43372],[102.03633,22.46164],[101.98487,22.42766],[101.91344,22.44417],[101.90714,22.38688],[101.86828,22.38397],[101.7685,22.50337],[101.68973,22.46843],[101.61306,22.27515],[101.56789,22.28876],[101.53638,22.24794],[101.60675,22.13513],[101.57525,22.13026],[101.62566,21.96574],[101.7791,21.83019],[101.74555,21.72852],[101.83257,21.61562],[101.80001,21.57461],[101.7475,21.5873],[101.7727,21.51794],[101.74224,21.48276],[101.74014,21.30967],[101.84412,21.25291],[101.83887,21.20983],[101.76745,21.21571],[101.79266,21.19025],[101.7622,21.14813],[101.70548,21.14911],[101.66977,21.20004],[101.60886,21.17947],[101.59491,21.18621],[101.6068,21.23329],[101.54563,21.25668],[101.29326,21.17254],[101.2229,21.23271],[101.26912,21.36482],[101.19349,21.41959],[101.2124,21.56422],[101.15156,21.56129],[101.16198,21.52808],[101.00234,21.39612],[100.80173,21.2934],[100.72716,21.31786],[100.63578,21.05639],[100.55281,21.02796],[100.50974,20.88574],[100.64628,20.88279],[100.60112,20.8347],[100.51079,20.82194],[100.36375,20.82783],[100.1957,20.68247],[100.08404,20.36626]]]}},{"type":"Feature","properties":{"id":"LB"},"geometry":{"type":"Polygon","coordinates":[[[34.78515,33.20368],[35.10645,33.09318],[35.1924,33.08743],[35.31429,33.10515],[35.35223,33.05617],[35.43059,33.06659],[35.448,33.09264],[35.50272,33.09056],[35.50335,33.114],[35.52573,33.11921],[35.54228,33.19865],[35.5362,33.23196],[35.54808,33.236],[35.54544,33.25513],[35.55555,33.25844],[35.56523,33.28969],[35.58326,33.28381],[35.58502,33.26653],[35.62283,33.24226],[35.62019,33.27278],[35.77477,33.33609],[35.81324,33.36354],[35.82577,33.40479],[35.88668,33.43183],[35.94816,33.47886],[35.94465,33.52774],[36.05723,33.57904],[35.9341,33.6596],[36.06778,33.82927],[36.14517,33.85118],[36.3967,33.83365],[36.38263,33.86579],[36.28589,33.91981],[36.41078,34.05253],[36.50576,34.05982],[36.5128,34.09916],[36.62537,34.20251],[36.59195,34.2316],[36.58667,34.27667],[36.60778,34.31009],[36.56556,34.31881],[36.53039,34.3798],[36.55853,34.41609],[36.46179,34.46541],[36.4442,34.50165],[36.34745,34.5002],[36.3369,34.52629],[36.39846,34.55672],[36.41429,34.61175],[36.45299,34.59438],[36.46003,34.6378],[36.42941,34.62505],[36.35384,34.65447],[36.35135,34.68516],[36.32399,34.69334],[36.29165,34.62991],[35.98718,34.64977],[35.97386,34.63322],[35.48515,34.70851],[34.78515,33.20368]]]}},{"type":"Feature","properties":{"id":"LR"},"geometry":{"type":"Polygon","coordinates":[[[-12.15048,6.15992],[-7.52774,3.7105],[-7.53259,4.35145],[-7.59349,4.8909],[-7.53876,4.94294],[-7.55369,5.08667],[-7.48901,5.14118],[-7.46165,5.26256],[-7.36463,5.32944],[-7.43428,5.42355],[-7.37209,5.61173],[-7.43926,5.74787],[-7.43677,5.84687],[-7.46165,5.84934],[-7.48155,5.80974],[-7.67309,5.94337],[-7.70294,5.90625],[-7.78254,5.99037],[-7.79747,6.07696],[-7.8497,6.08932],[-7.83478,6.20309],[-7.90692,6.27728],[-8.00642,6.31684],[-8.17557,6.28222],[-8.3298,6.36381],[-8.38453,6.35887],[-8.45666,6.49977],[-8.48652,6.43797],[-8.59456,6.50612],[-8.31736,6.82837],[-8.29249,7.1691],[-8.37458,7.25794],[-8.41935,7.51203],[-8.47114,7.55676],[-8.55874,7.62525],[-8.55874,7.70167],[-8.67814,7.69428],[-8.72789,7.51429],[-8.8448,7.35149],[-8.85724,7.26019],[-8.93435,7.2824],[-9.09107,7.1985],[-9.18311,7.30461],[-9.20798,7.38109],[-9.305,7.42056],[-9.41943,7.41809],[-9.48161,7.37122],[-9.37465,7.62032],[-9.35724,7.74111],[-9.44928,7.9284],[-9.41445,8.02448],[-9.50898,8.18455],[-9.47415,8.35195],[-9.77763,8.54633],[-10.05873,8.42578],[-10.05375,8.50697],[-10.14579,8.52665],[-10.203,8.47991],[-10.27575,8.48711],[-10.30084,8.30008],[-10.31635,8.28554],[-10.29839,8.21283],[-10.35227,8.15223],[-10.45023,8.15627],[-10.51554,8.1393],[-10.57523,8.04829],[-10.60492,8.04072],[-10.60422,7.7739],[-11.29417,7.21576],[-11.4027,6.97746],[-11.50429,6.92704],[-12.15048,6.15992]]]}},{"type":"Feature","properties":{"id":"LY"},"geometry":{"type":"Polygon","coordinates":[[[9.3876,30.16738],[9.78136,29.40961],[9.89569,26.57696],[9.51696,26.39148],[9.38834,26.19288],[10.03146,25.35635],[10.02432,24.98124],[10.33159,24.5465],[10.85323,24.5595],[11.41061,24.21456],[11.62498,24.26669],[11.96886,23.51735],[13.5631,23.16574],[14.22918,22.61719],[14.99751,23.00539],[15.99566,23.49639],[23.99539,19.49944],[23.99715,20.00038],[24.99794,19.99661],[24.99885,21.99535],[24.99968,29.24574],[24.71117,30.17441],[25.01077,30.73861],[24.83101,31.31921],[25.06041,31.57937],[25.14001,31.67534],[25.63787,31.9359],[22.5213,33.45682],[11.66543,33.34642],[11.56255,33.16754],[11.55852,33.1409],[11.51549,33.09826],[11.46037,32.6307],[11.57828,32.48013],[11.53898,32.4138],[11.04234,32.2145],[10.7315,31.97235],[10.62788,31.96629],[10.48497,31.72956],[10.31364,31.72648],[10.12239,31.42098],[10.29516,30.90337],[9.88152,30.34074],[9.76848,30.34366],[9.55544,30.23971],[9.3876,30.16738]]]}},{"type":"Feature","properties":{"id":"LC"},"geometry":{"type":"Polygon","coordinates":[[[-61.43129,13.68336],[-60.70539,13.41452],[-60.5958,14.23076],[-61.26561,14.25664],[-61.43129,13.68336]]]}},{"type":"Feature","properties":{"id":"LI"},"geometry":{"type":"Polygon","coordinates":[[[9.47139,47.06402],[9.47548,47.05257],[9.54041,47.06495],[9.55721,47.04762],[9.60717,47.06091],[9.61216,47.07732],[9.63395,47.08443],[9.62623,47.14685],[9.56539,47.17124],[9.58264,47.20673],[9.56981,47.21926],[9.55176,47.22585],[9.56766,47.24281],[9.53116,47.27029],[9.52406,47.24959],[9.50318,47.22153],[9.4891,47.19346],[9.48774,47.17402],[9.51044,47.13727],[9.52089,47.10019],[9.51362,47.08505],[9.47139,47.06402]]]}},{"type":"Feature","properties":{"id":"CN-HN"},"geometry":{"type":"Polygon","coordinates":[[[108.78936,27.08908],[108.87451,27.00652],[109.13268,27.06462],[109.40734,27.15264],[109.49043,27.06524],[109.51652,26.75726],[109.28581,26.70022],[109.38056,26.57931],[109.28649,26.28664],[109.45129,26.30449],[109.47669,26.03334],[109.72663,26.00001],[109.68749,25.88517],[109.82276,25.88023],[109.81109,26.04259],[109.97486,26.19241],[110.09811,26.01914],[110.31028,25.96915],[110.61172,26.33465],[110.75317,26.25277],[110.9262,26.26694],[110.96328,26.3851],[111.09306,26.31188],[111.29287,26.24662],[111.19125,25.94693],[111.25579,25.85922],[111.48239,25.88208],[111.37596,25.73001],[111.30042,25.70155],[111.31347,25.47613],[110.97015,25.10922],[110.98525,24.9157],[111.28326,25.14528],[111.46179,25.02526],[111.42745,24.6832],[111.52633,24.63952],[111.68666,24.78486],[112.04166,24.77987],[112.15015,24.82662],[112.17143,24.91944],[112.11547,24.96582],[112.13504,25.00535],[112.18963,25.18987],[112.65174,25.13751],[112.70393,25.08622],[112.7798,24.89328],[113.00571,24.93999],[112.96674,25.16361],[113.02854,25.20059],[112.98923,25.24857],[112.8713,25.24857],[112.84984,25.34278],[112.89653,25.31501],[113.01721,25.34976],[113.12725,25.47039],[113.26217,25.51486],[113.56635,25.30802],[113.98315,25.40916],[113.91448,25.69908],[114.00787,25.89258],[114.02606,26.021],[114.23789,26.20042],[113.94058,26.18193],[114.07482,26.40847],[114.104,26.57624],[113.91208,26.61277],[113.86402,26.65513],[113.83895,26.79342],[113.91517,26.94716],[113.77715,27.11781],[113.875,27.38548],[113.62232,27.34859],[113.62232,27.41017],[113.5976,27.42114],[113.59863,27.63365],[113.75656,27.81782],[113.72085,27.8755],[113.74351,27.9477],[114.03945,28.06289],[113.99963,28.15253],[114.25094,28.38233],[114.07653,28.55919],[114.14932,28.77187],[114.01817,28.89758],[113.9447,29.05196],[113.85749,29.04056],[113.81835,29.10837],[113.69132,29.06637],[113.69132,29.2187],[113.59588,29.25914],[113.7569,29.44767],[113.62403,29.52118],[113.7363,29.58928],[113.56704,29.67254],[113.51898,29.82813],[113.15505,29.46112],[112.94769,29.47188],[112.9264,29.69014],[112.8955,29.79328],[112.80796,29.74917],[112.68093,29.59734],[112.46566,29.6433],[112.28713,29.50147],[112.2377,29.65673],[112.10861,29.66001],[112.06706,29.73188],[111.95548,29.82843],[111.79447,29.91209],[111.6053,29.89006],[111.51912,29.93232],[111.40274,29.91268],[111.24481,30.04383],[110.94646,30.06493],[110.75729,30.12523],[110.75386,30.04888],[110.52246,30.06523],[110.4943,29.91923],[110.64742,29.77272],[110.37036,29.63345],[110.14514,29.78374],[109.78637,29.76556],[109.70088,29.60928],[109.53609,29.62331],[109.45678,29.55613],[109.2346,29.11829],[109.31877,29.05827],[109.23208,28.87985],[109.23688,28.78255],[109.29885,28.73018],[109.18556,28.63184],[109.19466,28.60275],[109.29671,28.62913],[109.31576,28.58595],[109.29121,28.57268],[109.27645,28.52202],[109.26727,28.51787],[109.26898,28.50437],[109.26993,28.49849],[109.25302,28.4711],[109.2616,28.39034],[109.28495,28.3772],[109.26881,28.3145],[109.39807,28.27717],[109.29336,28.05531],[109.38056,28.03683],[109.31533,27.9856],[109.33799,27.78198],[109.46708,27.69508],[109.45197,27.5722],[109.2937,27.42541],[109.14642,27.45344],[109.10659,27.3495],[109.04651,27.33578],[109.0472,27.29643],[108.78936,27.08908]]]}},{"type":"Feature","properties":{"id":"LS"},"geometry":{"type":"Polygon","coordinates":[[[27.01016,-29.65439],[27.09489,-29.72796],[27.22719,-30.00718],[27.29603,-30.05473],[27.32555,-30.14785],[27.40778,-30.14577],[27.37293,-30.19401],[27.36649,-30.27246],[27.38108,-30.33456],[27.45452,-30.32239],[27.56901,-30.42504],[27.56781,-30.44562],[27.62137,-30.50509],[27.6521,-30.51707],[27.67819,-30.53437],[27.69467,-30.55862],[27.74814,-30.60635],[28.12073,-30.68072],[28.2319,-30.28476],[28.399,-30.1592],[28.68627,-30.12885],[28.80222,-30.10579],[28.9338,-30.05072],[29.16548,-29.91706],[29.12553,-29.76266],[29.28545,-29.58456],[29.33204,-29.45598],[29.44883,-29.3772],[29.40524,-29.21246],[28.68043,-28.58744],[28.65091,-28.57025],[28.40612,-28.6215],[28.30518,-28.69531],[28.2348,-28.69471],[28.1317,-28.7293],[28.02503,-28.85991],[27.98675,-28.8787],[27.9392,-28.84864],[27.88933,-28.88156],[27.8907,-28.91612],[27.75458,-28.89839],[27.55974,-29.18954],[27.5158,-29.2261],[27.54258,-29.25575],[27.48679,-29.29349],[27.45125,-29.29708],[27.47254,-29.31968],[27.4358,-29.33465],[27.33464,-29.48161],[27.01016,-29.65439]]]}},{"type":"Feature","properties":{"id":"LT"},"geometry":{"type":"Polygon","coordinates":[[[20.60454,55.40986],[20.95181,55.27994],[21.26425,55.24456],[21.35465,55.28427],[21.38446,55.29348],[21.46766,55.21115],[21.51095,55.18507],[21.55605,55.20311],[21.64954,55.1791],[21.85521,55.09493],[21.96505,55.07353],[21.99543,55.08691],[22.03984,55.07888],[22.02582,55.05078],[22.06087,55.02935],[22.11697,55.02131],[22.14267,55.05345],[22.31562,55.0655],[22.47688,55.04408],[22.58907,55.07085],[22.60075,55.01863],[22.65451,54.97037],[22.68723,54.9811],[22.76422,54.92521],[22.85083,54.88711],[22.87317,54.79492],[22.73631,54.72952],[22.73397,54.66604],[22.75467,54.6483],[22.74225,54.64339],[22.7522,54.63525],[22.68021,54.58486],[22.71293,54.56454],[22.67788,54.532],[22.70208,54.45312],[22.7253,54.41732],[22.79705,54.36264],[22.83756,54.40827],[23.00584,54.38514],[22.99649,54.35927],[23.05726,54.34565],[23.04323,54.31567],[23.104,54.29794],[23.13905,54.31567],[23.15526,54.31076],[23.15938,54.29894],[23.24656,54.25701],[23.3494,54.25155],[23.39525,54.21672],[23.42418,54.17911],[23.45223,54.17775],[23.49196,54.14764],[23.52702,54.04622],[23.48261,53.98855],[23.51284,53.95052],[23.61677,53.92691],[23.71726,53.93379],[23.80543,53.89558],[23.81309,53.94205],[23.95098,53.9613],[23.98837,53.92554],[24.19638,53.96405],[24.34128,53.90076],[24.44411,53.90076],[24.62275,54.00217],[24.69652,54.01901],[24.69185,53.96543],[24.74279,53.96663],[24.85311,54.02862],[24.77131,54.11091],[24.96894,54.17589],[24.991,54.14241],[25.0728,54.13419],[25.19199,54.219],[25.22705,54.26271],[25.35559,54.26544],[25.509,54.30267],[25.56823,54.25212],[25.51452,54.17799],[25.54724,54.14925],[25.64875,54.1259],[25.71084,54.16704],[25.78563,54.15747],[25.78553,54.23327],[25.68513,54.31727],[25.55425,54.31591],[25.5376,54.33158],[25.63371,54.42075],[25.62203,54.4656],[25.64813,54.48704],[25.68045,54.5321],[25.75977,54.57252],[25.74122,54.80108],[25.89462,54.93438],[25.99129,54.95705],[26.05907,54.94631],[26.13386,54.98924],[26.20397,54.99729],[26.26941,55.08032],[26.23202,55.10439],[26.30628,55.12536],[26.35121,55.1525],[26.46249,55.12814],[26.51481,55.16051],[26.54753,55.14181],[26.69243,55.16718],[26.68075,55.19787],[26.72983,55.21788],[26.73017,55.24226],[26.835,55.28182],[26.83266,55.30444],[26.80929,55.31642],[26.6714,55.33902],[26.5709,55.32572],[26.44937,55.34832],[26.5522,55.40277],[26.55094,55.5093],[26.63167,55.57887],[26.63231,55.67968],[26.58248,55.6754],[26.46661,55.70375],[26.39561,55.71156],[26.18509,55.86813],[26.03815,55.95884],[25.90047,56.0013],[25.85893,56.00188],[25.81773,56.05444],[25.69246,56.08892],[25.68588,56.14725],[25.53621,56.16663],[25.39751,56.15707],[25.23099,56.19147],[25.09325,56.1878],[25.05762,56.26742],[24.89005,56.46666],[24.83686,56.41565],[24.70022,56.40483],[24.57353,56.31525],[24.58143,56.29125],[24.42746,56.26522],[24.32334,56.30226],[24.13139,56.24881],[24.02657,56.3231],[23.75726,56.37282],[23.49803,56.34307],[23.40486,56.37689],[23.31606,56.3827],[23.17312,56.36795],[23.09531,56.30511],[22.96988,56.41213],[22.83048,56.367],[22.69354,56.36284],[22.56441,56.39305],[22.3361,56.4016],[22.09728,56.42851],[22.00548,56.41508],[21.74558,56.33181],[21.57888,56.31406],[21.49736,56.29106],[21.24644,56.16917],[21.15016,56.07818],[20.68447,56.04073],[20.60454,55.40986]]]}},{"type":"Feature","properties":{"id":"LU"},"geometry":{"type":"Polygon","coordinates":[[[5.73621,49.89796],[5.78415,49.87922],[5.75269,49.8711],[5.75861,49.85631],[5.74567,49.85368],[5.75884,49.84811],[5.74953,49.84709],[5.74975,49.83933],[5.74076,49.83823],[5.7404,49.83452],[5.74844,49.82435],[5.74364,49.82058],[5.74953,49.81428],[5.75409,49.79239],[5.78871,49.7962],[5.82245,49.75048],[5.83149,49.74729],[5.82562,49.72395],[5.84193,49.72161],[5.86503,49.72739],[5.88677,49.70951],[5.86527,49.69291],[5.86175,49.67862],[5.9069,49.66377],[5.90164,49.6511],[5.90599,49.63853],[5.88552,49.63507],[5.88393,49.62802],[5.87609,49.62047],[5.8762,49.60898],[5.84826,49.5969],[5.84971,49.58674],[5.86986,49.58756],[5.87256,49.57539],[5.8424,49.56082],[5.84692,49.55663],[5.84143,49.5533],[5.81838,49.54777],[5.80871,49.5425],[5.81664,49.53775],[5.83648,49.5425],[5.84466,49.53027],[5.83467,49.52717],[5.83389,49.52152],[5.86571,49.50015],[5.94128,49.50034],[5.94224,49.49608],[5.96876,49.49053],[5.97693,49.45513],[6.02648,49.45451],[6.02743,49.44845],[6.04176,49.44801],[6.05553,49.46663],[6.07887,49.46399],[6.08373,49.45594],[6.10072,49.45268],[6.09845,49.46351],[6.10325,49.4707],[6.12346,49.4735],[6.12814,49.49365],[6.14321,49.48796],[6.16115,49.49297],[6.15366,49.50226],[6.17386,49.50934],[6.19543,49.50536],[6.2409,49.51408],[6.25029,49.50609],[6.27875,49.503],[6.28818,49.48465],[6.3687,49.4593],[6.36778,49.46937],[6.36907,49.48931],[6.36788,49.50377],[6.35666,49.52931],[6.38072,49.55171],[6.38228,49.55855],[6.35825,49.57053],[6.36676,49.57813],[6.38024,49.57593],[6.38342,49.5799],[6.37464,49.58886],[6.385,49.59946],[6.39822,49.60081],[6.41861,49.61723],[6.4413,49.65722],[6.43768,49.66021],[6.42726,49.66078],[6.42937,49.66857],[6.44654,49.67799],[6.46048,49.69092],[6.48014,49.69767],[6.49785,49.71118],[6.50647,49.71353],[6.5042,49.71808],[6.49694,49.72205],[6.49535,49.72645],[6.50261,49.72718],[6.51397,49.72058],[6.51805,49.72425],[6.50193,49.73291],[6.50174,49.75292],[6.51646,49.75961],[6.51828,49.76855],[6.51056,49.77515],[6.51669,49.78336],[6.50534,49.78952],[6.52169,49.79787],[6.53122,49.80666],[6.52121,49.81338],[6.51215,49.80124],[6.50647,49.80916],[6.48718,49.81267],[6.47111,49.82263],[6.45425,49.81164],[6.44131,49.81443],[6.42905,49.81091],[6.42521,49.81591],[6.40022,49.82029],[6.36576,49.85032],[6.34267,49.84974],[6.33585,49.83785],[6.32098,49.83728],[6.32303,49.85133],[6.30963,49.87021],[6.29692,49.86685],[6.28874,49.87592],[6.26146,49.88203],[6.23496,49.89972],[6.22926,49.92096],[6.21882,49.92403],[6.22608,49.929],[6.22094,49.94955],[6.19856,49.95053],[6.19089,49.96991],[6.18045,49.96611],[6.18554,49.95622],[6.17872,49.9537],[6.16466,49.97086],[6.1701,49.98518],[6.14147,49.99563],[6.14948,50.00908],[6.13806,50.01056],[6.1295,50.01849],[6.13273,50.02019],[6.13794,50.01466],[6.14666,50.02207],[6.13044,50.02929],[6.13458,50.04141],[6.11274,50.05916],[6.12055,50.09171],[6.1379,50.12964],[6.1137,50.13668],[6.12028,50.16374],[6.08577,50.17246],[6.06406,50.15344],[6.03093,50.16362],[6.02488,50.18283],[5.96453,50.17259],[5.95929,50.13295],[5.89488,50.11476],[5.8857,50.07824],[5.85474,50.06342],[5.86904,50.04614],[5.8551,50.02683],[5.81866,50.01286],[5.82331,49.99662],[5.83968,49.9892],[5.83467,49.97823],[5.81163,49.97142],[5.80833,49.96451],[5.77291,49.96056],[5.77314,49.93646],[5.73621,49.89796]]]}},{"type":"Feature","properties":{"id":"LV"},"geometry":{"type":"Polygon","coordinates":[[[19.64795,57.06466],[20.68447,56.04073],[21.15016,56.07818],[21.24644,56.16917],[21.49736,56.29106],[21.57888,56.31406],[21.74558,56.33181],[22.00548,56.41508],[22.09728,56.42851],[22.3361,56.4016],[22.56441,56.39305],[22.69354,56.36284],[22.83048,56.367],[22.96988,56.41213],[23.09531,56.30511],[23.17312,56.36795],[23.31606,56.3827],[23.40486,56.37689],[23.49803,56.34307],[23.75726,56.37282],[24.02657,56.3231],[24.13139,56.24881],[24.32334,56.30226],[24.42746,56.26522],[24.58143,56.29125],[24.57353,56.31525],[24.70022,56.40483],[24.83686,56.41565],[24.89005,56.46666],[25.05762,56.26742],[25.09325,56.1878],[25.23099,56.19147],[25.39751,56.15707],[25.53621,56.16663],[25.68588,56.14725],[25.69246,56.08892],[25.81773,56.05444],[25.85893,56.00188],[25.90047,56.0013],[26.03815,55.95884],[26.18509,55.86813],[26.39561,55.71156],[26.46661,55.70375],[26.58248,55.6754],[26.63231,55.67968],[26.64888,55.70515],[26.71802,55.70645],[26.76872,55.67658],[26.87448,55.7172],[26.97153,55.8102],[27.1559,55.85032],[27.27804,55.78299],[27.3541,55.8089],[27.61683,55.78558],[27.63065,55.89687],[27.97865,56.11849],[28.15217,56.16964],[28.23716,56.27588],[28.16599,56.37806],[28.19057,56.44637],[28.10069,56.524],[28.13526,56.57989],[28.04768,56.59004],[27.86101,56.88204],[27.66511,56.83921],[27.86101,57.29402],[27.52453,57.42826],[27.56832,57.53728],[27.34698,57.52242],[26.90364,57.62823],[26.54675,57.51813],[26.46527,57.56885],[26.29253,57.59244],[26.1866,57.6849],[26.2029,57.7206],[26.08098,57.76619],[26.0543,57.76105],[26.03332,57.7718],[26.02415,57.76865],[26.02069,57.77169],[26.0266,57.77441],[26.027,57.78158],[26.02456,57.78342],[26.0324,57.79037],[26.05949,57.84744],[25.73499,57.90193],[25.29581,58.08288],[25.28237,57.98539],[25.19484,58.0831],[24.3579,57.87471],[24.26221,57.91787],[23.20055,57.56697],[22.80496,57.87798],[19.84909,57.57876],[19.64795,57.06466]]]}},{"type":"Feature","properties":{"id":"MC"},"geometry":{"type":"Polygon","coordinates":[[[7.40903,43.7296],[7.41855,43.72479],[7.50102,43.51859],[7.53358,43.53609],[7.45448,43.7432],[7.4379,43.74963],[7.4389,43.75151],[7.43708,43.75197],[7.43624,43.75014],[7.43013,43.74895],[7.42809,43.74396],[7.42443,43.74087],[7.42299,43.74176],[7.42062,43.73977],[7.41233,43.73439],[7.41298,43.73311],[7.41291,43.73168],[7.41113,43.73156],[7.40903,43.7296]]]}},{"type":"Feature","properties":{"id":"MD"},"geometry":{"type":"Polygon","coordinates":[[[26.62823,48.25804],[26.81161,48.25049],[26.87708,48.19919],[26.94265,48.1969],[26.98042,48.15752],[26.96119,48.13003],[27.04118,48.12522],[27.02985,48.09083],[27.15622,47.98538],[27.1618,47.92391],[27.29069,47.73722],[27.25519,47.71366],[27.32202,47.64009],[27.3979,47.59473],[27.47942,47.48113],[27.55731,47.46637],[27.60263,47.32507],[27.68706,47.28962],[27.73172,47.29248],[27.81892,47.1381],[28.09095,46.97621],[28.12173,46.82283],[28.24808,46.64305],[28.22281,46.50481],[28.25769,46.43334],[28.18902,46.35283],[28.19864,46.31869],[28.10937,46.22852],[28.13684,46.18099],[28.08612,46.01105],[28.13111,45.92819],[28.16568,45.6421],[28.08927,45.6051],[28.18741,45.47358],[28.21139,45.46895],[28.30201,45.54744],[28.41836,45.51715],[28.43072,45.48538],[28.51449,45.49982],[28.49252,45.56716],[28.54196,45.58062],[28.51587,45.6613],[28.47879,45.66994],[28.52823,45.73803],[28.70401,45.78019],[28.69852,45.81753],[28.78503,45.83475],[28.74383,45.96664],[28.98004,46.00385],[29.00613,46.04962],[28.94643,46.09176],[29.06656,46.19716],[28.94953,46.25852],[28.98478,46.31803],[29.004,46.31495],[28.9306,46.45699],[29.01241,46.46177],[29.02409,46.49582],[29.23547,46.55435],[29.24886,46.37912],[29.35357,46.49505],[29.49914,46.45889],[29.5939,46.35472],[29.6763,46.36041],[29.66359,46.4215],[29.74496,46.45605],[29.88329,46.35851],[29.94114,46.40114],[30.09103,46.38694],[30.16794,46.40967],[30.02511,46.45132],[29.88916,46.54302],[29.94409,46.56002],[29.9743,46.75325],[29.94522,46.80055],[29.98814,46.82358],[29.87405,46.88199],[29.75458,46.8604],[29.72986,46.92234],[29.57056,46.94766],[29.62137,47.05069],[29.61038,47.09932],[29.53044,47.07851],[29.49732,47.12878],[29.57696,47.13581],[29.54996,47.24962],[29.59665,47.25521],[29.5733,47.36508],[29.48678,47.36043],[29.47854,47.30366],[29.39889,47.30179],[29.3261,47.44664],[29.18603,47.43387],[29.11743,47.55001],[29.22414,47.60012],[29.22242,47.73607],[29.27255,47.79953],[29.20663,47.80367],[29.27804,47.88893],[29.19839,47.89261],[29.1723,47.99013],[28.9306,47.96255],[28.8414,48.03392],[28.85232,48.12506],[28.69896,48.13106],[28.53921,48.17453],[28.48428,48.0737],[28.42454,48.12047],[28.43701,48.15832],[28.38712,48.17567],[28.34009,48.13147],[28.30609,48.14018],[28.30586,48.1597],[28.34912,48.1787],[28.36996,48.20543],[28.35519,48.24957],[28.32508,48.23384],[28.2856,48.23202],[28.19314,48.20749],[28.17666,48.25963],[28.07504,48.23494],[28.09873,48.3124],[28.04527,48.32661],[27.95883,48.32368],[27.88391,48.36699],[27.87533,48.4037],[27.81902,48.41874],[27.79225,48.44244],[27.74422,48.45926],[27.6658,48.44034],[27.59027,48.46311],[27.5889,48.49224],[27.46942,48.454],[27.44333,48.41209],[27.37741,48.41026],[27.37604,48.44398],[27.32159,48.4434],[27.27855,48.37534],[27.13434,48.37288],[27.08078,48.43214],[27.0231,48.42485],[27.03821,48.37653],[26.93384,48.36558],[26.85556,48.41095],[26.71274,48.40388],[26.82809,48.31629],[26.79239,48.29071],[26.6839,48.35828],[26.62823,48.25804]]]}},{"type":"Feature","properties":{"id":"MG"},"geometry":{"type":"Polygon","coordinates":[[[40.40841,-23.17181],[42.93867,-25.64228],[47.18248,-26.33102],[51.94557,-12.74579],[48.86266,-10.8109],[47.29063,-12.45583],[43.72277,-16.09877],[40.40841,-23.17181]]]}},{"type":"Feature","properties":{"id":"MK"},"geometry":{"type":"Polygon","coordinates":[[[20.45331,41.51436],[20.49039,41.49277],[20.51301,41.442],[20.55976,41.4087],[20.52119,41.34381],[20.49432,41.33679],[20.51068,41.2323],[20.59715,41.13644],[20.58546,41.11179],[20.59832,41.09066],[20.63454,41.0889],[20.65558,41.08009],[20.71634,40.91781],[20.73504,40.9081],[20.81567,40.89662],[20.83671,40.92752],[20.94305,40.92399],[20.97693,40.90103],[20.97887,40.85475],[21.15262,40.85546],[21.21105,40.8855],[21.25779,40.86165],[21.35595,40.87578],[21.41555,40.9173],[21.53007,40.90759],[21.57448,40.86076],[21.69601,40.9429],[21.7556,40.92525],[21.91102,41.04786],[21.90869,41.09191],[22.06527,41.15617],[22.1424,41.12449],[22.17629,41.15969],[22.26744,41.16409],[22.42285,41.11921],[22.5549,41.13065],[22.58295,41.11568],[22.62852,41.14385],[22.65306,41.18168],[22.71266,41.13945],[22.74538,41.16321],[22.76408,41.32225],[22.81199,41.3398],[22.93334,41.34104],[22.96331,41.35782],[22.95513,41.63265],[23.03342,41.71034],[23.01239,41.76527],[22.96682,41.77137],[22.90254,41.87587],[22.86749,42.02275],[22.67701,42.06614],[22.51224,42.15457],[22.50289,42.19527],[22.47251,42.20393],[22.38136,42.30339],[22.34773,42.31725],[22.29275,42.34913],[22.29605,42.37477],[22.16384,42.32103],[22.02908,42.29848],[21.94405,42.34669],[21.91595,42.30392],[21.84654,42.3247],[21.77176,42.2648],[21.70111,42.23789],[21.58992,42.25915],[21.52145,42.24465],[21.50823,42.27156],[21.43882,42.2789],[21.43882,42.23609],[21.38428,42.24465],[21.30496,42.1418],[21.29913,42.13954],[21.31983,42.10993],[21.22728,42.08909],[21.16614,42.19815],[21.11491,42.20794],[20.75464,42.05229],[20.76786,41.91839],[20.68523,41.85318],[20.59524,41.8818],[20.55976,41.87068],[20.57144,41.7897],[20.53405,41.78099],[20.51301,41.72433],[20.52937,41.69292],[20.51769,41.65975],[20.55508,41.58113],[20.52103,41.56473],[20.45809,41.5549],[20.45331,41.51436]]]}},{"type":"Feature","properties":{"id":"ML"},"geometry":{"type":"Polygon","coordinates":[[[-12.23936,14.76324],[-11.93043,13.84505],[-12.06897,13.71049],[-11.83345,13.33333],[-11.63025,13.39174],[-11.39935,12.97808],[-11.37536,12.40788],[-11.50006,12.17826],[-11.24136,12.01286],[-10.99758,12.24634],[-10.80355,12.1053],[-10.71897,11.91552],[-10.30604,12.24634],[-9.714,12.0226],[-9.63938,12.18312],[-9.32097,12.29009],[-9.38067,12.48446],[-9.13689,12.50875],[-8.94784,12.34842],[-8.80854,11.66715],[-8.40058,11.37466],[-8.66923,10.99397],[-8.35083,11.06234],[-8.2667,10.91762],[-8.32614,10.69273],[-8.22711,10.41722],[-8.10207,10.44649],[-7.9578,10.2703],[-7.97971,10.17117],[-7.92107,10.15577],[-7.63048,10.46334],[-7.54462,10.40921],[-7.52261,10.4655],[-7.44555,10.44602],[-7.3707,10.24677],[-7.13331,10.24877],[-7.0603,10.14711],[-7.00966,10.15794],[-6.97444,10.21644],[-7.01186,10.25111],[-6.93921,10.35291],[-6.68164,10.35074],[-6.63541,10.66893],[-6.52974,10.59104],[-6.42847,10.5694],[-6.40646,10.69922],[-6.325,10.68624],[-6.24795,10.74248],[-6.1731,10.46983],[-6.18851,10.24244],[-5.99478,10.19694],[-5.78124,10.43952],[-5.65135,10.46767],[-5.51058,10.43177],[-5.46643,10.56074],[-5.47083,10.75329],[-5.41579,10.84628],[-5.49284,11.07538],[-5.32994,11.13371],[-5.32553,11.21578],[-5.25949,11.24816],[-5.25509,11.36905],[-5.20665,11.43811],[-5.22867,11.60421],[-5.29251,11.61715],[-5.26389,11.75728],[-5.40258,11.8327],[-5.26389,11.84778],[-5.07897,11.97918],[-4.72893,12.01579],[-4.70692,12.06746],[-4.62987,12.06531],[-4.62546,12.13204],[-4.54841,12.1385],[-4.57703,12.19875],[-4.41412,12.31922],[-4.47356,12.71252],[-4.238,12.71467],[-4.21819,12.95722],[-4.34477,13.12927],[-3.96501,13.49778],[-3.90558,13.44375],[-3.96282,13.38164],[-3.7911,13.36665],[-3.54454,13.1781],[-3.4313,13.1588],[-3.43507,13.27272],[-3.23599,13.29035],[-3.28396,13.5422],[-3.26407,13.70699],[-2.88189,13.64921],[-2.90831,13.81174],[-2.84667,14.05532],[-2.66175,14.14713],[-2.47587,14.29671],[-2.10223,14.14878],[-1.9992,14.19011],[-1.97945,14.47709],[-1.68083,14.50023],[-1.32166,14.72774],[-1.05875,14.7921],[-0.72004,15.08655],[-0.24673,15.07805],[0.06588,14.96961],[0.23859,15.00135],[0.72632,14.95898],[0.96711,14.98275],[1.31275,15.27978],[3.01806,15.34571],[3.03134,15.42221],[3.50368,15.35934],[4.19893,16.39923],[4.21787,17.00118],[4.26762,17.00432],[4.26651,19.14224],[3.36082,18.9745],[3.12501,19.1366],[3.24648,19.81703],[1.20992,20.73533],[1.15698,21.12843],[-4.83423,24.99935],[-6.57191,25.0002],[-5.60725,16.49919],[-5.33435,16.33354],[-5.50165,15.50061],[-9.32979,15.50032],[-9.31106,15.69412],[-9.33314,15.7044],[-9.44673,15.60553],[-9.40447,15.4396],[-10.71721,15.4223],[-10.90932,15.11001],[-11.43483,15.62339],[-11.70705,15.51558],[-11.94903,14.76143],[-12.23936,14.76324]]]}},{"type":"Feature","properties":{"id":"MM"},"geometry":{"type":"Polygon","coordinates":[[[92.17752,21.17445],[92.26071,21.05697],[92.37665,20.72172],[92.28464,20.63179],[92.31348,20.57137],[92.4302,20.5688],[92.39837,20.38919],[92.61282,13.95915],[93.69443,13.6468],[94.6395,14.00732],[97.16045,11.79791],[97.63455,9.60854],[98.21525,9.56576],[98.33094,9.91973],[98.47298,9.95782],[98.52291,9.92389],[98.55174,9.92804],[98.7391,10.31488],[98.81944,10.52761],[98.77275,10.62548],[98.78511,10.68351],[98.86819,10.78336],[99.0069,10.85485],[98.99701,10.92962],[99.02337,10.97217],[99.06938,10.94857],[99.32756,11.28545],[99.31573,11.32081],[99.39485,11.3925],[99.47598,11.62434],[99.5672,11.62732],[99.64108,11.78948],[99.64891,11.82699],[99.53424,12.02317],[99.56445,12.14805],[99.47519,12.1353],[99.409,12.60603],[99.29254,12.68921],[99.18905,12.84799],[99.18748,12.9898],[99.10646,13.05804],[99.12225,13.19847],[99.20617,13.20575],[99.16695,13.72621],[98.97356,14.04868],[98.56762,14.37701],[98.24874,14.83013],[98.18821,15.13125],[98.22,15.21327],[98.30446,15.30667],[98.40522,15.25268],[98.41906,15.27103],[98.39351,15.34177],[98.4866,15.39154],[98.56027,15.33471],[98.58598,15.46821],[98.541,15.65406],[98.59853,15.87197],[98.57019,16.04578],[98.69585,16.13353],[98.8376,16.11706],[98.92656,16.36425],[98.84485,16.42354],[98.68074,16.27068],[98.63817,16.47424],[98.57912,16.55983],[98.5695,16.62826],[98.51113,16.64503],[98.51833,16.676],[98.51472,16.68521],[98.51579,16.69433],[98.51043,16.70107],[98.49713,16.69022],[98.50253,16.7139],[98.46994,16.73613],[98.53833,16.81934],[98.49603,16.8446],[98.52624,16.89979],[98.39441,17.06266],[98.34566,17.04822],[98.10439,17.33847],[98.11185,17.36829],[97.91829,17.54504],[97.76407,17.71595],[97.66794,17.88005],[97.73723,17.97912],[97.60841,18.23846],[97.64116,18.29778],[97.56219,18.33885],[97.50383,18.26844],[97.34522,18.54596],[97.36444,18.57138],[97.5258,18.4939],[97.76752,18.58097],[97.73836,18.88478],[97.66487,18.9371],[97.73654,18.9812],[97.73797,19.04261],[97.83479,19.09972],[97.84024,19.22217],[97.78606,19.26769],[97.84186,19.29526],[97.78769,19.39429],[97.88423,19.5041],[97.84715,19.55782],[98.04364,19.65755],[98.03314,19.80941],[98.13829,19.78541],[98.24884,19.67876],[98.51182,19.71303],[98.56065,19.67807],[98.83661,19.80931],[98.98679,19.7419],[99.0735,20.10298],[99.20328,20.12877],[99.416,20.08614],[99.52943,20.14811],[99.5569,20.20676],[99.46077,20.36198],[99.46008,20.39673],[99.68255,20.32077],[99.81096,20.33687],[99.86383,20.44371],[99.88211,20.44488],[99.88451,20.44596],[99.89168,20.44548],[99.89301,20.44311],[99.89692,20.44789],[99.90499,20.4487],[99.91616,20.44986],[99.95721,20.46301],[100.08404,20.36626],[100.1957,20.68247],[100.36375,20.82783],[100.51079,20.82194],[100.60112,20.8347],[100.64628,20.88279],[100.50974,20.88574],[100.55281,21.02796],[100.63578,21.05639],[100.72716,21.31786],[100.80173,21.2934],[101.00234,21.39612],[101.16198,21.52808],[101.15156,21.56129],[101.11744,21.77659],[100.87265,21.67396],[100.72143,21.51898],[100.57861,21.45637],[100.4811,21.46148],[100.42892,21.54325],[100.35201,21.53176],[100.25863,21.47043],[100.18447,21.51898],[100.1625,21.48704],[100.12542,21.50365],[100.10757,21.59945],[100.17486,21.65306],[100.12679,21.70539],[100.04956,21.66843],[99.98654,21.71064],[99.94003,21.82782],[99.99084,21.97053],[99.96612,22.05965],[99.85351,22.04183],[99.47585,22.13345],[99.33166,22.09656],[99.1552,22.15874],[99.19176,22.16983],[99.17318,22.18025],[99.28771,22.4105],[99.37972,22.50188],[99.38247,22.57544],[99.31243,22.73893],[99.45654,22.85726],[99.43537,22.94086],[99.54218,22.90014],[99.52214,23.08218],[99.34127,23.13099],[99.25741,23.09025],[99.04601,23.12215],[99.05975,23.16382],[98.88597,23.18656],[98.92515,23.29535],[98.93958,23.31414],[98.87573,23.33038],[98.92104,23.36946],[98.87683,23.48995],[98.82877,23.47908],[98.80294,23.5345],[98.88396,23.59555],[98.81775,23.694],[98.82933,23.72921],[98.79607,23.77947],[98.68209,23.80492],[98.67797,23.9644],[98.89632,24.10612],[98.87998,24.15624],[98.85319,24.13042],[98.59256,24.08371],[98.54476,24.13119],[98.20666,24.11406],[98.07806,24.07988],[98.06703,24.08028],[98.0607,24.07812],[98.05671,24.07961],[98.05302,24.07408],[98.04709,24.07616],[97.99583,24.04932],[97.98691,24.03897],[97.93951,24.01953],[97.90998,24.02094],[97.88616,24.00463],[97.88414,23.99405],[97.88814,23.98605],[97.89683,23.98389],[97.89676,23.97931],[97.8955,23.97758],[97.88811,23.97446],[97.86545,23.97723],[97.84328,23.97603],[97.79416,23.95663],[97.79456,23.94836],[97.72302,23.89288],[97.64667,23.84574],[97.5247,23.94032],[97.62363,24.00506],[97.72903,24.12606],[97.75305,24.16902],[97.72799,24.18883],[97.72998,24.2302],[97.76799,24.26365],[97.71941,24.29652],[97.66723,24.30027],[97.65624,24.33781],[97.7098,24.35658],[97.66998,24.45288],[97.60029,24.4401],[97.52757,24.43748],[97.56286,24.54535],[97.56525,24.72838],[97.54675,24.74202],[97.5542,24.74943],[97.56383,24.75535],[97.56648,24.76475],[97.64354,24.79171],[97.70181,24.84557],[97.73127,24.83015],[97.76481,24.8289],[97.79949,24.85655],[97.72903,24.91332],[97.72216,25.08508],[97.77023,25.11492],[97.83614,25.2715],[97.92541,25.20815],[98.14925,25.41547],[98.12591,25.50722],[98.18084,25.56298],[98.16848,25.62739],[98.25774,25.6051],[98.31268,25.55307],[98.40606,25.61129],[98.54064,25.85129],[98.63128,25.79937],[98.70818,25.86241],[98.60763,26.01512],[98.57085,26.11547],[98.63128,26.15492],[98.66884,26.09165],[98.7329,26.17218],[98.67797,26.24487],[98.72741,26.36183],[98.77547,26.61994],[98.7333,26.85615],[98.69582,27.56499],[98.43353,27.67086],[98.42529,27.55404],[98.32641,27.51385],[98.13964,27.9478],[98.15337,28.12114],[97.90069,28.3776],[97.79632,28.33168],[97.70705,28.5056],[97.56835,28.55628],[97.50518,28.49716],[97.47085,28.2688],[97.41729,28.29783],[97.34547,28.21385],[97.31292,28.06784],[97.35412,28.06663],[97.38845,28.01329],[97.35824,27.87256],[97.29919,27.92233],[96.90112,27.62149],[96.91431,27.45752],[97.17422,27.14052],[97.14675,27.09041],[96.89132,27.17474],[96.85287,27.2065],[96.88445,27.25046],[96.73888,27.36638],[96.55761,27.29928],[96.40779,27.29818],[96.15591,27.24572],[96.04949,27.19428],[95.93002,27.04149],[95.81603,27.01335],[95.437,26.7083],[95.30339,26.65372],[95.23513,26.68499],[95.05798,26.45408],[95.12801,26.38397],[95.11428,26.1019],[95.18556,26.07338],[94.80117,25.49359],[94.68032,25.47003],[94.57458,25.20318],[94.74212,25.13606],[94.73937,25.00545],[94.60204,24.70889],[94.5526,24.70764],[94.50729,24.59281],[94.45279,24.56656],[94.32362,24.27692],[94.30215,24.23752],[94.14081,23.83333],[93.92089,23.95812],[93.80279,23.92549],[93.75952,24.0003],[93.62871,24.00922],[93.50616,23.94432],[93.46633,23.97067],[93.41415,24.07854],[93.34735,24.10151],[93.32351,24.04468],[93.36059,23.93176],[93.3908,23.92925],[93.3908,23.7622],[93.43475,23.68299],[93.38805,23.4728],[93.39981,23.38828],[93.38781,23.36139],[93.36862,23.35426],[93.38478,23.13698],[93.2878,23.00464],[93.12988,23.05772],[93.134,22.92498],[93.09417,22.69459],[93.134,22.59573],[93.11477,22.54374],[93.13537,22.45873],[93.18206,22.43716],[93.19991,22.25425],[93.14224,22.24535],[93.15734,22.18687],[93.04885,22.20595],[92.99255,22.05965],[92.99804,21.98964],[92.93899,22.02656],[92.89504,21.95143],[92.86208,22.05456],[92.70416,22.16017],[92.67532,22.03547],[92.60949,21.97638],[92.62187,21.87037],[92.59775,21.6092],[92.68152,21.28454],[92.60187,21.24615],[92.55105,21.3856],[92.43158,21.37025],[92.37939,21.47764],[92.20087,21.337],[92.17752,21.17445]]]}},{"type":"Feature","properties":{"id":"ME"},"geometry":{"type":"Polygon","coordinates":[[[18.43588,42.48556],[18.52152,42.42302],[18.54128,42.39171],[18.45131,42.21682],[19.26406,41.74971],[19.37597,41.84849],[19.37451,41.8842],[19.33812,41.90669],[19.34601,41.95675],[19.37691,41.96977],[19.36867,42.02564],[19.37548,42.06835],[19.40687,42.10024],[19.28623,42.17745],[19.42,42.33019],[19.42352,42.36546],[19.4836,42.40831],[19.65972,42.62774],[19.73244,42.66299],[19.77375,42.58517],[19.74731,42.57422],[19.76549,42.50237],[19.82333,42.46581],[19.9324,42.51699],[20.00842,42.5109],[20.01834,42.54622],[20.07761,42.55582],[20.0969,42.65559],[20.02915,42.71147],[20.02088,42.74789],[20.04898,42.77701],[20.2539,42.76245],[20.27869,42.81945],[20.35692,42.8335],[20.34528,42.90676],[20.16415,42.97177],[20.14896,42.99058],[20.12325,42.96237],[20.05431,42.99571],[20.04729,43.02732],[19.98887,43.0538],[19.96549,43.11098],[19.92576,43.08539],[19.79255,43.11951],[19.76918,43.16044],[19.64063,43.19027],[19.62661,43.2286],[19.54598,43.25158],[19.52962,43.31623],[19.48171,43.32644],[19.44315,43.38846],[19.22229,43.47926],[19.22807,43.5264],[19.15685,43.53943],[19.13933,43.5282],[19.04934,43.50384],[19.01078,43.55806],[18.91379,43.50299],[18.95469,43.49367],[18.96053,43.45042],[19.01078,43.43854],[19.04071,43.397],[19.08673,43.31453],[19.08206,43.29668],[19.04233,43.30008],[19.00844,43.24988],[18.95001,43.29327],[18.95819,43.32899],[18.90911,43.36383],[18.83912,43.34795],[18.84794,43.33735],[18.85342,43.32426],[18.76538,43.29838],[18.6976,43.25243],[18.71747,43.2286],[18.66605,43.2056],[18.64735,43.14766],[18.66254,43.03928],[18.52232,43.01451],[18.49076,42.95553],[18.49661,42.89306],[18.4935,42.86433],[18.47633,42.85829],[18.45921,42.81682],[18.47324,42.74992],[18.56789,42.72074],[18.55221,42.69045],[18.54603,42.69171],[18.54841,42.68328],[18.57373,42.64429],[18.52232,42.62279],[18.55504,42.58409],[18.53751,42.57376],[18.49778,42.58409],[18.43735,42.55921],[18.44307,42.51077],[18.43588,42.48556]]]}},{"type":"Feature","properties":{"id":"MN"},"geometry":{"type":"Polygon","coordinates":[[[87.73822,48.89582],[88.0788,48.71436],[87.96361,48.58478],[88.58939,48.34531],[88.58316,48.21893],[88.8011,48.11302],[88.93186,48.10263],[89.0711,47.98528],[89.55453,48.0423],[89.76624,47.82745],[90.06512,47.88177],[90.10871,47.7375],[90.33598,47.68303],[90.48854,47.41826],[90.48542,47.30438],[90.76108,46.99399],[90.84035,46.99525],[91.03649,46.72916],[91.0147,46.58171],[91.07696,46.57315],[90.89639,46.30711],[90.99672,46.14207],[91.03026,46.04194],[90.70907,45.73437],[90.65114,45.49314],[90.89169,45.19667],[91.64048,45.07408],[93.51161,44.95964],[94.10003,44.71016],[94.71959,44.35284],[95.01191,44.25274],[95.39772,44.2805],[95.32891,44.02407],[95.52594,43.99353],[95.89543,43.2528],[96.35658,42.90363],[96.37926,42.72055],[97.1777,42.7964],[99.50671,42.56535],[100.33297,42.68231],[100.84979,42.67087],[101.28833,42.58524],[101.80515,42.50074],[102.07645,42.22519],[102.42826,42.15137],[102.72403,42.14675],[103.3685,41.89696],[103.92804,41.78246],[104.52258,41.8706],[104.51667,41.66113],[104.91272,41.64619],[105.01119,41.58382],[105.24708,41.7442],[106.76517,42.28741],[107.24774,42.36107],[107.29755,42.41395],[107.49681,42.46221],[107.57258,42.40898],[108.23156,42.45532],[108.84489,42.40246],[109.00679,42.45302],[109.452,42.44842],[109.89402,42.63111],[110.08401,42.6411],[110.4327,42.78293],[111.0149,43.3289],[111.59087,43.51207],[111.79758,43.6637],[111.93776,43.68709],[111.96289,43.81596],[111.40498,44.3461],[111.76275,44.98032],[111.98695,45.09074],[112.4164,45.06858],[112.74662,44.86297],[113.63821,44.74326],[113.909,44.91444],[114.08071,44.92847],[114.5166,45.27189],[114.54801,45.38337],[114.74612,45.43585],[114.94546,45.37377],[115.35757,45.39106],[115.69688,45.45761],[115.91898,45.6227],[116.16989,45.68603],[116.27366,45.78637],[116.24012,45.8778],[116.26678,45.96479],[116.58612,46.30211],[116.75551,46.33083],[116.83166,46.38637],[117.07252,46.35818],[117.36609,46.36335],[117.41782,46.57862],[117.60748,46.59771],[117.69554,46.50991],[118.30534,46.73519],[118.78747,46.68689],[118.8337,46.77742],[118.89974,46.77139],[118.92616,46.72765],[119.00541,46.74273],[119.10448,46.65516],[119.24978,46.64761],[119.30261,46.6083],[119.37306,46.61132],[119.42827,46.63783],[119.65265,46.62342],[119.68127,46.59015],[119.77373,46.62947],[119.80455,46.67631],[119.89261,46.66423],[119.91242,46.90091],[119.85518,46.92196],[119.71209,47.19192],[119.62403,47.24575],[119.56019,47.24874],[119.54918,47.29505],[119.31964,47.42617],[119.35892,47.48104],[119.13995,47.53997],[119.12343,47.66458],[118.7564,47.76947],[118.55766,47.99277],[118.29654,48.00246],[118.22677,48.03853],[118.11009,48.04],[118.03676,48.00982],[117.80196,48.01661],[117.50181,47.77216],[117.37875,47.63627],[117.08918,47.82242],[116.87527,47.88836],[116.67405,47.89039],[116.4465,47.83662],[116.2527,47.87766],[116.08431,47.80693],[115.94296,47.67741],[115.57128,47.91988],[115.52082,48.15367],[115.811,48.25699],[115.78876,48.51781],[116.06565,48.81716],[116.03781,48.87014],[116.71193,49.83813],[116.62502,49.92919],[116.22402,50.04477],[115.73602,49.87688],[115.26068,49.97367],[114.9703,50.19254],[114.325,50.28098],[113.20216,49.83356],[113.02647,49.60772],[110.64493,49.1816],[110.39891,49.25083],[110.24373,49.16676],[109.51325,49.22859],[109.18017,49.34709],[108.53969,49.32325],[108.27937,49.53167],[107.95387,49.66659],[107.96116,49.93191],[107.36407,49.97612],[107.1174,50.04239],[107.00007,50.1977],[106.80326,50.30177],[106.58373,50.34044],[106.51122,50.34408],[106.49628,50.32436],[106.47156,50.31909],[106.07865,50.33474],[106.05562,50.40582],[105.32528,50.4648],[103.70343,50.13952],[102.71178,50.38873],[102.32194,50.67982],[102.14032,51.35566],[101.5044,51.50467],[101.39085,51.45753],[100.61116,51.73028],[99.89203,51.74903],[99.75578,51.90108],[99.27888,51.96876],[98.87768,52.14563],[98.74142,51.8637],[98.33222,51.71832],[98.22053,51.46579],[98.05257,51.46696],[97.83305,51.00248],[98.01472,50.86652],[97.9693,50.78044],[98.06393,50.61262],[98.31373,50.4996],[98.29481,50.33561],[97.85197,49.91339],[97.76871,49.99861],[97.56432,49.92801],[97.56811,49.84265],[97.24639,49.74737],[96.97388,49.88413],[95.80056,50.04239],[95.74757,49.97915],[95.02465,49.96941],[94.97166,50.04725],[94.6121,50.04239],[94.49477,50.17832],[94.39258,50.22193],[94.30823,50.57498],[92.99595,50.63183],[93.01109,50.79001],[92.44714,50.78762],[92.07173,50.69585],[91.86048,50.73734],[89.59711,49.90851],[89.70687,49.72535],[88.82499,49.44808],[88.42449,49.48821],[88.17223,49.46934],[88.15543,49.30314],[87.98977,49.18147],[87.81333,49.17354],[87.88171,48.95853],[87.73822,48.89582]]]}},{"type":"Feature","properties":{"id":"MZ"},"geometry":{"type":"Polygon","coordinates":[[[30.22098,-14.99447],[30.41902,-15.62269],[30.42568,-15.9962],[30.91597,-15.99924],[30.97761,-16.05848],[31.13171,-15.98019],[31.30563,-16.01193],[31.42451,-16.15154],[31.67988,-16.19595],[31.90223,-16.34388],[31.91324,-16.41569],[32.02772,-16.43892],[32.28529,-16.43892],[32.42838,-16.4727],[32.71017,-16.59932],[32.69917,-16.66893],[32.78943,-16.70267],[32.97655,-16.70689],[32.91051,-16.89446],[32.84113,-16.92259],[32.96554,-17.11971],[33.00517,-17.30477],[33.0426,-17.3468],[32.96554,-17.48964],[32.98536,-17.55891],[33.0492,-17.60298],[32.94133,-17.99705],[33.03159,-18.35054],[33.02278,-18.4696],[32.88629,-18.51344],[32.88629,-18.58023],[32.95013,-18.69079],[32.9017,-18.7992],[32.82465,-18.77419],[32.70137,-18.84712],[32.73439,-18.92628],[32.69917,-18.94293],[32.72118,-19.02204],[32.84006,-19.0262],[32.87088,-19.09279],[32.85107,-19.29238],[32.77966,-19.36098],[32.78282,-19.47513],[32.84446,-19.48343],[32.84666,-19.68462],[32.95013,-19.67219],[33.06461,-19.77787],[33.01178,-20.02007],[32.93032,-20.03868],[32.85987,-20.16686],[32.85987,-20.27841],[32.66174,-20.56106],[32.55167,-20.56312],[32.48122,-20.63319],[32.51644,-20.91929],[32.37115,-21.133],[32.48236,-21.32873],[32.41234,-21.31246],[31.38336,-22.36919],[31.30611,-22.422],[31.55779,-23.176],[31.56539,-23.47268],[31.67942,-23.60858],[31.70223,-23.72695],[31.77445,-23.90082],[31.87707,-23.95293],[31.90368,-24.18892],[31.9835,-24.29983],[32.03196,-25.10785],[32.01676,-25.38117],[31.97875,-25.46356],[32.00631,-25.65044],[31.92649,-25.84216],[31.974,-25.95387],[32.00916,-25.999],[32.08599,-26.00978],[32.10435,-26.15656],[32.07352,-26.40185],[32.13409,-26.5317],[32.13315,-26.84345],[32.19409,-26.84032],[32.22302,-26.84136],[32.29584,-26.852],[32.35222,-26.86027],[32.89816,-26.8579],[33.10054,-26.92273],[39.10324,-21.48967],[41.06663,-17.08802],[42.99868,-12.65261],[42.93552,-11.11413],[40.74206,-10.25691],[40.44265,-10.4618],[40.00295,-10.80255],[39.58249,-10.96043],[39.24395,-11.17433],[38.88996,-11.16978],[38.47258,-11.4199],[38.21598,-11.27289],[37.93618,-11.26228],[37.8388,-11.3123],[37.76614,-11.53352],[37.3936,-11.68949],[36.80309,-11.56836],[36.62068,-11.72884],[36.19094,-11.70008],[36.19094,-11.57593],[35.82767,-11.41081],[35.63599,-11.55927],[34.96296,-11.57354],[34.64241,-11.57499],[34.57917,-11.87849],[34.82903,-12.04837],[34.70739,-12.15652],[34.46088,-12.0174],[34.37831,-12.17408],[34.60253,-13.48487],[34.86229,-13.48958],[35.47989,-14.15594],[35.5299,-14.27714],[35.86945,-14.67481],[35.87212,-14.89478],[35.91812,-14.89514],[35.78799,-15.17428],[35.85303,-15.41913],[35.80487,-16.03907],[35.70107,-16.10147],[35.52365,-16.15414],[35.43355,-16.11371],[35.30157,-16.2211],[35.25828,-16.4792],[35.14235,-16.56812],[35.27219,-16.69402],[35.30929,-16.82871],[35.27065,-16.93817],[35.3062,-17.1244],[35.0923,-17.13235],[35.04805,-17.00027],[35.17017,-16.93521],[35.13771,-16.81687],[35.04805,-16.83167],[34.40344,-16.20923],[34.43126,-16.04737],[34.25195,-15.90321],[34.44981,-15.60864],[34.43126,-15.44778],[34.57503,-15.30619],[34.61522,-14.99583],[34.567,-14.77345],[34.54503,-14.74672],[34.52057,-14.68263],[34.53516,-14.67782],[34.55112,-14.64494],[34.53962,-14.59776],[34.52366,-14.5667],[34.49636,-14.55091],[34.48932,-14.53646],[34.47628,-14.53363],[34.45053,-14.49873],[34.44641,-14.47746],[34.4192,-14.43191],[34.39277,-14.39467],[34.35843,-14.38652],[34.34453,-14.3985],[34.22355,-14.43607],[34.18733,-14.43823],[34.08588,-14.48893],[33.92898,-14.47929],[33.88503,-14.51652],[33.7247,-14.4989],[33.66677,-14.61306],[33.24249,-14.00019],[30.22098,-14.99447]]]}},{"type":"Feature","properties":{"id":"MR"},"geometry":{"type":"Polygon","coordinates":[[[-17.15288,16.07139],[-16.50854,16.09032],[-16.48967,16.0496],[-16.44814,16.09753],[-16.4429,16.20605],[-16.27016,16.51565],[-15.6509,16.50315],[-15.00557,16.64997],[-14.32144,16.61495],[-13.80075,16.13961],[-13.43135,16.09022],[-13.11029,15.52116],[-12.23936,14.76324],[-11.94903,14.76143],[-11.70705,15.51558],[-11.43483,15.62339],[-10.90932,15.11001],[-10.71721,15.4223],[-9.40447,15.4396],[-9.44673,15.60553],[-9.33314,15.7044],[-9.31106,15.69412],[-9.32979,15.50032],[-5.50165,15.50061],[-5.33435,16.33354],[-5.60725,16.49919],[-6.57191,25.0002],[-4.83423,24.99935],[-8.66674,27.31569],[-8.66721,25.99918],[-12.0002,25.9986],[-12.00251,23.4538],[-12.14969,23.41935],[-12.36213,23.3187],[-12.5741,23.28975],[-13.00412,23.02297],[-13.10753,22.89493],[-13.15313,22.75649],[-13.08438,22.53866],[-13.01525,21.33343],[-16.95474,21.33997],[-16.99806,21.12142],[-17.0357,21.05368],[-17.0396,20.9961],[-17.06781,20.92697],[-17.0695,20.85742],[-17.0471,20.76408],[-17.15288,16.07139]]]}},{"type":"Feature","properties":{"id":"MW"},"geometry":{"type":"Polygon","coordinates":[[[32.66468,-13.60019],[32.68654,-13.64268],[32.7828,-13.64805],[32.84528,-13.71576],[32.76962,-13.77224],[32.79015,-13.80755],[32.88985,-13.82956],[32.99042,-13.95689],[33.02977,-14.05022],[33.07568,-13.98447],[33.16749,-13.93992],[33.24249,-14.00019],[33.66677,-14.61306],[33.7247,-14.4989],[33.88503,-14.51652],[33.92898,-14.47929],[34.08588,-14.48893],[34.18733,-14.43823],[34.22355,-14.43607],[34.34453,-14.3985],[34.35843,-14.38652],[34.39277,-14.39467],[34.4192,-14.43191],[34.44641,-14.47746],[34.45053,-14.49873],[34.47628,-14.53363],[34.48932,-14.53646],[34.49636,-14.55091],[34.52366,-14.5667],[34.53962,-14.59776],[34.55112,-14.64494],[34.53516,-14.67782],[34.52057,-14.68263],[34.54503,-14.74672],[34.567,-14.77345],[34.61522,-14.99583],[34.57503,-15.30619],[34.43126,-15.44778],[34.44981,-15.60864],[34.25195,-15.90321],[34.43126,-16.04737],[34.40344,-16.20923],[35.04805,-16.83167],[35.13771,-16.81687],[35.17017,-16.93521],[35.04805,-17.00027],[35.0923,-17.13235],[35.3062,-17.1244],[35.27065,-16.93817],[35.30929,-16.82871],[35.27219,-16.69402],[35.14235,-16.56812],[35.25828,-16.4792],[35.30157,-16.2211],[35.43355,-16.11371],[35.52365,-16.15414],[35.70107,-16.10147],[35.80487,-16.03907],[35.85303,-15.41913],[35.78799,-15.17428],[35.91812,-14.89514],[35.87212,-14.89478],[35.86945,-14.67481],[35.5299,-14.27714],[35.47989,-14.15594],[34.86229,-13.48958],[34.60253,-13.48487],[34.37831,-12.17408],[34.46088,-12.0174],[34.70739,-12.15652],[34.82903,-12.04837],[34.57917,-11.87849],[34.64241,-11.57499],[34.96296,-11.57354],[34.91153,-11.39799],[34.79375,-11.32245],[34.63305,-11.11731],[34.61161,-11.01611],[34.67047,-10.93796],[34.65946,-10.6828],[34.57581,-10.56271],[34.51911,-10.12279],[34.54499,-10.0678],[34.03865,-9.49398],[33.95829,-9.54066],[33.9638,-9.62206],[33.93298,-9.71647],[33.76677,-9.58516],[33.48052,-9.62442],[33.31581,-9.48554],[33.14925,-9.49322],[32.99397,-9.36712],[32.95389,-9.40138],[33.00476,-9.5133],[33.00256,-9.63053],[33.05485,-9.61316],[33.10163,-9.66525],[33.12144,-9.58929],[33.2095,-9.61099],[33.31517,-9.82364],[33.36581,-9.81063],[33.37902,-9.9104],[33.31297,-10.05133],[33.53863,-10.20148],[33.54797,-10.36077],[33.70675,-10.56896],[33.47636,-10.78465],[33.28022,-10.84428],[33.25998,-10.88862],[33.39697,-11.15296],[33.29267,-11.3789],[33.29267,-11.43536],[33.23663,-11.40637],[33.24252,-11.59302],[33.32692,-11.59248],[33.33937,-11.91252],[33.25998,-12.14242],[33.3705,-12.34931],[33.47636,-12.32498],[33.54485,-12.35996],[33.37517,-12.54085],[33.28177,-12.54692],[33.18837,-12.61377],[33.05917,-12.59554],[32.94397,-12.76868],[32.96733,-12.88251],[33.02181,-12.88707],[32.98289,-13.12671],[33.0078,-13.19492],[32.86113,-13.47292],[32.84176,-13.52794],[32.73683,-13.57682],[32.68436,-13.55769],[32.66468,-13.60019]]]}},{"type":"Feature","properties":{"id":"NA"},"geometry":{"type":"Polygon","coordinates":[[[10.5065,-17.25284],[15.70388,-29.23989],[16.45332,-28.63117],[16.46592,-28.57126],[16.59922,-28.53246],[16.90446,-28.057],[17.15405,-28.08573],[17.4579,-28.68718],[18.99885,-28.89165],[19.99882,-28.42622],[19.99817,-24.76768],[19.99912,-21.99991],[20.99751,-22.00026],[20.99904,-18.31743],[21.45556,-18.31795],[23.0996,-18.00075],[23.29618,-17.99855],[23.61088,-18.4881],[24.19416,-18.01919],[24.40577,-17.95726],[24.57485,-18.07151],[24.6303,-17.9863],[24.71887,-17.9218],[24.73364,-17.89338],[24.95586,-17.79674],[25.05895,-17.84452],[25.16882,-17.78253],[25.26433,-17.79571],[25.00198,-17.58221],[24.70864,-17.49501],[24.5621,-17.52963],[24.38712,-17.46818],[24.32811,-17.49082],[24.23619,-17.47489],[23.47474,-17.62877],[21.42741,-18.02787],[21.14283,-17.94318],[18.84226,-17.80375],[18.39229,-17.38927],[14.28743,-17.38814],[13.95896,-17.43141],[13.36212,-16.98048],[12.97145,-16.98567],[12.52111,-17.24495],[12.07076,-17.15165],[11.75063,-17.25013],[10.5065,-17.25284]]]}},{"type":"Feature","properties":{"id":"NE"},"geometry":{"type":"Polygon","coordinates":[[[0.16936,14.51654],[0.38051,14.05575],[0.61924,13.68491],[0.77377,13.6866],[0.77637,13.64442],[0.99514,13.5668],[1.02813,13.46635],[1.20088,13.38951],[1.24429,13.39373],[1.28509,13.35488],[1.24516,13.33968],[1.21217,13.37853],[1.18873,13.31771],[0.99253,13.37515],[0.99167,13.10727],[2.26349,12.41915],[2.05785,12.35539],[2.39723,11.89473],[2.45824,11.98672],[2.39657,12.10952],[2.37783,12.24804],[2.6593,12.30631],[2.83978,12.40585],[3.25352,12.01467],[3.31613,11.88495],[3.48187,11.86092],[3.59375,11.70269],[3.61075,11.69181],[3.67988,11.75429],[3.67122,11.80865],[3.63063,11.83042],[3.61955,11.91847],[3.67775,11.97599],[3.63136,12.11826],[3.66364,12.25884],[3.65111,12.52223],[3.94339,12.74979],[4.10006,12.98862],[4.14367,13.17189],[4.14186,13.47586],[4.23456,13.47725],[4.4668,13.68286],[4.87425,13.78],[4.9368,13.7345],[5.07396,13.75052],[5.21026,13.73627],[5.27797,13.75474],[5.35437,13.83567],[5.52957,13.8845],[6.15771,13.64564],[6.27411,13.67835],[6.43053,13.6006],[6.69617,13.34057],[6.94445,12.99825],[7.0521,13.00076],[7.12676,13.02445],[7.22399,13.1293],[7.39241,13.09717],[7.81085,13.34902],[8.07997,13.30847],[8.25185,13.20369],[8.41853,13.06166],[8.49493,13.07519],[8.60431,13.01768],[8.64251,12.93985],[8.97413,12.83661],[9.65995,12.80614],[10.00373,13.18171],[10.19993,13.27129],[10.46731,13.28819],[10.66004,13.36422],[11.4535,13.37773],[11.88236,13.2527],[12.04209,13.14452],[12.16189,13.10056],[12.19315,13.12423],[12.47095,13.06673],[12.58033,13.27805],[12.6793,13.29157],[12.87376,13.48919],[13.05085,13.53984],[13.19844,13.52802],[13.33213,13.71195],[13.6302,13.71094],[13.47559,14.40881],[13.48259,14.46704],[13.68573,14.55276],[13.67878,14.64013],[13.809,14.72915],[13.78991,14.87519],[13.86301,15.04043],[14.37425,15.72591],[15.50373,16.89649],[15.6032,18.77402],[15.75098,19.93002],[15.99632,20.35364],[15.6721,20.70069],[15.59841,20.74039],[15.56004,20.79488],[15.55382,20.86507],[15.57248,20.92138],[15.62515,20.95395],[15.28332,21.44557],[15.20213,21.49365],[15.19692,21.99339],[14.99751,23.00539],[14.22918,22.61719],[13.5631,23.16574],[11.96886,23.51735],[7.48273,20.87258],[7.38361,20.79165],[5.8153,19.45101],[4.26651,19.14224],[4.26762,17.00432],[4.21787,17.00118],[4.19893,16.39923],[3.50368,15.35934],[3.03134,15.42221],[3.01806,15.34571],[1.31275,15.27978],[0.96711,14.98275],[0.72632,14.95898],[0.23859,15.00135],[0.16936,14.51654]]]}},{"type":"Feature","properties":{"id":"NG"},"geometry":{"type":"Polygon","coordinates":[[[2.67523,7.87825],[2.73095,7.7755],[2.73405,7.5423],[2.78668,7.5116],[2.79442,7.43486],[2.74489,7.42565],[2.76965,7.13543],[2.71702,6.95722],[2.74024,6.92802],[2.73405,6.78508],[2.78823,6.76356],[2.78204,6.70514],[2.7325,6.64057],[2.74334,6.57291],[2.70464,6.50831],[2.70566,6.38038],[2.74181,6.13349],[5.87055,3.78489],[8.34397,4.30689],[8.60302,4.87353],[8.78027,5.1243],[8.92029,5.58403],[8.83687,5.68483],[8.88156,5.78857],[8.84209,5.82562],[9.51757,6.43874],[9.70674,6.51717],[9.77824,6.79088],[9.86314,6.77756],[10.15135,7.03781],[10.21466,6.88996],[10.53639,6.93432],[10.57214,7.16345],[10.59746,7.14719],[10.60789,7.06885],[10.83727,6.9358],[10.8179,6.83377],[10.94302,6.69325],[11.09644,6.68437],[11.09495,6.51717],[11.42041,6.53789],[11.42264,6.5882],[11.51499,6.60892],[11.57755,6.74059],[11.55818,6.86186],[11.63117,6.9905],[11.87396,7.09398],[11.84864,7.26098],[11.93205,7.47812],[12.01844,7.52981],[11.99908,7.67302],[12.20909,7.97553],[12.19271,8.10826],[12.24782,8.17904],[12.26123,8.43696],[12.4489,8.52536],[12.44146,8.6152],[12.68722,8.65938],[12.71701,8.7595],[12.79,8.75361],[12.81085,8.91992],[12.90022,9.11411],[12.91958,9.33905],[12.85628,9.36698],[13.02385,9.49334],[13.22642,9.57266],[13.25472,9.76795],[13.29941,9.8296],[13.25025,9.86042],[13.24132,9.91031],[13.27409,9.93232],[13.286,9.9822],[13.25323,10.00127],[13.25025,10.03647],[13.34111,10.12299],[13.43644,10.13326],[13.5705,10.53183],[13.54964,10.61236],[13.73434,10.9255],[13.70753,10.94451],[13.7403,11.00593],[13.78945,11.00154],[13.97489,11.30258],[14.17821,11.23831],[14.6124,11.51283],[14.64591,11.66166],[14.55207,11.72001],[14.61612,11.7798],[14.6474,12.17466],[14.4843,12.35223],[14.22215,12.36533],[14.17523,12.41916],[14.20204,12.53405],[14.08251,13.0797],[13.6302,13.71094],[13.33213,13.71195],[13.19844,13.52802],[13.05085,13.53984],[12.87376,13.48919],[12.6793,13.29157],[12.58033,13.27805],[12.47095,13.06673],[12.19315,13.12423],[12.16189,13.10056],[12.04209,13.14452],[11.88236,13.2527],[11.4535,13.37773],[10.66004,13.36422],[10.46731,13.28819],[10.19993,13.27129],[10.00373,13.18171],[9.65995,12.80614],[8.97413,12.83661],[8.64251,12.93985],[8.60431,13.01768],[8.49493,13.07519],[8.41853,13.06166],[8.25185,13.20369],[8.07997,13.30847],[7.81085,13.34902],[7.39241,13.09717],[7.22399,13.1293],[7.12676,13.02445],[7.0521,13.00076],[6.94445,12.99825],[6.69617,13.34057],[6.43053,13.6006],[6.27411,13.67835],[6.15771,13.64564],[5.52957,13.8845],[5.35437,13.83567],[5.27797,13.75474],[5.21026,13.73627],[5.07396,13.75052],[4.9368,13.7345],[4.87425,13.78],[4.4668,13.68286],[4.23456,13.47725],[4.14186,13.47586],[4.14367,13.17189],[4.10006,12.98862],[3.94339,12.74979],[3.65111,12.52223],[3.66364,12.25884],[3.63136,12.11826],[3.67775,11.97599],[3.61955,11.91847],[3.63063,11.83042],[3.67122,11.80865],[3.67988,11.75429],[3.61075,11.69181],[3.59375,11.70269],[3.49175,11.29765],[3.71505,11.13015],[3.84243,10.59316],[3.78292,10.40538],[3.6844,10.46351],[3.57275,10.27185],[3.66908,10.18136],[3.54429,9.87739],[3.35383,9.83641],[3.32099,9.78032],[3.34726,9.70696],[3.25093,9.61632],[3.13928,9.47167],[3.14147,9.28375],[3.08017,9.10006],[2.77907,9.06924],[2.67523,7.87825]]]}},{"type":"Feature","properties":{"id":"NI"},"geometry":{"type":"Polygon","coordinates":[[[-88.11443,12.63306],[-86.14524,11.09059],[-85.71223,11.06868],[-85.60529,11.22607],[-84.92439,10.9497],[-84.68197,11.07568],[-83.90838,10.71161],[-83.66597,10.79916],[-83.68276,11.01562],[-82.56142,11.91792],[-82.06974,14.49418],[-83.04763,15.03256],[-83.13724,15.00002],[-83.49268,15.01158],[-83.62101,14.89448],[-83.89551,14.76697],[-84.10584,14.76353],[-84.48373,14.63249],[-84.70119,14.68078],[-84.82596,14.82212],[-84.90082,14.80489],[-85.1575,14.53934],[-85.18602,14.24929],[-85.32149,14.2562],[-85.45762,14.11304],[-85.73964,13.9698],[-85.75477,13.8499],[-86.03458,13.99181],[-86.00685,14.08474],[-86.14801,14.04317],[-86.35219,13.77157],[-86.76812,13.79605],[-86.71267,13.30348],[-86.87066,13.30641],[-86.93383,13.18677],[-86.93197,13.05313],[-87.03785,12.98682],[-87.06306,13.00892],[-87.37107,12.98646],[-87.55124,13.12523],[-87.7346,13.13228],[-88.11443,12.63306]]]}},{"type":"Feature","properties":{"id":"IN-OR"},"geometry":{"type":"Polygon","coordinates":[[[81.39221,17.81014],[81.77398,17.88596],[82.04383,18.06622],[82.25532,17.98199],[82.35351,18.14193],[82.42835,18.49198],[82.63366,18.23522],[82.82592,18.43727],[82.96737,18.33692],[83.08822,18.54081],[83.11019,18.77176],[83.37936,18.83481],[83.33816,19.01084],[83.65058,19.12311],[83.88748,18.80296],[84.03991,18.79581],[84.08128,18.74478],[84.2284,18.78883],[84.31594,18.78411],[84.50408,19.04394],[84.59369,19.02317],[84.65961,19.06925],[84.59335,19.11662],[84.70287,19.15068],[84.76158,19.07055],[85.11932,18.86211],[87.74779,20.78694],[87.43709,21.76045],[87.27264,21.81114],[87.22457,21.96024],[87.05635,21.85066],[86.94992,22.08627],[86.71371,22.1448],[86.7247,22.21569],[86.03324,22.56012],[85.96115,22.48242],[86.01779,22.30434],[85.90587,21.95801],[85.38986,22.15783],[85.22952,22.00003],[85.01426,22.10886],[85.10662,22.318],[85.05958,22.48083],[84.4876,22.40785],[84.27955,22.32467],[83.99425,22.53602],[84.04129,22.46005],[84.00421,22.37261],[83.65436,22.22967],[83.54347,22.05159],[83.58844,21.84078],[83.48167,21.81242],[83.46828,21.72569],[83.3385,21.50226],[83.40236,21.33958],[83.27499,21.3722],[83.1792,21.11044],[82.67555,21.16008],[82.4517,20.82608],[82.34252,20.85688],[82.39437,20.05302],[82.70439,20.00141],[82.72258,19.85036],[82.58285,19.76444],[82.58628,19.86263],[82.45513,19.91461],[82.34939,19.83776],[81.93809,20.09527],[81.82617,19.92558],[82.06375,19.78221],[82.2512,18.91473],[82.17945,18.90401],[82.16468,18.79094],[81.90101,18.64266],[81.84402,18.5714],[81.79664,18.477],[81.69158,18.42196],[81.706,18.39916],[81.61468,18.31052],[81.54275,18.26864],[81.50722,18.19217],[81.52679,18.16248],[81.44988,17.89707],[81.40165,17.88727],[81.39221,17.81014]]]}},{"type":"Feature","properties":{"id":"BV"},"geometry":{"type":"Polygon","coordinates":[[[2.85578,-54.70531],[3.87126,-54.71036],[3.87947,-54.15611],[2.86398,-54.15099],[2.85578,-54.70531]]]}},{"type":"Feature","properties":{"id":"NP"},"geometry":{"type":"Polygon","coordinates":[[[80.05743,28.91479],[80.06957,28.82763],[80.12125,28.82346],[80.37188,28.63371],[80.44504,28.63098],[80.52443,28.54897],[80.50575,28.6706],[80.55142,28.69182],[80.89648,28.47237],[81.08507,28.38346],[81.19847,28.36284],[81.32923,28.13521],[81.38683,28.17638],[81.48179,28.12148],[81.47867,28.08303],[81.91223,27.84995],[81.97214,27.93322],[82.06554,27.92222],[82.46405,27.6716],[82.70378,27.72122],[82.74119,27.49838],[82.93261,27.50328],[82.94938,27.46036],[83.19413,27.45632],[83.27197,27.38309],[83.2673,27.36235],[83.29999,27.32778],[83.35136,27.33885],[83.38872,27.39276],[83.39495,27.4798],[83.61288,27.47013],[83.85595,27.35797],[83.86182,27.4241],[83.93306,27.44939],[84.02229,27.43836],[84.10791,27.52399],[84.21376,27.45218],[84.25735,27.44941],[84.29315,27.39],[84.62161,27.33885],[84.69166,27.21294],[84.64496,27.04669],[84.793,26.9968],[84.82913,27.01989],[84.85754,26.98984],[84.96687,26.95599],[84.97186,26.9149],[85.00536,26.89523],[85.05592,26.88991],[85.02635,26.85381],[85.15883,26.86966],[85.19291,26.86909],[85.18046,26.80519],[85.21159,26.75933],[85.34302,26.74954],[85.47752,26.79292],[85.56471,26.84133],[85.5757,26.85955],[85.59461,26.85161],[85.61621,26.86721],[85.66239,26.84822],[85.73483,26.79613],[85.72315,26.67471],[85.76907,26.63076],[85.83126,26.61134],[85.85126,26.60866],[85.8492,26.56667],[86.02729,26.66756],[86.13596,26.60651],[86.22513,26.58863],[86.26235,26.61886],[86.31564,26.61925],[86.49726,26.54218],[86.54258,26.53819],[86.57073,26.49825],[86.61313,26.48658],[86.62686,26.46891],[86.69124,26.45169],[86.74025,26.42386],[86.76797,26.45892],[86.82898,26.43919],[86.94543,26.52076],[86.95912,26.52076],[87.01559,26.53228],[87.04691,26.58685],[87.0707,26.58571],[87.09147,26.45039],[87.14751,26.40542],[87.18863,26.40558],[87.24682,26.4143],[87.26587,26.40592],[87.26568,26.37294],[87.34568,26.34787],[87.37314,26.40815],[87.46566,26.44058],[87.51571,26.43106],[87.55274,26.40596],[87.59175,26.38342],[87.66803,26.40294],[87.67893,26.43501],[87.76004,26.40711],[87.7918,26.46737],[87.84193,26.43663],[87.89085,26.48565],[87.90115,26.44923],[88.00895,26.36029],[88.09414,26.43732],[88.09963,26.54195],[88.16452,26.64111],[88.1659,26.68177],[88.19107,26.75516],[88.12302,26.95324],[88.13422,26.98705],[88.11719,26.98758],[87.9887,27.11045],[88.01587,27.21388],[88.01646,27.21612],[88.07277,27.43007],[88.04008,27.49223],[88.19107,27.79285],[88.1973,27.85067],[88.13378,27.88015],[87.82681,27.95248],[87.72718,27.80938],[87.56996,27.84517],[87.11696,27.84104],[87.03757,27.94835],[86.75582,28.04182],[86.74181,28.10638],[86.56265,28.09569],[86.51609,27.96623],[86.42736,27.91122],[86.22966,27.9786],[86.18607,28.17364],[86.088,28.09264],[86.08333,28.02121],[86.12069,27.93047],[86.06309,27.90021],[85.94946,27.9401],[85.97813,27.99023],[85.90743,28.05144],[85.84672,28.18187],[85.74864,28.23126],[85.71907,28.38064],[85.69105,28.38475],[85.60854,28.25045],[85.59765,28.30529],[85.4233,28.32996],[85.38127,28.28336],[85.10729,28.34092],[85.18668,28.54076],[85.19135,28.62825],[85.06059,28.68562],[84.85511,28.58041],[84.62317,28.73887],[84.47528,28.74023],[84.2231,28.89571],[84.24801,29.02783],[84.18107,29.23451],[83.97559,29.33091],[83.82303,29.30513],[83.63156,29.16249],[83.44787,29.30513],[83.28131,29.56813],[83.07116,29.61957],[82.73024,29.81695],[82.5341,29.9735],[82.38622,30.02608],[82.16984,30.0692],[82.19475,30.16884],[82.10757,30.23745],[82.10135,30.35439],[81.99082,30.33423],[81.62033,30.44703],[81.41018,30.42153],[81.39928,30.21862],[81.33355,30.15303],[81.2623,30.14596],[81.29032,30.08806],[81.24362,30.0126],[81.12842,30.01395],[81.03953,30.20059],[80.93695,30.18229],[80.8778,30.13384],[80.67076,29.95732],[80.60226,29.95732],[80.56957,29.88176],[80.56247,29.86661],[80.48997,29.79566],[80.43458,29.80466],[80.41554,29.79451],[80.36803,29.73865],[80.38428,29.68513],[80.41858,29.63581],[80.37939,29.57098],[80.24322,29.44299],[80.31428,29.30784],[80.28626,29.20327],[80.24112,29.21414],[80.26602,29.13938],[80.23178,29.11626],[80.18085,29.13649],[80.05743,28.91479]]]}},{"type":"Feature","properties":{"id":"NR"},"geometry":{"type":"Polygon","coordinates":[[[166.65,-0.8],[167.2,-0.8],[167.2,-0.26],[166.65,-0.26],[166.65,-0.8]]]}},{"type":"Feature","properties":{"id":"PK"},"geometry":{"type":"Polygon","coordinates":[[[60.87231,29.86514],[61.31508,29.38903],[61.53765,29.00507],[61.65978,28.77937],[61.93581,28.55284],[62.40259,28.42703],[62.59499,28.24842],[62.79412,28.28108],[62.7638,28.02992],[62.84905,27.47627],[62.79684,27.34381],[62.80604,27.22412],[63.19649,27.25674],[63.32283,27.14437],[63.25005,27.08692],[63.25005,26.84212],[63.18688,26.83844],[63.1889,26.65072],[62.77352,26.64099],[62.31484,26.528],[62.21304,26.26601],[62.05117,26.31647],[61.89391,26.26251],[61.83831,26.07249],[61.83968,25.7538],[61.683,25.66638],[61.6433,25.27541],[61.57592,25.0492],[61.5251,24.57287],[68.11329,23.53945],[68.20763,23.85849],[68.39339,23.96838],[68.74643,23.97027],[68.7416,24.31904],[68.90914,24.33156],[68.97781,24.26021],[69.07806,24.29777],[69.19341,24.25646],[69.29778,24.28712],[69.59579,24.29777],[69.73335,24.17007],[70.03428,24.172],[70.11712,24.30915],[70.5667,24.43787],[70.57906,24.27774],[70.71502,24.23517],[70.88393,24.27398],[70.85784,24.30903],[70.94985,24.3791],[71.04461,24.34657],[71.12838,24.42662],[71.00341,24.46038],[70.97594,24.60904],[71.09405,24.69017],[70.94002,24.92843],[70.89148,25.15064],[70.66695,25.39314],[70.67382,25.68186],[70.60378,25.71898],[70.53649,25.68928],[70.37444,25.67443],[70.2687,25.71156],[70.0985,25.93238],[70.08193,26.08094],[70.17532,26.24118],[70.17532,26.55362],[70.05584,26.60398],[69.88555,26.56836],[69.50904,26.74892],[69.58519,27.18109],[70.03136,27.56627],[70.12502,27.8057],[70.37307,28.01208],[70.60927,28.02178],[70.79054,27.68423],[71.89921,27.96035],[71.9244,28.11555],[72.20329,28.3869],[72.29495,28.66367],[72.40402,28.78283],[72.94272,29.02487],[73.01337,29.16422],[73.05886,29.1878],[73.28094,29.56646],[73.3962,29.94707],[73.58665,30.01848],[73.80299,30.06969],[73.97225,30.19829],[73.95736,30.28466],[73.88993,30.36305],[74.5616,31.04153],[74.67971,31.05479],[74.6852,31.12771],[74.60006,31.13711],[74.60281,31.10419],[74.56023,31.08303],[74.51629,31.13829],[74.53223,31.30321],[74.59773,31.4136],[74.64713,31.45605],[74.59319,31.50197],[74.61517,31.55698],[74.57498,31.60382],[74.47771,31.72227],[74.58907,31.87824],[74.79919,31.95983],[74.86236,32.04485],[74.9269,32.0658],[75.00793,32.03786],[75.25649,32.10187],[75.38046,32.26836],[75.28259,32.36556],[75.03265,32.49538],[74.97634,32.45367],[74.84725,32.49075],[74.68362,32.49298],[74.67431,32.56676],[74.65251,32.56416],[74.64424,32.60985],[74.69542,32.66792],[74.65345,32.71225],[74.7113,32.84219],[74.64675,32.82604],[74.6289,32.75561],[74.45312,32.77755],[74.41467,32.90563],[74.31227,32.92795],[74.34875,32.97823],[74.31854,33.02891],[74.17571,33.07495],[74.15374,33.13477],[74.02144,33.18908],[74.01366,33.25199],[74.08782,33.26232],[74.17983,33.3679],[74.18121,33.4745],[74.10115,33.56392],[74.03576,33.56718],[73.97367,33.64061],[73.98968,33.66155],[73.96423,33.73071],[74.00891,33.75437],[74.05898,33.82089],[74.14001,33.83002],[74.26086,33.92237],[74.25262,34.01577],[74.21554,34.03853],[73.91341,34.01235],[73.88732,34.05105],[73.90677,34.10504],[73.98208,34.2522],[73.90517,34.35317],[73.8475,34.32935],[73.74862,34.34183],[73.74999,34.3781],[73.88732,34.48911],[73.89419,34.54568],[73.93951,34.57169],[73.93401,34.63386],[73.96423,34.68244],[74.12897,34.70073],[74.31239,34.79626],[74.58083,34.77386],[74.6663,34.703],[75.01479,34.64629],[75.38009,34.55021],[75.75438,34.51827],[76.04614,34.67566],[76.15463,34.6429],[76.47186,34.78965],[76.67648,34.76371],[76.74377,34.84039],[76.74514,34.92488],[76.87193,34.96906],[76.99251,34.93349],[77.11796,35.05419],[76.93465,35.39866],[76.85088,35.39754],[76.75475,35.52617],[76.77323,35.66062],[76.50961,35.8908],[76.33453,35.84296],[76.14913,35.82848],[76.15325,35.9264],[75.93028,36.13136],[76.00906,36.17511],[76.0324,36.41198],[75.92391,36.56986],[75.72737,36.7529],[75.45562,36.71971],[75.40481,36.95382],[75.13839,37.02622],[74.56453,37.03023],[74.53739,36.96224],[74.43389,37.00977],[74.04856,36.82648],[73.82685,36.91421],[72.6323,36.84601],[72.18135,36.71838],[71.80267,36.49924],[71.60491,36.39429],[71.19505,36.04134],[71.37969,35.95865],[71.55273,35.71483],[71.49917,35.6267],[71.65435,35.4479],[71.54294,35.31037],[71.5541,35.28776],[71.67495,35.21262],[71.52938,35.09023],[71.55273,35.02615],[71.49917,35.00478],[71.50329,34.97328],[71.29472,34.87728],[71.28356,34.80882],[71.08718,34.69034],[71.11602,34.63047],[71.0089,34.54568],[71.02401,34.44835],[71.17662,34.36769],[71.12815,34.26619],[71.13078,34.16503],[71.09453,34.13524],[71.09307,34.11961],[71.06933,34.10564],[71.07345,34.06242],[70.88119,33.97933],[70.54336,33.9463],[69.90203,34.04194],[69.87307,33.9689],[69.85671,33.93719],[70.00503,33.73528],[70.14236,33.71701],[70.14785,33.6553],[70.20141,33.64387],[70.17062,33.53535],[70.32775,33.34496],[70.13686,33.21064],[70.07369,33.22557],[70.02563,33.14282],[69.85259,33.09451],[69.79766,33.13247],[69.71526,33.09911],[69.57656,33.09911],[69.49004,33.01509],[69.49854,32.88843],[69.5436,32.8768],[69.47082,32.85834],[69.38018,32.76601],[69.43649,32.7302],[69.44747,32.6678],[69.38155,32.56601],[69.2868,32.53938],[69.23599,32.45946],[69.27932,32.29119],[69.27032,32.14141],[69.3225,31.93186],[69.20577,31.85957],[69.11514,31.70782],[69.00939,31.62249],[68.95995,31.64822],[68.91078,31.59687],[68.79997,31.61665],[68.6956,31.75687],[68.57475,31.83158],[68.44222,31.76446],[68.27605,31.75863],[68.25614,31.80357],[68.1655,31.82691],[68.00071,31.6564],[67.86887,31.63536],[67.72056,31.52304],[67.58323,31.52772],[67.62374,31.40473],[67.7748,31.4188],[67.78854,31.33203],[67.29964,31.19586],[67.03323,31.24519],[67.04147,31.31561],[66.83273,31.26867],[66.72561,31.20526],[66.68166,31.07597],[66.58175,30.97532],[66.42645,30.95309],[66.39194,30.9408],[66.28413,30.57001],[66.34869,30.404],[66.23609,30.06321],[66.36042,29.9583],[66.24175,29.85181],[65.04005,29.53957],[64.62116,29.58903],[64.19796,29.50407],[64.12966,29.39157],[63.5876,29.50456],[62.47751,29.40782],[60.87231,29.86514]]]}},{"type":"Feature","properties":{"id":"IN-JH"},"geometry":{"type":"Polygon","coordinates":[[[83.32134,24.10131],[83.4185,24.08596],[83.55342,23.8783],[83.69487,23.82209],[83.71564,23.68587],[83.78929,23.58853],[84.02652,23.63068],[83.97039,23.37424],[84.06978,23.33059],[84.02961,23.16592],[84.14909,22.97546],[84.3695,22.97704],[84.40177,22.89325],[83.99425,22.53602],[84.27955,22.32467],[84.4876,22.40785],[85.05958,22.48083],[85.10662,22.318],[85.01426,22.10886],[85.22952,22.00003],[85.38986,22.15783],[85.90587,21.95801],[86.01779,22.30434],[85.96115,22.48242],[86.03324,22.56012],[86.7247,22.21569],[86.88812,22.24715],[86.78031,22.56487],[86.42188,22.77586],[86.5472,22.97641],[86.21761,22.98747],[86.04423,23.14572],[85.92269,23.12236],[85.82279,23.26784],[85.89969,23.37062],[85.8633,23.41316],[85.87223,23.47489],[86.04389,23.48245],[86.0123,23.56619],[86.14311,23.56619],[86.14208,23.47647],[86.31374,23.42544],[86.44214,23.62816],[86.5242,23.62628],[86.59286,23.67156],[86.7374,23.68194],[86.79508,23.68351],[86.8198,23.76115],[86.79748,23.82806],[86.89704,23.89054],[87.08312,23.80639],[87.27933,23.87516],[87.23281,24.04238],[87.4443,23.97527],[87.71038,24.14048],[87.63587,24.21628],[87.7811,24.34928],[87.77904,24.57147],[87.89543,24.57616],[87.83431,24.7381],[87.95173,24.97174],[87.78831,25.15212],[87.86521,25.27139],[87.65373,25.28009],[87.58369,25.35271],[87.53906,25.27947],[87.48962,25.29933],[87.47348,25.19686],[87.32688,25.22016],[87.28946,25.09741],[87.21668,25.09243],[87.1511,25.02246],[87.15866,24.89391],[87.10235,24.84812],[87.05532,24.61237],[86.60316,24.60457],[86.45004,24.36586],[86.28833,24.46027],[86.32163,24.5824],[86.12491,24.61143],[86.12869,24.71876],[85.7373,24.8154],[85.66108,24.61518],[84.89307,24.36304],[84.81994,24.529],[84.57447,24.41151],[84.49756,24.28546],[83.9994,24.63141],[83.94584,24.54899],[83.75015,24.50245],[83.49883,24.52651],[83.39275,24.50027],[83.46347,24.36992],[83.37387,24.3155],[83.4027,24.26793],[83.32134,24.10131]]]}},{"type":"Feature","properties":{"id":"PA"},"geometry":{"type":"Polygon","coordinates":[[[-83.05209,8.33394],[-82.9388,8.26634],[-82.88641,8.10219],[-82.89137,8.05755],[-82.89978,8.04083],[-82.94503,7.93865],[-82.13751,6.97312],[-78.06168,7.07793],[-77.89178,7.22681],[-77.81426,7.48319],[-77.72157,7.47612],[-77.72514,7.72348],[-77.57185,7.51147],[-77.17257,7.97422],[-77.45064,8.49991],[-77.32389,8.81247],[-77.58292,9.22278],[-78.79327,9.93766],[-82.51044,9.65379],[-82.56507,9.57279],[-82.61345,9.49881],[-82.66667,9.49746],[-82.77206,9.59573],[-82.87919,9.62645],[-82.84871,9.4973],[-82.93516,9.46741],[-82.93516,9.07687],[-82.72126,8.97125],[-82.88253,8.83331],[-82.91377,8.774],[-82.92068,8.74832],[-82.8794,8.6981],[-82.82739,8.60153],[-82.83975,8.54755],[-82.83322,8.52464],[-82.8382,8.48117],[-82.8679,8.44042],[-82.93056,8.43465],[-83.05209,8.33394]]]}},{"type":"Feature","properties":{"id":"PE"},"geometry":{"type":"Polygon","coordinates":[[[-84.52388,-3.36941],[-84.46057,-7.80762],[-78.15039,-17.99635],[-73.98689,-20.10822],[-70.59118,-18.35072],[-70.378,-18.3495],[-70.31267,-18.31258],[-70.16394,-18.31737],[-69.96732,-18.25992],[-69.81607,-18.12582],[-69.75305,-17.94605],[-69.82868,-17.72048],[-69.79087,-17.65563],[-69.66483,-17.65083],[-69.46897,-17.4988],[-69.46863,-17.37466],[-69.62883,-17.28142],[-69.16896,-16.72233],[-69.00853,-16.66769],[-69.04027,-16.57214],[-68.98358,-16.42165],[-68.79464,-16.33272],[-68.96238,-16.194],[-69.09986,-16.22693],[-69.20291,-16.16668],[-69.40336,-15.61358],[-69.14856,-15.23478],[-69.36254,-14.94634],[-68.88135,-14.18639],[-69.05265,-13.68546],[-68.8864,-13.40792],[-68.85615,-12.87769],[-68.65044,-12.50689],[-68.98115,-11.8979],[-69.57156,-10.94555],[-69.57835,-10.94051],[-69.90896,-10.92744],[-70.38791,-11.07096],[-70.51395,-10.92249],[-70.64134,-11.0108],[-70.62487,-9.80666],[-70.55429,-9.76692],[-70.58453,-9.58303],[-70.53373,-9.42628],[-71.23394,-9.9668],[-72.14742,-9.98049],[-72.31883,-9.5184],[-72.72216,-9.41397],[-73.21498,-9.40904],[-72.92886,-9.04074],[-73.76576,-7.89884],[-73.65485,-7.77897],[-73.96938,-7.58465],[-73.77011,-7.28944],[-73.73986,-6.87919],[-73.12983,-6.43852],[-73.24579,-6.05764],[-72.83973,-5.14765],[-72.64391,-5.0391],[-71.87003,-4.51661],[-70.96814,-4.36915],[-70.77601,-4.15717],[-70.33236,-4.15214],[-70.19582,-4.3607],[-70.11305,-4.27281],[-70.00888,-4.37833],[-69.94708,-4.2431],[-70.3374,-3.79505],[-70.52393,-3.87553],[-70.71396,-3.7921],[-70.04609,-2.73906],[-70.94377,-2.23142],[-71.75223,-2.15058],[-72.92587,-2.44514],[-73.65312,-1.26222],[-74.26675,-0.97229],[-74.42701,-0.50218],[-75.18513,-0.0308],[-75.25764,-0.11943],[-75.40192,-0.17196],[-75.61997,-0.10012],[-75.60169,-0.18708],[-75.53615,-0.19213],[-75.22862,-0.60048],[-75.22862,-0.95588],[-75.3872,-0.9374],[-75.57429,-1.55961],[-76.05203,-2.12179],[-76.6324,-2.58397],[-77.94147,-3.05454],[-78.19369,-3.36431],[-78.14324,-3.47653],[-78.22642,-3.51113],[-78.24589,-3.39907],[-78.34362,-3.38633],[-78.68394,-4.60754],[-78.85149,-4.66795],[-79.01659,-5.01481],[-79.1162,-4.97774],[-79.26248,-4.95167],[-79.59402,-4.46848],[-79.79722,-4.47558],[-80.13945,-4.29786],[-80.39256,-4.48269],[-80.46386,-4.41516],[-80.32114,-4.21323],[-80.45023,-4.20938],[-80.4822,-4.05477],[-80.46386,-4.01342],[-80.13232,-3.90317],[-80.19926,-3.68894],[-80.18741,-3.63994],[-80.19848,-3.59249],[-80.21642,-3.5888],[-80.20535,-3.51667],[-80.22629,-3.501],[-80.23651,-3.48652],[-80.24586,-3.48677],[-80.24475,-3.47846],[-80.24123,-3.46124],[-80.20647,-3.431],[-80.30602,-3.39149],[-84.52388,-3.36941]]]}},{"type":"Feature","properties":{"id":"PG"},"geometry":{"type":"Polygon","coordinates":[[[140.85295,-6.72996],[141.01763,-6.90181],[141.00782,-9.1242],[140.88922,-9.34945],[142.0601,-9.56571],[142.0953,-9.23534],[142.1462,-9.19923],[142.23304,-9.19253],[142.31447,-9.24611],[142.5723,-9.35994],[142.81927,-9.31709],[144.30183,-9.48146],[155.22803,-12.9001],[154.74815,-7.33315],[155.60735,-6.92266],[155.69784,-6.92661],[155.92557,-6.84664],[156.03993,-6.65703],[156.03296,-6.55528],[157.60997,-5.69776],[156.88247,-1.39237],[141.00167,0.68547],[140.99813,-6.3233],[140.85295,-6.72996]]]}},{"type":"Feature","properties":{"id":"PL"},"geometry":{"type":"Polygon","coordinates":[[[14.12256,52.84311],[14.13806,52.82392],[14.22071,52.81175],[14.61073,52.59847],[14.6289,52.57136],[14.60081,52.53116],[14.63056,52.48993],[14.54423,52.42568],[14.55228,52.35264],[14.56378,52.33838],[14.58149,52.28007],[14.70139,52.25038],[14.71319,52.22144],[14.68344,52.19612],[14.70616,52.16927],[14.67683,52.13936],[14.6917,52.10283],[14.72971,52.09167],[14.76026,52.06624],[14.71339,52.00337],[14.70488,51.97679],[14.7139,51.95643],[14.71836,51.95606],[14.72163,51.95188],[14.7177,51.94048],[14.70601,51.92944],[14.6933,51.9044],[14.6588,51.88359],[14.59089,51.83302],[14.60493,51.80473],[14.64625,51.79472],[14.66386,51.73282],[14.69065,51.70842],[14.75392,51.67445],[14.75759,51.62318],[14.7727,51.61263],[14.71125,51.56209],[14.73047,51.54606],[14.72652,51.53902],[14.73219,51.52922],[14.94749,51.47155],[14.9652,51.44793],[14.96899,51.38367],[14.98008,51.33449],[15.04288,51.28387],[15.01242,51.21285],[15.0047,51.16874],[14.99311,51.16249],[14.99414,51.15813],[15.00083,51.14974],[14.99646,51.14365],[14.99079,51.14284],[14.99689,51.12205],[14.98229,51.11354],[14.97938,51.07742],[14.95529,51.04552],[14.92942,50.99744],[14.89252,50.94999],[14.89681,50.9422],[14.81664,50.88148],[14.82803,50.86966],[14.99852,50.86817],[15.01088,50.97984],[14.96419,50.99108],[15.02433,51.0242],[15.03895,51.0123],[15.06218,51.02269],[15.10152,51.01095],[15.11937,50.99021],[15.16744,51.01959],[15.1743,50.9833],[15.2361,50.99886],[15.27043,50.97724],[15.2773,50.8907],[15.36656,50.83956],[15.3803,50.77187],[15.43798,50.80833],[15.73186,50.73885],[15.81683,50.75666],[15.87331,50.67188],[15.97219,50.69799],[16.0175,50.63009],[15.98317,50.61528],[16.02437,50.60046],[16.10265,50.66405],[16.20839,50.63096],[16.23174,50.67101],[16.33611,50.66579],[16.44597,50.58041],[16.34572,50.49575],[16.31413,50.50274],[16.19526,50.43291],[16.21585,50.40627],[16.22821,50.41054],[16.28118,50.36891],[16.30289,50.38292],[16.36495,50.37679],[16.3622,50.34875],[16.39379,50.3207],[16.42674,50.32509],[16.56407,50.21009],[16.55446,50.16613],[16.63137,50.1142],[16.7014,50.09659],[16.8456,50.20834],[16.98018,50.24172],[17.00353,50.21449],[17.02825,50.23118],[16.99803,50.25753],[17.02138,50.27772],[16.99803,50.30316],[16.94448,50.31281],[16.90877,50.38642],[16.85933,50.41093],[16.89229,50.45117],[17.1224,50.39494],[17.14498,50.38117],[17.19579,50.38817],[17.19991,50.3654],[17.27681,50.32246],[17.34273,50.32947],[17.34548,50.2628],[17.3702,50.28123],[17.58889,50.27837],[17.67764,50.28977],[17.69292,50.32859],[17.74648,50.29966],[17.72176,50.25665],[17.76296,50.23382],[17.70528,50.18812],[17.59404,50.16437],[17.66683,50.10275],[17.6888,50.12037],[17.7506,50.07896],[17.77669,50.02253],[17.86886,49.97452],[18.00191,50.01723],[18.04585,50.01194],[18.04585,50.03311],[18.00396,50.04954],[18.03212,50.06574],[18.07898,50.04535],[18.10628,50.00223],[18.20241,49.99958],[18.21752,49.97309],[18.27107,49.96779],[18.27794,49.93863],[18.31914,49.91565],[18.33278,49.92415],[18.33562,49.94747],[18.41604,49.93498],[18.53423,49.89906],[18.54495,49.9079],[18.54299,49.92537],[18.57697,49.91565],[18.57045,49.87849],[18.60341,49.86256],[18.57183,49.83334],[18.61278,49.7618],[18.61368,49.75426],[18.62645,49.75002],[18.62943,49.74603],[18.62676,49.71983],[18.69817,49.70473],[18.72838,49.68163],[18.80479,49.6815],[18.84786,49.5446],[18.84521,49.51672],[18.94536,49.52143],[18.97283,49.49914],[18.9742,49.39557],[19.18019,49.41165],[19.25435,49.53391],[19.36009,49.53747],[19.37795,49.574],[19.45348,49.61583],[19.52626,49.57311],[19.53313,49.52856],[19.57845,49.46077],[19.64162,49.45184],[19.6375,49.40897],[19.72127,49.39288],[19.78581,49.41701],[19.82237,49.27806],[19.75286,49.20751],[19.86409,49.19316],[19.90529,49.23532],[19.98494,49.22904],[20.08238,49.1813],[20.13738,49.31685],[20.21977,49.35265],[20.31453,49.34817],[20.31728,49.39914],[20.39939,49.3896],[20.46422,49.41612],[20.5631,49.375],[20.61666,49.41791],[20.72274,49.41813],[20.77971,49.35383],[20.9229,49.29626],[20.98733,49.30774],[21.09799,49.37176],[21.041,49.41791],[21.12477,49.43666],[21.19756,49.4054],[21.27858,49.45988],[21.43376,49.41433],[21.62328,49.4447],[21.77983,49.35443],[21.82927,49.39467],[21.96385,49.3437],[22.04427,49.22136],[22.56155,49.08865],[22.89122,49.00725],[22.86336,49.10513],[22.72009,49.20288],[22.748,49.32759],[22.69444,49.49378],[22.64534,49.53094],[22.78304,49.65543],[22.80261,49.69098],[22.83179,49.69875],[22.99329,49.84249],[23.28221,50.0957],[23.67635,50.33385],[23.71382,50.38248],[23.79445,50.40481],[23.99563,50.41289],[24.03668,50.44507],[24.07048,50.5071],[24.0996,50.60752],[24.0595,50.71625],[23.95925,50.79271],[23.99254,50.83847],[24.0952,50.83262],[24.14524,50.86128],[24.04576,50.90196],[23.92217,51.00836],[23.90376,51.07697],[23.80678,51.18405],[23.63858,51.32182],[23.69905,51.40871],[23.62751,51.50512],[23.56236,51.53673],[23.57053,51.55938],[23.53198,51.74298],[23.62691,51.78208],[23.61523,51.92066],[23.68733,51.9906],[23.64066,52.07626],[23.61,52.11264],[23.54314,52.12148],[23.47859,52.18215],[23.20071,52.22848],[23.18196,52.28812],[23.34141,52.44845],[23.45112,52.53774],[23.58296,52.59868],[23.73615,52.6149],[23.93763,52.71332],[23.91805,52.94016],[23.94689,52.95919],[23.92184,53.02079],[23.87548,53.0831],[23.91393,53.16469],[23.85657,53.22923],[23.81995,53.24131],[23.62004,53.60942],[23.51284,53.95052],[23.48261,53.98855],[23.52702,54.04622],[23.49196,54.14764],[23.45223,54.17775],[23.42418,54.17911],[23.39525,54.21672],[23.3494,54.25155],[23.24656,54.25701],[23.15938,54.29894],[23.15526,54.31076],[23.13905,54.31567],[23.104,54.29794],[23.04323,54.31567],[23.05726,54.34565],[22.99649,54.35927],[23.00584,54.38514],[22.83756,54.40827],[22.79705,54.36264],[21.41123,54.32395],[20.63871,54.3706],[19.8038,54.44203],[19.64312,54.45423],[18.57853,55.25302],[14.20811,54.12784],[14.22634,53.9291],[14.20647,53.91671],[14.18544,53.91258],[14.20823,53.90776],[14.21323,53.8664],[14.27249,53.74464],[14.26782,53.69866],[14.2836,53.67721],[14.27133,53.66613],[14.28477,53.65955],[14.2853,53.63392],[14.31904,53.61581],[14.30416,53.55499],[14.3273,53.50587],[14.35209,53.49506],[14.4215,53.27724],[14.44133,53.27427],[14.45125,53.26241],[14.40662,53.21098],[14.37853,53.20405],[14.36696,53.16444],[14.38679,53.13669],[14.35044,53.05829],[14.25954,53.00264],[14.14056,52.95786],[14.15873,52.87715],[14.12256,52.84311]]]}},{"type":"Feature","properties":{"id":"KP"},"geometry":{"type":"Polygon","coordinates":[[[123.85601,37.49093],[124.67666,38.05679],[124.84224,37.977],[124.87921,37.80827],[125.06408,37.66334],[125.37112,37.62643],[125.81159,37.72949],[126.13074,37.70512],[126.18776,37.74728],[126.19097,37.81462],[126.24402,37.83113],[126.43239,37.84095],[126.46818,37.80873],[126.56709,37.76857],[126.59918,37.76364],[126.66067,37.7897],[126.68793,37.83728],[126.68793,37.9175],[126.67023,37.95852],[126.84961,38.0344],[126.88106,38.10246],[126.95887,38.1347],[126.95338,38.17735],[127.04479,38.25518],[127.15749,38.30722],[127.38727,38.33227],[127.49672,38.30647],[127.55013,38.32257],[128.02917,38.31861],[128.27652,38.41657],[128.31105,38.58462],[128.37487,38.62345],[128.65655,38.61914],[131.95041,41.5445],[130.65022,42.32281],[130.66367,42.38024],[130.64181,42.41422],[130.60805,42.4317],[130.56835,42.43281],[130.55143,42.52158],[130.50123,42.61636],[130.44361,42.54849],[130.41826,42.6011],[130.2385,42.71127],[130.23068,42.80125],[130.26095,42.9027],[130.09764,42.91425],[130.12957,42.98361],[129.96409,42.97306],[129.95082,43.01051],[129.8865,43.00395],[129.85261,42.96494],[129.83277,42.86746],[129.80719,42.79218],[129.7835,42.76521],[129.77183,42.69435],[129.75294,42.59409],[129.72541,42.43739],[129.60482,42.44461],[129.54701,42.37254],[129.42882,42.44702],[129.28541,42.41574],[129.22423,42.3553],[129.22285,42.26491],[129.15178,42.17224],[128.96068,42.06657],[128.94007,42.03537],[128.04487,42.01769],[128.15119,41.74568],[128.30716,41.60322],[128.20061,41.40895],[128.18546,41.41279],[128.12967,41.37931],[128.03311,41.39232],[128.02633,41.42103],[127.92943,41.44291],[127.29712,41.49473],[127.17841,41.59714],[126.90729,41.79955],[126.60631,41.65565],[126.53189,41.35206],[126.242,41.15454],[126.00335,40.92835],[125.76869,40.87908],[125.71172,40.85223],[124.86913,40.45387],[124.40719,40.13655],[124.38556,40.11047],[124.3322,40.05573],[124.37089,40.03004],[124.35029,39.95639],[124.23201,39.9248],[124.17532,39.8232],[123.90497,38.79949],[123.85601,37.49093]]]}},{"type":"Feature","properties":{"id":"PT"},"geometry":{"type":"Polygon","coordinates":[[[-32.42346,39.07068],[-15.92339,29.50503],[-14.33337,30.94071],[-7.37282,36.96896],[-7.39769,37.16868],[-7.41133,37.20314],[-7.41854,37.23813],[-7.43227,37.25152],[-7.43974,37.38913],[-7.46878,37.47127],[-7.51759,37.56119],[-7.41981,37.75729],[-7.33441,37.81193],[-7.27314,37.90145],[-7.24544,37.98884],[-7.12648,38.00296],[-7.10366,38.04404],[-7.05966,38.01966],[-7.00375,38.01914],[-6.93418,38.21454],[-7.09389,38.17227],[-7.15581,38.27597],[-7.32529,38.44336],[-7.265,38.61674],[-7.26174,38.72107],[-7.03848,38.87221],[-7.051,38.907],[-6.95211,39.0243],[-6.97004,39.07619],[-7.04011,39.11919],[-7.10692,39.10275],[-7.14929,39.11287],[-7.12811,39.17101],[-7.23566,39.20132],[-7.23403,39.27579],[-7.3149,39.34857],[-7.2927,39.45847],[-7.49477,39.58794],[-7.54121,39.66717],[-7.33507,39.64569],[-7.24707,39.66576],[-7.01613,39.66877],[-6.97492,39.81488],[-6.91463,39.86618],[-6.86737,40.01986],[-6.94233,40.10716],[-7.00589,40.12087],[-7.02544,40.18564],[-7.00426,40.23169],[-6.86085,40.26776],[-6.86085,40.2976],[-6.80218,40.33239],[-6.78426,40.36468],[-6.84618,40.42177],[-6.84944,40.46394],[-6.7973,40.51723],[-6.80218,40.55067],[-6.84292,40.56801],[-6.79567,40.65955],[-6.82826,40.74603],[-6.82337,40.84472],[-6.79892,40.84842],[-6.80707,40.88047],[-6.84292,40.89771],[-6.8527,40.93958],[-6.9357,41.02888],[-6.913,41.03922],[-6.88843,41.03027],[-6.84781,41.02692],[-6.80942,41.03629],[-6.79241,41.05397],[-6.75655,41.10187],[-6.77319,41.13049],[-6.69711,41.1858],[-6.68286,41.21641],[-6.65046,41.24725],[-6.55937,41.24417],[-6.38551,41.35274],[-6.38553,41.38655],[-6.3306,41.37677],[-6.26777,41.48796],[-6.19128,41.57638],[-6.29863,41.66432],[-6.44204,41.68258],[-6.49907,41.65823],[-6.54633,41.68623],[-6.56426,41.74219],[-6.51374,41.8758],[-6.56752,41.88429],[-6.5447,41.94371],[-6.58544,41.96674],[-6.61967,41.94008],[-6.75004,41.94129],[-6.76959,41.98734],[-6.81196,41.99097],[-6.82174,41.94493],[-6.94396,41.94403],[-6.95537,41.96553],[-6.98144,41.9728],[-7.01078,41.94977],[-7.07596,41.94977],[-7.08574,41.97401],[-7.14115,41.98855],[-7.18549,41.97515],[-7.18677,41.88793],[-7.32366,41.8406],[-7.37092,41.85031],[-7.42864,41.80589],[-7.42854,41.83262],[-7.44759,41.84451],[-7.45566,41.86488],[-7.49803,41.87095],[-7.52737,41.83939],[-7.62188,41.83089],[-7.58603,41.87944],[-7.65774,41.88308],[-7.69848,41.90977],[-7.84188,41.88065],[-7.88055,41.84571],[-7.88751,41.92553],[-7.90707,41.92432],[-7.92336,41.8758],[-7.9804,41.87337],[-8.01136,41.83453],[-8.0961,41.81024],[-8.16455,41.81753],[-8.16944,41.87944],[-8.19551,41.87459],[-8.2185,41.91237],[-8.16232,41.9828],[-8.08796,42.01398],[-8.08847,42.05767],[-8.11729,42.08537],[-8.18178,42.06436],[-8.19406,42.12141],[-8.18947,42.13853],[-8.1986,42.15402],[-8.22406,42.1328],[-8.24681,42.13993],[-8.2732,42.12396],[-8.29809,42.106],[-8.32161,42.10218],[-8.33912,42.08358],[-8.36353,42.09065],[-8.38323,42.07683],[-8.40143,42.08052],[-8.42512,42.07199],[-8.44123,42.08218],[-8.48185,42.0811],[-8.52837,42.07658],[-8.5252,42.06264],[-8.54563,42.0537],[-8.58086,42.05147],[-8.59493,42.05708],[-8.63791,42.04691],[-8.64626,42.03668],[-8.65832,42.02972],[-8.6681,41.99703],[-8.69071,41.98862],[-8.7478,41.96282],[-8.74606,41.9469],[-8.75712,41.92833],[-8.81794,41.90375],[-8.87157,41.86488],[-9.14112,41.86623],[-30.18705,41.4962],[-32.42346,39.07068]]]}},{"type":"Feature","properties":{"id":"PY"},"geometry":{"type":"Polygon","coordinates":[[[-62.64455,-22.25091],[-62.51761,-22.37684],[-62.22768,-22.55807],[-61.9756,-23.0507],[-61.0782,-23.62932],[-60.99754,-23.80934],[-60.28163,-24.04436],[-60.03367,-24.00701],[-59.45482,-24.34787],[-59.33886,-24.49935],[-58.33055,-24.97099],[-58.25492,-24.92528],[-57.80821,-25.13863],[-57.57431,-25.47269],[-57.87176,-25.93604],[-58.1188,-26.16704],[-58.3198,-26.83443],[-58.65321,-27.14028],[-58.59549,-27.29973],[-58.04205,-27.2387],[-56.85337,-27.5165],[-56.18313,-27.29851],[-55.89195,-27.3467],[-55.74475,-27.44485],[-55.59094,-27.32444],[-55.62322,-27.1941],[-55.39611,-26.97679],[-55.25243,-26.93808],[-55.16948,-26.96068],[-55.06351,-26.80195],[-55.00584,-26.78754],[-54.80868,-26.55669],[-54.70732,-26.45099],[-54.69333,-26.37705],[-54.67359,-25.98607],[-54.60664,-25.9691],[-54.62063,-25.91213],[-54.59398,-25.59224],[-54.59509,-25.53696],[-54.60196,-25.48397],[-54.62033,-25.46026],[-54.4423,-25.13381],[-54.28207,-24.07305],[-54.32807,-24.01865],[-54.6238,-23.83078],[-55.02691,-23.97317],[-55.0518,-23.98666],[-55.12292,-23.99669],[-55.41784,-23.9657],[-55.44117,-23.9185],[-55.43585,-23.87157],[-55.5555,-23.28237],[-55.52288,-23.2595],[-55.5446,-23.22811],[-55.63849,-22.95122],[-55.62493,-22.62765],[-55.68742,-22.58407],[-55.6986,-22.56268],[-55.72366,-22.5519],[-55.741,-22.52018],[-55.74941,-22.46436],[-55.8331,-22.29008],[-56.23206,-22.25347],[-56.45893,-22.08072],[-56.5212,-22.11556],[-56.6508,-22.28387],[-57.98625,-22.09157],[-57.94642,-21.73799],[-57.88239,-21.6868],[-57.93492,-21.65505],[-57.84536,-20.93155],[-58.16225,-20.16193],[-58.23216,-19.80058],[-59.06965,-19.29148],[-60.00638,-19.2981],[-61.73723,-19.63958],[-61.93912,-20.10053],[-62.26883,-20.55311],[-62.2757,-21.06657],[-62.64455,-22.25091]]]}},{"type":"Feature","properties":{"id":"QA"},"geometry":{"type":"Polygon","coordinates":[[[50.57069,25.57887],[50.8133,24.74049],[50.92992,24.54396],[51.09638,24.46907],[51.29972,24.50747],[51.39468,24.62785],[51.58834,24.66608],[51.83108,24.71675],[51.83682,26.70231],[50.93865,26.30758],[50.81266,25.88946],[50.86149,25.6965],[50.7801,25.595],[50.80824,25.54641],[50.57069,25.57887]]]}},{"type":"Feature","properties":{"id":"RO"},"geometry":{"type":"Polygon","coordinates":[[[20.26068,46.12332],[20.35862,45.99356],[20.54818,45.89939],[20.65645,45.82801],[20.70069,45.7493],[20.77416,45.75601],[20.78446,45.78522],[20.82364,45.77738],[20.80361,45.65875],[20.76798,45.60969],[20.83321,45.53567],[20.77217,45.49788],[20.86026,45.47295],[20.87948,45.42743],[21.09894,45.30144],[21.17612,45.32566],[21.20392,45.2677],[21.29398,45.24148],[21.48278,45.19557],[21.51299,45.15345],[21.4505,45.04294],[21.35855,45.01941],[21.54938,44.9327],[21.56328,44.89502],[21.48202,44.87199],[21.44013,44.87613],[21.35643,44.86364],[21.38802,44.78133],[21.55007,44.77304],[21.60019,44.75208],[21.61942,44.67059],[21.67504,44.67107],[21.71692,44.65349],[21.7795,44.66165],[21.99364,44.63395],[22.08016,44.49844],[22.13234,44.47444],[22.18315,44.48179],[22.30844,44.6619],[22.45301,44.7194],[22.61917,44.61489],[22.69196,44.61587],[22.76749,44.54446],[22.70981,44.51852],[22.61368,44.55719],[22.56493,44.53419],[22.54021,44.47836],[22.45436,44.47258],[22.56012,44.30712],[22.68166,44.28206],[22.67173,44.21564],[23.04988,44.07694],[23.01674,44.01946],[22.87873,43.9844],[22.83753,43.88055],[22.85314,43.84452],[23.05288,43.79494],[23.26772,43.84843],[23.4507,43.84936],[23.61687,43.79289],[23.73978,43.80627],[24.18149,43.68218],[24.35364,43.70211],[24.50264,43.76314],[24.62281,43.74082],[24.73542,43.68523],[24.96682,43.72693],[25.10718,43.6831],[25.17144,43.70261],[25.39528,43.61866],[25.72792,43.69263],[25.94911,43.85745],[26.05584,43.90925],[26.10115,43.96908],[26.38764,44.04356],[26.62712,44.05698],[26.95141,44.13555],[27.26845,44.12602],[27.39757,44.0141],[27.60834,44.01206],[27.64542,44.04958],[27.73468,43.95326],[27.92008,44.00761],[27.99558,43.84193],[28.23293,43.76],[29.24336,43.70874],[30.04414,45.08461],[29.69272,45.19227],[29.65428,45.25629],[29.68175,45.26885],[29.59798,45.38857],[29.42632,45.44545],[29.24779,45.43388],[28.96077,45.33164],[28.94292,45.28045],[28.81383,45.3384],[28.78911,45.24179],[28.71358,45.22631],[28.5735,45.24759],[28.34554,45.32102],[28.28504,45.43907],[28.21139,45.46895],[28.18741,45.47358],[28.08927,45.6051],[28.16568,45.6421],[28.13111,45.92819],[28.08612,46.01105],[28.13684,46.18099],[28.10937,46.22852],[28.19864,46.31869],[28.18902,46.35283],[28.25769,46.43334],[28.22281,46.50481],[28.24808,46.64305],[28.12173,46.82283],[28.09095,46.97621],[27.81892,47.1381],[27.73172,47.29248],[27.68706,47.28962],[27.60263,47.32507],[27.55731,47.46637],[27.47942,47.48113],[27.3979,47.59473],[27.32202,47.64009],[27.25519,47.71366],[27.29069,47.73722],[27.1618,47.92391],[27.15622,47.98538],[27.02985,48.09083],[27.04118,48.12522],[26.96119,48.13003],[26.98042,48.15752],[26.94265,48.1969],[26.87708,48.19919],[26.81161,48.25049],[26.62823,48.25804],[26.55202,48.22445],[26.33504,48.18418],[26.17711,47.99246],[26.05901,47.9897],[25.77723,47.93919],[25.63878,47.94924],[25.23778,47.89403],[25.11144,47.75203],[24.88896,47.7234],[24.81893,47.82031],[24.70632,47.84428],[24.61994,47.95062],[24.43578,47.97131],[24.34926,47.9244],[24.22566,47.90231],[24.11281,47.91487],[24.06466,47.95317],[24.02999,47.95087],[24.00801,47.968],[23.98553,47.96076],[23.96337,47.96672],[23.94192,47.94868],[23.89352,47.94512],[23.8602,47.9329],[23.80904,47.98142],[23.75188,47.99705],[23.66262,47.98786],[23.63894,48.00293],[23.5653,48.00499],[23.52803,48.01818],[23.4979,47.96858],[23.33577,48.0237],[23.27397,48.08245],[23.15999,48.12188],[23.1133,48.08061],[23.08858,48.00716],[23.0158,47.99338],[22.92241,48.02002],[22.94301,47.96672],[22.89849,47.95851],[22.77991,47.87211],[22.76617,47.8417],[22.67247,47.7871],[22.46559,47.76583],[22.41979,47.7391],[22.31816,47.76126],[22.00917,47.50492],[22.03389,47.42508],[22.01055,47.37767],[21.94463,47.38046],[21.78395,47.11104],[21.648,47.03902],[21.68645,46.99595],[21.59581,46.91628],[21.59307,46.86935],[21.52028,46.84118],[21.48935,46.7577],[21.5151,46.72147],[21.43926,46.65109],[21.33214,46.63035],[21.26929,46.4993],[21.28061,46.44941],[21.16872,46.30118],[21.06572,46.24897],[20.86797,46.28884],[20.74574,46.25467],[20.76085,46.21002],[20.63863,46.12728],[20.49718,46.18721],[20.45377,46.14405],[20.35573,46.16629],[20.28324,46.1438],[20.26068,46.12332]]]}},{"type":"Feature","properties":{"id":"RU-KGD"},"geometry":{"type":"Polygon","coordinates":[[[18.57853,55.25302],[19.64312,54.45423],[19.8038,54.44203],[20.63871,54.3706],[21.41123,54.32395],[22.79705,54.36264],[22.7253,54.41732],[22.70208,54.45312],[22.67788,54.532],[22.71293,54.56454],[22.68021,54.58486],[22.7522,54.63525],[22.74225,54.64339],[22.75467,54.6483],[22.73397,54.66604],[22.73631,54.72952],[22.87317,54.79492],[22.85083,54.88711],[22.76422,54.92521],[22.68723,54.9811],[22.65451,54.97037],[22.60075,55.01863],[22.58907,55.07085],[22.47688,55.04408],[22.31562,55.0655],[22.14267,55.05345],[22.11697,55.02131],[22.06087,55.02935],[22.02582,55.05078],[22.03984,55.07888],[21.99543,55.08691],[21.96505,55.07353],[21.85521,55.09493],[21.64954,55.1791],[21.55605,55.20311],[21.51095,55.18507],[21.46766,55.21115],[21.38446,55.29348],[21.35465,55.28427],[21.26425,55.24456],[20.95181,55.27994],[20.60454,55.40986],[18.57853,55.25302]]]}},{"type":"Feature","properties":{"id":"RW"},"geometry":{"type":"Polygon","coordinates":[[[28.86193,-2.53185],[28.87943,-2.55165],[28.89288,-2.55848],[28.90226,-2.62385],[28.89793,-2.66111],[28.94346,-2.69124],[29.00357,-2.70596],[29.04081,-2.7416],[29.0562,-2.58632],[29.32234,-2.6483],[29.36805,-2.82933],[29.88237,-2.75105],[29.95911,-2.33348],[30.14034,-2.43626],[30.42933,-2.31064],[30.54501,-2.41404],[30.83915,-2.35795],[30.89303,-2.08223],[30.80802,-1.91477],[30.84079,-1.64652],[30.71974,-1.43244],[30.57123,-1.33264],[30.50889,-1.16412],[30.45116,-1.10641],[30.47194,-1.0555],[30.35212,-1.06896],[30.16369,-1.34303],[29.912,-1.48269],[29.82657,-1.31187],[29.59061,-1.39016],[29.53062,-1.40499],[29.45038,-1.5054],[29.36322,-1.50887],[29.24323,-1.66826],[29.24458,-1.69663],[29.11847,-1.90576],[29.17562,-2.12278],[29.105,-2.27043],[29.00051,-2.29001],[28.95642,-2.37321],[28.89601,-2.37321],[28.86826,-2.41888],[28.86846,-2.44866],[28.89132,-2.47557],[28.89342,-2.49017],[28.88846,-2.50493],[28.87497,-2.50887],[28.86209,-2.5231],[28.86193,-2.53185]]]}},{"type":"Feature","properties":{"id":"EH"},"geometry":{"type":"Polygon","coordinates":[[[-17.35589,20.80492],[-17.0471,20.76408],[-17.0695,20.85742],[-17.06781,20.92697],[-17.0396,20.9961],[-17.0357,21.05368],[-16.99806,21.12142],[-16.95474,21.33997],[-13.01525,21.33343],[-13.08438,22.53866],[-13.15313,22.75649],[-13.10753,22.89493],[-13.00412,23.02297],[-12.5741,23.28975],[-12.36213,23.3187],[-12.14969,23.41935],[-12.00251,23.4538],[-12.0002,25.9986],[-8.66721,25.99918],[-8.66674,27.31569],[-8.66879,27.6666],[-8.77527,27.66663],[-8.71787,26.9898],[-9.08698,26.98639],[-9.56957,26.90042],[-9.81998,26.71379],[-10.68417,26.90984],[-11.35695,26.8505],[-11.23622,26.72023],[-11.38635,26.611],[-11.62052,26.05229],[-12.06001,26.04442],[-12.12281,25.13682],[-12.92147,24.39502],[-13.00628,24.01923],[-13.75627,23.77231],[-14.10361,22.75501],[-14.1291,22.41636],[-14.48112,22.00886],[-14.47329,21.63839],[-14.78487,21.36587],[-16.44269,21.39745],[-16.9978,21.36239],[-17.02707,21.34022],[-17.21511,21.34226],[-17.35589,20.80492]]]}},{"type":"Feature","properties":{"id":"SA"},"geometry":{"type":"Polygon","coordinates":[[[34.46254,27.99552],[34.51305,27.70027],[37.8565,22.00903],[39.63762,18.37348],[41.37609,16.19728],[42.15205,16.40211],[42.76801,16.40371],[42.94625,16.39721],[42.94351,16.49467],[42.97215,16.51093],[43.11601,16.53166],[43.15274,16.67248],[43.22066,16.65179],[43.21325,16.74416],[43.25857,16.75304],[43.26303,16.79479],[43.24801,16.80613],[43.22956,16.80613],[43.22012,16.83932],[43.18338,16.84852],[43.1398,16.90696],[43.19328,16.94703],[43.1813,16.98438],[43.18233,17.02673],[43.23967,17.03428],[43.17787,17.14717],[43.20156,17.25901],[43.32653,17.31179],[43.22533,17.38343],[43.29185,17.53224],[43.43005,17.56148],[43.70631,17.35762],[44.50126,17.47475],[46.31018,17.20464],[46.76494,17.29151],[47.00571,16.94765],[47.48245,17.10808],[47.58351,17.50366],[48.19996,18.20584],[49.04884,18.59899],[52.00311,19.00083],[54.99756,20.00083],[55.66469,21.99658],[55.2137,22.71065],[55.13599,22.63334],[52.56622,22.94341],[51.59617,24.12041],[51.58871,24.27256],[51.41644,24.39615],[51.58834,24.66608],[51.39468,24.62785],[51.29972,24.50747],[51.09638,24.46907],[50.92992,24.54396],[50.8133,24.74049],[50.57069,25.57887],[50.302,25.87592],[50.26923,26.08243],[50.38162,26.53976],[50.71771,26.73086],[50.37726,27.89227],[49.98877,27.87827],[49.00421,28.81495],[48.42991,28.53628],[47.70561,28.5221],[47.59863,28.66798],[47.58376,28.83382],[47.46202,29.0014],[46.5527,29.10283],[46.42415,29.05947],[44.72255,29.19736],[42.97796,30.48295],[42.97601,30.72204],[40.01521,32.05667],[39.29903,32.23259],[38.99233,31.99721],[36.99791,31.50081],[37.99354,30.49998],[37.66395,30.33245],[37.4971,29.99949],[36.75083,29.86903],[36.50005,29.49696],[36.07081,29.18469],[34.95987,29.35727],[34.88293,29.37455],[34.46254,27.99552]]]}},{"type":"Feature","properties":{"id":"CN-LN"},"geometry":{"type":"Polygon","coordinates":[[[118.88992,40.9576],[118.90296,40.75297],[119.12406,40.67647],[119.26277,40.53154],[119.55253,40.54876],[119.58137,40.36799],[119.73586,40.11431],[119.98931,39.77661],[120.97044,38.45359],[123.90497,38.79949],[124.17532,39.8232],[124.23201,39.9248],[124.35029,39.95639],[124.37089,40.03004],[124.3322,40.05573],[124.38556,40.11047],[124.40719,40.13655],[124.86913,40.45387],[125.71172,40.85223],[125.66574,40.91403],[125.56892,40.89327],[125.63552,40.95086],[125.78487,41.16185],[125.75225,41.23005],[125.63724,41.26877],[125.63449,41.33325],[125.57544,41.39174],[125.54283,41.39767],[125.49167,41.53222],[125.44738,41.67393],[125.32241,41.67034],[125.28671,41.9554],[125.41854,42.09159],[125.25443,42.31387],[125.18096,42.29813],[124.97909,42.77574],[124.84863,42.79086],[124.85961,43.17563],[124.45793,42.81907],[124.35836,42.88854],[124.41261,43.06738],[124.28832,43.14608],[124.28077,43.21268],[124.14619,43.2412],[123.79806,43.49627],[123.68682,43.3756],[123.55224,42.99862],[123.24737,42.98255],[122.80517,42.73087],[122.41241,42.8659],[121.92077,42.67032],[121.86309,42.53588],[121.66534,42.43967],[121.56646,42.51968],[121.28494,42.43359],[121.03774,42.27019],[120.5722,42.16747],[120.40878,41.98297],[120.18424,41.84245],[120.09498,41.68932],[120.02975,41.71341],[120.03662,41.81277],[119.83474,42.21428],[119.54498,42.29356],[119.50584,42.39709],[119.27032,42.25901],[119.23736,42.19596],[119.29916,42.12522],[119.3795,42.08803],[119.28268,41.78155],[119.32456,41.62519],[119.39392,41.58566],[119.36576,41.43191],[119.29435,41.32732],[119.23562,41.3149],[119.24972,41.27264],[119.07325,41.08608],[118.92631,41.06382],[119.01695,40.97523],[118.88992,40.9576]]]}},{"type":"Feature","properties":{"id":"SD"},"geometry":{"type":"Polygon","coordinates":[[[21.81432,12.81362],[21.89371,12.68001],[21.98711,12.63292],[22.15679,12.66634],[22.22684,12.74682],[22.46345,12.61925],[22.38873,12.45514],[22.50548,12.16769],[22.48369,12.02766],[22.64092,12.07485],[22.54907,11.64372],[22.7997,11.40424],[22.93124,11.41645],[22.97249,11.21955],[22.87758,10.91915],[23.02221,10.69235],[23.3128,10.45214],[23.67164,9.86923],[23.69155,9.67566],[24.09319,9.66572],[24.12744,9.73784],[24.49389,9.79962],[24.84653,9.80643],[24.97739,9.9081],[25.05688,10.06776],[25.0918,10.33718],[25.78141,10.42599],[25.93163,10.38159],[25.93241,10.17941],[26.21338,9.91545],[26.35815,9.57946],[26.70685,9.48735],[27.14427,9.62858],[27.90704,9.61323],[28.99983,9.67155],[29.06988,9.74826],[29.53844,9.75133],[29.54,10.07949],[29.94629,10.29245],[30.00389,10.28633],[30.53005,9.95992],[30.82893,9.71451],[30.84605,9.7498],[31.28504,9.75287],[31.77539,10.28939],[31.99177,10.65065],[32.46967,11.04662],[32.39358,11.18207],[32.39578,11.70208],[32.10079,11.95203],[32.73921,11.95203],[32.73921,12.22757],[33.25876,12.22111],[33.13988,11.43248],[33.26977,10.83632],[33.24645,10.77913],[33.52294,10.64382],[33.66604,10.44254],[33.80913,10.32994],[33.90159,10.17179],[33.96984,10.15446],[33.99185,9.99623],[33.96323,9.80972],[33.9082,9.762],[33.87958,9.49937],[34.10229,9.50238],[34.08717,9.55243],[34.13186,9.7492],[34.20484,9.9033],[34.22718,10.02506],[34.32102,10.11599],[34.34783,10.23914],[34.2823,10.53508],[34.4372,10.781],[34.59062,10.89072],[34.77383,10.74588],[34.77532,10.69027],[34.86618,10.74588],[34.86916,10.78832],[34.97491,10.86147],[34.97789,10.91559],[34.93172,10.95946],[35.01215,11.19626],[34.95704,11.24448],[35.09556,11.56278],[35.05832,11.71158],[35.11492,11.85156],[35.24302,11.91132],[35.70476,12.67101],[36.01458,12.72478],[36.14268,12.70879],[36.16651,12.88019],[36.13374,12.92665],[36.24545,13.36759],[36.38993,13.56459],[36.48824,13.83954],[36.44653,13.95666],[36.54376,14.25597],[36.44337,15.14963],[36.54276,15.23478],[36.69761,15.75323],[36.76371,15.80831],[36.92193,16.23451],[36.99777,17.07172],[37.42694,17.04041],[37.50967,17.32199],[38.13362,17.53906],[38.37133,17.66269],[38.45916,17.87167],[38.57727,17.98125],[39.63762,18.37348],[37.8565,22.00903],[34.0765,22.00501],[33.99686,21.76784],[33.57251,21.72406],[33.17563,22.00405],[24.99885,21.99535],[24.99794,19.99661],[23.99715,20.00038],[23.99539,19.49944],[23.99997,15.69575],[23.62785,15.7804],[23.38812,15.69649],[23.10792,15.71297],[22.93201,15.55107],[22.92579,15.47007],[22.99584,15.40105],[22.99584,15.22989],[22.66115,14.86308],[22.70474,14.69149],[22.38562,14.58907],[22.44944,14.24986],[22.55997,14.23024],[22.5553,14.11704],[22.22995,13.96754],[22.08674,13.77863],[22.29689,13.3731],[22.1599,13.19281],[22.02914,13.13976],[21.94819,13.05637],[21.81432,12.81362]]]}},{"type":"Feature","properties":{"id":"SS"},"geometry":{"type":"Polygon","coordinates":[[[23.44744,8.99128],[23.59065,8.99743],[23.51905,8.71749],[24.25691,8.69288],[24.13238,8.36959],[24.35965,8.26177],[24.85156,8.16933],[24.98855,7.96588],[25.25319,7.8487],[25.29214,7.66675],[25.20649,7.61115],[25.20337,7.50312],[25.35281,7.42595],[25.37461,7.33024],[25.90076,7.09549],[26.38022,6.63493],[26.32729,6.36272],[26.58259,6.1987],[26.51721,6.09655],[27.22705,5.71254],[27.22705,5.62889],[27.28621,5.56382],[27.23017,5.37167],[27.26886,5.25876],[27.44012,5.07349],[27.56656,4.89375],[27.65462,4.89375],[27.76469,4.79284],[27.79551,4.59976],[28.20719,4.35614],[28.6651,4.42638],[28.8126,4.48784],[29.03054,4.48784],[29.22207,4.34297],[29.43341,4.50101],[29.49726,4.7007],[29.82087,4.56246],[29.79666,4.37809],[30.06964,4.13221],[30.1621,4.10586],[30.22374,3.93896],[30.27658,3.95653],[30.47691,3.83353],[30.55396,3.84451],[30.57378,3.74567],[30.56277,3.62703],[30.78512,3.67097],[30.80713,3.60506],[30.85997,3.5743],[30.85153,3.48867],[30.97601,3.693],[31.16666,3.79853],[31.29476,3.8015],[31.50478,3.67814],[31.50776,3.63652],[31.72075,3.74354],[31.81459,3.82083],[31.86821,3.78664],[31.96205,3.6499],[31.95907,3.57408],[32.05187,3.589],[32.08491,3.56287],[32.08866,3.53543],[32.19888,3.50867],[32.20782,3.6053],[32.41337,3.748],[32.72021,3.77327],[32.89746,3.81339],[33.02852,3.89296],[33.18356,3.77812],[33.51264,3.75068],[33.9873,4.23316],[34.47601,4.72162],[35.34151,5.02364],[35.30992,4.90402],[35.47843,4.91872],[35.42366,4.76969],[35.51424,4.61643],[35.9419,4.61933],[35.82118,4.77382],[35.81968,5.10757],[35.8576,5.33413],[35.50792,5.42431],[35.29938,5.34042],[35.31188,5.50106],[35.13058,5.62118],[35.12611,5.68937],[35.00546,5.89387],[34.96227,6.26415],[35.01738,6.46991],[34.87736,6.60161],[34.77459,6.5957],[34.65096,6.72589],[34.53776,6.74808],[34.53925,6.82794],[34.47669,6.91076],[34.35753,6.91963],[34.19369,7.04382],[34.19369,7.12807],[34.01495,7.25664],[34.03878,7.27437],[34.02984,7.36449],[33.87642,7.5491],[33.71407,7.65983],[33.44745,7.7543],[33.32531,7.71297],[33.24637,7.77939],[33.04944,7.78989],[33.0006,7.90333],[33.08401,8.05822],[33.18083,8.13047],[33.1853,8.29264],[33.19721,8.40317],[33.3119,8.45474],[33.54575,8.47094],[33.66938,8.44442],[33.71407,8.3678],[33.87195,8.41938],[33.89579,8.4842],[34.01346,8.50041],[34.14453,8.60204],[34.14304,9.04654],[34.10229,9.50238],[33.87958,9.49937],[33.9082,9.762],[33.96323,9.80972],[33.99185,9.99623],[33.96984,10.15446],[33.90159,10.17179],[33.80913,10.32994],[33.66604,10.44254],[33.52294,10.64382],[33.24645,10.77913],[33.26977,10.83632],[33.13988,11.43248],[33.25876,12.22111],[32.73921,12.22757],[32.73921,11.95203],[32.10079,11.95203],[32.39578,11.70208],[32.39358,11.18207],[32.46967,11.04662],[31.99177,10.65065],[31.77539,10.28939],[31.28504,9.75287],[30.84605,9.7498],[30.82893,9.71451],[30.53005,9.95992],[30.00389,10.28633],[29.94629,10.29245],[29.54,10.07949],[29.53844,9.75133],[29.06988,9.74826],[28.99983,9.67155],[27.90704,9.61323],[27.14427,9.62858],[26.70685,9.48735],[26.35815,9.57946],[26.21338,9.91545],[25.93241,10.17941],[25.93163,10.38159],[25.78141,10.42599],[25.0918,10.33718],[25.05688,10.06776],[24.97739,9.9081],[24.84653,9.80643],[24.49389,9.79962],[24.12744,9.73784],[24.09319,9.66572],[23.69155,9.67566],[23.62179,9.53823],[23.64981,9.44303],[23.64358,9.28637],[23.56263,9.19418],[23.4848,9.16959],[23.44744,8.99128]]]}},{"type":"Feature","properties":{"id":"SN"},"geometry":{"type":"Polygon","coordinates":[[[-18.35085,14.63444],[-17.43598,13.59273],[-15.47902,13.58758],[-15.36504,13.79313],[-14.93719,13.80173],[-14.34721,13.46578],[-13.8955,13.59126],[-13.79409,13.34472],[-14.36795,13.23033],[-15.14917,13.57989],[-15.26908,13.37768],[-15.80478,13.34832],[-15.80355,13.16729],[-16.69343,13.16791],[-16.74676,13.06025],[-17.43966,13.04579],[-17.4623,11.92379],[-16.70562,12.34803],[-16.38191,12.36449],[-16.20591,12.46157],[-15.67302,12.42974],[-15.17582,12.6847],[-13.70523,12.68013],[-13.05296,12.64003],[-13.06603,12.49342],[-12.87336,12.51892],[-12.35415,12.32758],[-11.91331,12.42008],[-11.46267,12.44559],[-11.37536,12.40788],[-11.39935,12.97808],[-11.63025,13.39174],[-11.83345,13.33333],[-12.06897,13.71049],[-11.93043,13.84505],[-12.23936,14.76324],[-13.11029,15.52116],[-13.43135,16.09022],[-13.80075,16.13961],[-14.32144,16.61495],[-15.00557,16.64997],[-15.6509,16.50315],[-16.27016,16.51565],[-16.4429,16.20605],[-16.44814,16.09753],[-16.48967,16.0496],[-16.50854,16.09032],[-17.15288,16.07139],[-18.35085,14.63444]]]}},{"type":"Feature","properties":{"id":"SG"},"geometry":{"type":"Polygon","coordinates":[[[103.56591,1.19719],[103.66049,1.18825],[103.74084,1.12902],[104.03085,1.26954],[104.12282,1.27714],[104.08072,1.35998],[104.09162,1.39694],[104.08871,1.42015],[104.07348,1.43322],[104.04622,1.44691],[104.02277,1.4438],[104.00131,1.42405],[103.93384,1.42926],[103.89565,1.42841],[103.86383,1.46288],[103.81181,1.47953],[103.76395,1.45183],[103.74161,1.4502],[103.7219,1.46108],[103.67468,1.43166],[103.62738,1.35255],[103.56591,1.19719]]]}},{"type":"Feature","properties":{"id":"SL"},"geometry":{"type":"Polygon","coordinates":[[[-14.36218,8.64107],[-12.15048,6.15992],[-11.50429,6.92704],[-11.4027,6.97746],[-11.29417,7.21576],[-10.60422,7.7739],[-10.60492,8.04072],[-10.57523,8.04829],[-10.51554,8.1393],[-10.45023,8.15627],[-10.35227,8.15223],[-10.29839,8.21283],[-10.31635,8.28554],[-10.30084,8.30008],[-10.27575,8.48711],[-10.37257,8.48941],[-10.54891,8.31174],[-10.63934,8.35326],[-10.70565,8.29235],[-10.61422,8.5314],[-10.47707,8.67669],[-10.56197,8.81225],[-10.5783,9.06386],[-10.74484,9.07998],[-10.6534,9.29919],[-11.2118,10.00098],[-11.89624,9.99763],[-11.91023,9.93927],[-12.12634,9.87203],[-12.24262,9.92386],[-12.47254,9.86834],[-12.76788,9.3133],[-12.94095,9.26335],[-13.08953,9.0409],[-13.18586,9.0925],[-13.29911,9.04245],[-14.36218,8.64107]]]}},{"type":"Feature","properties":{"id":"SV"},"geometry":{"type":"Polygon","coordinates":[[[-90.55276,12.8866],[-88.11443,12.63306],[-87.7346,13.13228],[-87.55124,13.12523],[-87.69751,13.25228],[-87.73714,13.32715],[-87.80177,13.35689],[-87.84675,13.41078],[-87.83467,13.44655],[-87.77354,13.45767],[-87.73841,13.44169],[-87.72115,13.46083],[-87.71657,13.50577],[-87.78148,13.52906],[-87.73106,13.75443],[-87.68821,13.80829],[-87.7966,13.91353],[-88.00331,13.86948],[-88.07641,13.98447],[-88.23018,13.99915],[-88.25791,13.91108],[-88.48982,13.86458],[-88.49738,13.97224],[-88.70661,14.04317],[-88.73182,14.10919],[-88.815,14.11652],[-88.85785,14.17763],[-88.94608,14.20207],[-89.04187,14.33644],[-89.34776,14.43013],[-89.39028,14.44561],[-89.57441,14.41637],[-89.58814,14.33165],[-89.50614,14.26084],[-89.52397,14.22628],[-89.61844,14.21937],[-89.70756,14.1537],[-89.75569,14.07073],[-89.73251,14.04133],[-89.76103,14.02923],[-89.81807,14.07073],[-89.88937,14.0396],[-90.10505,13.85104],[-90.11344,13.73679],[-90.55276,12.8866]]]}},{"type":"Feature","properties":{"id":"SM"},"geometry":{"type":"Polygon","coordinates":[[[12.40415,43.95485],[12.40506,43.94325],[12.41165,43.93769],[12.41551,43.92984],[12.40733,43.92379],[12.41233,43.90956],[12.40935,43.9024],[12.41641,43.89991],[12.44184,43.90498],[12.45648,43.89369],[12.48771,43.89706],[12.49429,43.90973],[12.49247,43.91774],[12.49724,43.92248],[12.50269,43.92363],[12.50496,43.93017],[12.51553,43.94096],[12.51427,43.94897],[12.50655,43.95796],[12.50875,43.96198],[12.50622,43.97131],[12.51109,43.97201],[12.51064,43.98165],[12.5154,43.98508],[12.51463,43.99122],[12.50678,43.99113],[12.49406,43.98492],[12.47853,43.98052],[12.46205,43.97463],[12.44684,43.96597],[12.43662,43.95698],[12.42005,43.9578],[12.41414,43.95273],[12.40415,43.95485]]]}},{"type":"Feature","properties":{"id":"RS"},"geometry":{"type":"Polygon","coordinates":[[[18.81394,45.91329],[18.85783,45.85493],[18.90305,45.71863],[18.96691,45.66731],[18.88776,45.57253],[18.94562,45.53712],[19.07471,45.53086],[19.08364,45.48804],[18.99918,45.49333],[18.97446,45.37528],[19.10774,45.29547],[19.28208,45.23813],[19.41941,45.23475],[19.43589,45.17137],[19.19144,45.17863],[19.14063,45.12972],[19.07952,45.14668],[19.1011,45.01191],[19.05205,44.97692],[19.15573,44.95409],[19.06853,44.89915],[19.02871,44.92541],[18.98957,44.90645],[19.01994,44.85493],[19.18183,44.92055],[19.36722,44.88164],[19.32543,44.74058],[19.26388,44.65412],[19.16699,44.52197],[19.13369,44.52521],[19.12278,44.50132],[19.14837,44.45253],[19.14681,44.41463],[19.11785,44.40313],[19.10749,44.39421],[19.10704,44.38249],[19.10365,44.37795],[19.10298,44.36924],[19.11865,44.36712],[19.1083,44.3558],[19.11547,44.34218],[19.13556,44.338],[19.13332,44.31492],[19.16741,44.28648],[19.18328,44.28383],[19.20508,44.2917],[19.23306,44.26097],[19.26945,44.26957],[19.32464,44.27185],[19.34773,44.23244],[19.3588,44.18353],[19.40927,44.16722],[19.43905,44.13088],[19.47338,44.15034],[19.48386,44.14332],[19.47321,44.1193],[19.51167,44.08158],[19.55999,44.06894],[19.57467,44.04716],[19.61991,44.05254],[19.61836,44.01464],[19.56498,43.99922],[19.52515,43.95573],[19.38439,43.96611],[19.24363,44.01502],[19.23465,43.98764],[19.3986,43.79668],[19.5176,43.71403],[19.50455,43.58385],[19.42696,43.57987],[19.41941,43.54056],[19.36653,43.60921],[19.33426,43.58833],[19.2553,43.5938],[19.24774,43.53061],[19.22807,43.5264],[19.22229,43.47926],[19.44315,43.38846],[19.48171,43.32644],[19.52962,43.31623],[19.54598,43.25158],[19.62661,43.2286],[19.64063,43.19027],[19.76918,43.16044],[19.79255,43.11951],[19.92576,43.08539],[19.96549,43.11098],[19.98887,43.0538],[20.04729,43.02732],[20.05431,42.99571],[20.12325,42.96237],[20.14896,42.99058],[20.16415,42.97177],[20.34528,42.90676],[20.35692,42.8335],[20.40594,42.84853],[20.43734,42.83157],[20.53484,42.8885],[20.48692,42.93208],[20.59929,43.01067],[20.64557,43.00826],[20.69515,43.09641],[20.59929,43.20492],[20.68688,43.21335],[20.73811,43.25068],[20.82145,43.26769],[20.88685,43.21697],[20.83727,43.17842],[20.96287,43.12416],[21.00749,43.13984],[21.05378,43.10707],[21.08952,43.13471],[21.14465,43.11089],[21.16734,42.99694],[21.2041,43.02277],[21.23877,43.00848],[21.23534,42.95523],[21.2719,42.8994],[21.32974,42.90424],[21.36941,42.87397],[21.44047,42.87276],[21.39045,42.74888],[21.47498,42.74695],[21.59154,42.72643],[21.58755,42.70418],[21.6626,42.67813],[21.75025,42.70125],[21.79413,42.65923],[21.75672,42.62695],[21.7327,42.55041],[21.70522,42.54176],[21.7035,42.51899],[21.62556,42.45106],[21.64209,42.41081],[21.62887,42.37664],[21.59029,42.38042],[21.57021,42.3647],[21.53467,42.36809],[21.5264,42.33634],[21.56772,42.30946],[21.58992,42.25915],[21.70111,42.23789],[21.77176,42.2648],[21.84654,42.3247],[21.91595,42.30392],[21.94405,42.34669],[22.02908,42.29848],[22.16384,42.32103],[22.29605,42.37477],[22.29275,42.34913],[22.34773,42.31725],[22.45919,42.33822],[22.47498,42.3915],[22.51961,42.3991],[22.55669,42.50144],[22.43983,42.56851],[22.4997,42.74144],[22.43309,42.82057],[22.54302,42.87774],[22.74826,42.88701],[22.78397,42.98253],[22.89521,43.03625],[22.98104,43.11199],[23.00806,43.19279],[22.89727,43.22417],[22.82036,43.33665],[22.53397,43.47225],[22.47582,43.6558],[22.41043,43.69566],[22.35558,43.81281],[22.41449,44.00514],[22.61688,44.06534],[22.61711,44.16938],[22.67173,44.21564],[22.68166,44.28206],[22.56012,44.30712],[22.45436,44.47258],[22.54021,44.47836],[22.56493,44.53419],[22.61368,44.55719],[22.70981,44.51852],[22.76749,44.54446],[22.69196,44.61587],[22.61917,44.61489],[22.45301,44.7194],[22.30844,44.6619],[22.18315,44.48179],[22.13234,44.47444],[22.08016,44.49844],[21.99364,44.63395],[21.7795,44.66165],[21.71692,44.65349],[21.67504,44.67107],[21.61942,44.67059],[21.60019,44.75208],[21.55007,44.77304],[21.38802,44.78133],[21.35643,44.86364],[21.44013,44.87613],[21.48202,44.87199],[21.56328,44.89502],[21.54938,44.9327],[21.35855,45.01941],[21.4505,45.04294],[21.51299,45.15345],[21.48278,45.19557],[21.29398,45.24148],[21.20392,45.2677],[21.17612,45.32566],[21.09894,45.30144],[20.87948,45.42743],[20.86026,45.47295],[20.77217,45.49788],[20.83321,45.53567],[20.76798,45.60969],[20.80361,45.65875],[20.82364,45.77738],[20.78446,45.78522],[20.77416,45.75601],[20.70069,45.7493],[20.65645,45.82801],[20.54818,45.89939],[20.35862,45.99356],[20.26068,46.12332],[20.09713,46.17315],[20.03533,46.14509],[20.01816,46.17696],[19.93508,46.17553],[19.81491,46.1313],[19.66007,46.19005],[19.56113,46.16824],[19.52473,46.1171],[19.28826,45.99694],[19.14543,45.9998],[19.10388,46.04015],[19.0791,45.96458],[19.01284,45.96529],[18.99712,45.93537],[18.81394,45.91329]]]}},{"type":"Feature","properties":{"id":"SR"},"geometry":{"type":"Polygon","coordinates":[[[-58.0307,3.95513],[-57.35891,3.32121],[-56.70519,2.02964],[-56.55439,2.02003],[-56.47045,1.95135],[-55.99278,1.83137],[-55.89863,1.89861],[-55.92159,2.05236],[-56.13054,2.27723],[-55.96292,2.53188],[-55.71493,2.40342],[-55.01919,2.564],[-54.6084,2.32856],[-54.42864,2.42442],[-54.28534,2.67798],[-53.9849,3.58697],[-53.98914,3.627],[-54.05128,3.63557],[-54.19367,3.84387],[-54.38444,4.13222],[-54.4717,4.91964],[-54.26916,5.26909],[-54.01877,5.52789],[-54.01074,5.68785],[-53.7094,6.2264],[-56.84822,6.73257],[-57.31629,5.33714],[-57.22536,5.15605],[-57.37442,5.0208],[-57.8699,4.89394],[-58.0307,3.95513]]]}},{"type":"Feature","properties":{"id":"SK"},"geometry":{"type":"Polygon","coordinates":[[[16.83317,48.38138],[16.84243,48.35258],[16.90903,48.32519],[16.89461,48.31332],[16.97701,48.17385],[17.02919,48.13996],[17.05735,48.14179],[17.09168,48.09366],[17.07039,48.0317],[17.16001,48.00636],[17.23699,48.02094],[17.71215,47.7548],[18.02938,47.75665],[18.29305,47.73541],[18.56496,47.76588],[18.66521,47.76772],[18.74074,47.8157],[18.8506,47.82308],[18.76821,47.87469],[18.76134,47.97499],[18.82176,48.04206],[19.01952,48.07052],[19.23924,48.0595],[19.28182,48.08336],[19.47957,48.09437],[19.52489,48.19791],[19.63338,48.25006],[19.92452,48.1283],[20.24312,48.2784],[20.29943,48.26104],[20.5215,48.53336],[20.83248,48.5824],[21.11516,48.49546],[21.44063,48.58456],[21.6068,48.50365],[21.67134,48.3989],[21.72525,48.34628],[21.8279,48.33321],[21.83339,48.36242],[22.14689,48.4005],[22.16023,48.56548],[22.21379,48.6218],[22.34151,48.68893],[22.42934,48.92857],[22.48296,48.99172],[22.54338,49.01424],[22.56155,49.08865],[22.04427,49.22136],[21.96385,49.3437],[21.82927,49.39467],[21.77983,49.35443],[21.62328,49.4447],[21.43376,49.41433],[21.27858,49.45988],[21.19756,49.4054],[21.12477,49.43666],[21.041,49.41791],[21.09799,49.37176],[20.98733,49.30774],[20.9229,49.29626],[20.77971,49.35383],[20.72274,49.41813],[20.61666,49.41791],[20.5631,49.375],[20.46422,49.41612],[20.39939,49.3896],[20.31728,49.39914],[20.31453,49.34817],[20.21977,49.35265],[20.13738,49.31685],[20.08238,49.1813],[19.98494,49.22904],[19.90529,49.23532],[19.86409,49.19316],[19.75286,49.20751],[19.82237,49.27806],[19.78581,49.41701],[19.72127,49.39288],[19.6375,49.40897],[19.64162,49.45184],[19.57845,49.46077],[19.53313,49.52856],[19.52626,49.57311],[19.45348,49.61583],[19.37795,49.574],[19.36009,49.53747],[19.25435,49.53391],[19.18019,49.41165],[18.9742,49.39557],[18.97283,49.49914],[18.94536,49.52143],[18.84521,49.51672],[18.74761,49.492],[18.67757,49.50895],[18.6144,49.49824],[18.57183,49.51162],[18.53063,49.49022],[18.54848,49.47059],[18.44686,49.39467],[18.4084,49.40003],[18.4139,49.36517],[18.36446,49.3267],[18.18456,49.28909],[18.15022,49.24518],[18.1104,49.08624],[18.06885,49.03157],[17.91814,49.01784],[17.87831,48.92679],[17.77944,48.92318],[17.73126,48.87885],[17.7094,48.86721],[17.5295,48.81117],[17.45671,48.85004],[17.3853,48.80936],[17.29054,48.85546],[17.19355,48.87602],[17.11202,48.82925],[17.00215,48.70887],[16.93955,48.60371],[16.94611,48.53614],[16.85204,48.44968],[16.8497,48.38321],[16.83588,48.3844],[16.83317,48.38138]]]}},{"type":"Feature","properties":{"id":"SI"},"geometry":{"type":"Polygon","coordinates":[[[13.37671,46.29668],[13.42218,46.20758],[13.47587,46.22725],[13.56114,46.2054],[13.56682,46.18703],[13.64451,46.18966],[13.66472,46.17392],[13.64053,46.13587],[13.57072,46.09022],[13.50104,46.05986],[13.49568,46.04839],[13.50998,46.04498],[13.49702,46.01832],[13.47474,46.00546],[13.50104,45.98078],[13.52963,45.96588],[13.56759,45.96991],[13.58903,45.99009],[13.62074,45.98388],[13.63458,45.98947],[13.64307,45.98326],[13.6329,45.94894],[13.63815,45.93607],[13.61931,45.91782],[13.60857,45.89907],[13.59565,45.89446],[13.58644,45.88173],[13.57563,45.8425],[13.58858,45.83503],[13.59784,45.8072],[13.66986,45.79955],[13.8235,45.7176],[13.83332,45.70855],[13.83422,45.68703],[13.87933,45.65207],[13.9191,45.6322],[13.8695,45.60835],[13.86771,45.59898],[13.84106,45.58185],[13.78445,45.5825],[13.74587,45.59811],[13.7198,45.59352],[13.6076,45.64761],[13.45644,45.59464],[13.56979,45.4895],[13.62902,45.45898],[13.67398,45.4436],[13.7785,45.46787],[13.81742,45.43729],[13.88124,45.42637],[13.90771,45.45149],[13.97309,45.45258],[13.99488,45.47551],[13.96063,45.50825],[14.00578,45.52352],[14.07116,45.48752],[14.20348,45.46896],[14.22371,45.50388],[14.24239,45.50607],[14.26611,45.48239],[14.27681,45.4902],[14.32487,45.47142],[14.36693,45.48642],[14.49769,45.54424],[14.5008,45.60852],[14.53816,45.6205],[14.57397,45.67165],[14.60977,45.66403],[14.59576,45.62812],[14.69694,45.57366],[14.68605,45.53006],[14.71718,45.53442],[14.80124,45.49515],[14.81992,45.45913],[14.90554,45.47769],[14.92266,45.52788],[15.02385,45.48533],[15.05187,45.49079],[15.16862,45.42309],[15.27758,45.46678],[15.33051,45.45258],[15.38188,45.48752],[15.30249,45.53224],[15.29837,45.5841],[15.27747,45.60504],[15.31027,45.6303],[15.34695,45.63382],[15.34214,45.64702],[15.38952,45.63682],[15.4057,45.64727],[15.34919,45.71623],[15.30872,45.69014],[15.25423,45.72275],[15.40836,45.79491],[15.47531,45.79802],[15.47325,45.8253],[15.52234,45.82195],[15.57952,45.84953],[15.64185,45.82915],[15.66662,45.84085],[15.70411,45.8465],[15.68232,45.86819],[15.68383,45.88867],[15.67967,45.90455],[15.70636,45.92116],[15.70327,46.00015],[15.71246,46.01196],[15.72977,46.04682],[15.62317,46.09103],[15.6083,46.11992],[15.59909,46.14761],[15.64904,46.19229],[15.6434,46.21396],[15.67395,46.22478],[15.75436,46.21969],[15.75479,46.20336],[15.78817,46.21719],[15.79284,46.25811],[15.97965,46.30652],[16.07616,46.3463],[16.07314,46.36458],[16.05065,46.3833],[16.05281,46.39141],[16.14859,46.40547],[16.18824,46.38282],[16.30233,46.37837],[16.30162,46.40437],[16.27329,46.41467],[16.27398,46.42875],[16.25124,46.48067],[16.23961,46.49653],[16.26759,46.50566],[16.26733,46.51505],[16.29793,46.5121],[16.37193,46.55008],[16.38771,46.53608],[16.44036,46.5171],[16.5007,46.49644],[16.52604,46.47831],[16.59527,46.47524],[16.52604,46.5051],[16.52885,46.53303],[16.50139,46.56684],[16.39217,46.63673],[16.38594,46.6549],[16.41863,46.66238],[16.42641,46.69228],[16.37816,46.69975],[16.30966,46.7787],[16.31303,46.79838],[16.3408,46.80641],[16.34547,46.83836],[16.2941,46.87137],[16.2365,46.87775],[16.21892,46.86961],[16.15711,46.85434],[16.14365,46.8547],[16.10983,46.867],[16.05786,46.83927],[15.99054,46.82772],[15.99126,46.78199],[15.98432,46.74991],[15.99769,46.7266],[16.02808,46.71094],[16.04347,46.68694],[16.04036,46.6549],[15.99988,46.67947],[15.98512,46.68463],[15.94864,46.68769],[15.87691,46.7211],[15.8162,46.71897],[15.78518,46.70712],[15.76771,46.69863],[15.73823,46.70011],[15.72279,46.69548],[15.69523,46.69823],[15.67411,46.70735],[15.6543,46.70616],[15.6543,46.69228],[15.6365,46.6894],[15.63255,46.68069],[15.62317,46.67947],[15.59826,46.68908],[15.54533,46.66985],[15.55333,46.64988],[15.54431,46.6312],[15.46906,46.61321],[15.45514,46.63697],[15.41235,46.65556],[15.23711,46.63994],[15.14215,46.66131],[15.01451,46.641],[14.98024,46.6009],[14.96002,46.63459],[14.92283,46.60848],[14.87129,46.61],[14.86419,46.59411],[14.83549,46.56614],[14.81836,46.51046],[14.72185,46.49974],[14.66892,46.44936],[14.5942,46.43434],[14.56463,46.37208],[14.52176,46.42617],[14.45877,46.41717],[14.42608,46.44614],[14.314,46.43327],[14.28326,46.44315],[14.15989,46.43327],[14.12097,46.47724],[14.04002,46.49117],[14.00422,46.48474],[13.89837,46.52331],[13.7148,46.5222],[13.68684,46.43881],[13.59777,46.44137],[13.5763,46.42613],[13.5763,46.40915],[13.47019,46.3621],[13.43418,46.35992],[13.44808,46.33507],[13.37671,46.29668]]]}},{"type":"Feature","properties":{"id":"SE"},"geometry":{"type":"Polygon","coordinates":[[[10.40861,58.38489],[12.16597,56.60205],[12.07466,56.29488],[12.65312,56.04345],[12.6372,55.91371],[12.88472,55.63369],[12.60345,55.42675],[12.84405,55.13257],[14.28399,55.1553],[14.89259,55.5623],[15.79951,55.54655],[19.64795,57.06466],[19.84909,57.57876],[20.5104,59.15546],[19.08191,60.19152],[19.23413,60.61414],[20.15877,63.06556],[24.14112,65.39731],[24.15107,65.81427],[24.14798,65.83466],[24.15791,65.85385],[23.90497,66.15802],[23.71339,66.21299],[23.64982,66.30603],[23.67591,66.3862],[23.63776,66.43568],[23.85959,66.56434],[23.89488,66.772],[23.98059,66.79585],[23.98563,66.84149],[23.56214,67.17038],[23.58735,67.20752],[23.54701,67.25435],[23.75372,67.29914],[23.75372,67.43688],[23.39577,67.46974],[23.54701,67.59306],[23.45627,67.85297],[23.65793,67.9497],[23.40081,68.05545],[23.26469,68.15134],[23.15377,68.14759],[23.10336,68.26551],[22.73028,68.40881],[22.00429,68.50692],[21.03001,68.88969],[20.90649,68.89696],[20.85104,68.93142],[20.91658,68.96764],[20.78802,69.03087],[20.55258,69.06069],[20.0695,69.04469],[20.28444,68.93283],[20.33435,68.80174],[20.22027,68.67246],[19.95647,68.55546],[20.22027,68.48759],[19.93508,68.35911],[18.97255,68.52416],[18.63032,68.50849],[18.39503,68.58672],[18.1241,68.53721],[18.13836,68.20874],[17.90787,67.96537],[17.30416,68.11591],[16.7409,67.91037],[16.38441,67.52923],[16.12774,67.52106],[16.09922,67.4364],[16.39154,67.21653],[16.35589,67.06419],[15.37197,66.48217],[15.49318,66.28509],[15.05113,66.15572],[14.53778,66.12399],[14.50926,65.31786],[13.64276,64.58402],[14.11117,64.46674],[14.16051,64.18725],[13.98222,64.00953],[13.23411,64.09087],[12.74105,64.02171],[12.14928,63.59373],[12.19919,63.47935],[11.98529,63.27487],[12.19919,63.00104],[12.07085,62.6297],[12.29187,62.25699],[12.14746,61.7147],[12.40595,61.57226],[12.57707,61.56547],[12.86939,61.35427],[12.69115,61.06584],[12.2277,61.02442],[12.59133,60.50559],[12.52003,60.13846],[12.36317,59.99259],[12.15641,59.8926],[11.87121,59.86039],[11.92112,59.69531],[11.69297,59.59442],[11.8213,59.24985],[11.65732,58.90177],[11.45199,58.89604],[11.4601,58.99022],[11.34459,59.11672],[11.15367,59.07862],[11.08911,58.98745],[10.64958,58.89391],[10.40861,58.38489]]]}},{"type":"Feature","properties":{"id":"SZ"},"geometry":{"type":"Polygon","coordinates":[[[30.78927,-26.48271],[30.81101,-26.84722],[30.88826,-26.79622],[30.97757,-26.92706],[30.96088,-27.0245],[31.15027,-27.20151],[31.49834,-27.31549],[31.97592,-27.31675],[31.97463,-27.11057],[32.00893,-26.8096],[32.09664,-26.80721],[32.13315,-26.84345],[32.13409,-26.5317],[32.07352,-26.40185],[32.10435,-26.15656],[32.08599,-26.00978],[32.00916,-25.999],[31.974,-25.95387],[31.86881,-25.99973],[31.4175,-25.71886],[31.31237,-25.7431],[31.13073,-25.91558],[30.95819,-26.26303],[30.78927,-26.48271]]]}},{"type":"Feature","properties":{"id":"SY"},"geometry":{"type":"Polygon","coordinates":[[[35.48515,34.70851],[35.97386,34.63322],[35.98718,34.64977],[36.29165,34.62991],[36.32399,34.69334],[36.35135,34.68516],[36.35384,34.65447],[36.42941,34.62505],[36.46003,34.6378],[36.45299,34.59438],[36.41429,34.61175],[36.39846,34.55672],[36.3369,34.52629],[36.34745,34.5002],[36.4442,34.50165],[36.46179,34.46541],[36.55853,34.41609],[36.53039,34.3798],[36.56556,34.31881],[36.60778,34.31009],[36.58667,34.27667],[36.59195,34.2316],[36.62537,34.20251],[36.5128,34.09916],[36.50576,34.05982],[36.41078,34.05253],[36.28589,33.91981],[36.38263,33.86579],[36.3967,33.83365],[36.14517,33.85118],[36.06778,33.82927],[35.9341,33.6596],[36.05723,33.57904],[35.94465,33.52774],[35.94816,33.47886],[35.88668,33.43183],[35.82577,33.40479],[35.81324,33.36354],[35.77477,33.33609],[35.813,33.3172],[35.77513,33.27342],[35.81295,33.24841],[35.81647,33.2028],[35.83846,33.19397],[35.84285,33.16673],[35.81911,33.1336],[35.81911,33.11077],[35.84802,33.1031],[35.87188,32.98028],[35.89298,32.9456],[35.87012,32.91976],[35.84021,32.8725],[35.83758,32.82817],[35.78745,32.77938],[35.75983,32.74803],[35.88405,32.71321],[35.93307,32.71966],[35.96633,32.66237],[36.02239,32.65911],[36.08074,32.51463],[36.20379,32.52751],[36.20875,32.49529],[36.23948,32.50108],[36.40959,32.37908],[36.83946,32.31293],[38.79171,33.37328],[40.64314,34.31604],[40.97676,34.39788],[41.12388,34.65742],[41.2345,34.80049],[41.21654,35.1508],[41.26569,35.42708],[41.38184,35.62502],[41.37027,35.84095],[41.2564,36.06012],[41.28864,36.35368],[41.40058,36.52502],[41.81736,36.58782],[42.36697,37.0627],[42.35724,37.10998],[42.32313,37.17814],[42.34735,37.22548],[42.2824,37.2798],[42.26039,37.27017],[42.23683,37.2863],[42.21548,37.28026],[42.20454,37.28715],[42.22381,37.30238],[42.22257,37.31395],[42.2112,37.32491],[42.19301,37.31323],[42.18225,37.28569],[42.00894,37.17209],[41.515,37.08084],[41.21937,37.07665],[40.90856,37.13147],[40.69136,37.0996],[39.81589,36.75538],[39.21538,36.66834],[39.03217,36.70911],[38.74042,36.70629],[38.55908,36.84429],[38.38859,36.90064],[38.21064,36.91842],[37.81974,36.76055],[37.68048,36.75065],[37.49103,36.66904],[37.47253,36.63243],[37.21988,36.6736],[37.16177,36.66069],[37.10894,36.6704],[37.08279,36.63495],[37.02088,36.66422],[37.01647,36.69512],[37.04619,36.71101],[37.04399,36.73483],[36.99886,36.74012],[36.99557,36.75997],[36.66727,36.82901],[36.61581,36.74629],[36.62681,36.71189],[36.57398,36.65186],[36.58829,36.58295],[36.54206,36.49539],[36.6081,36.33772],[36.65653,36.33861],[36.68672,36.23677],[36.6125,36.22592],[36.50463,36.2419],[36.4617,36.20461],[36.39206,36.22088],[36.37474,36.01163],[36.33956,35.98687],[36.30099,36.00985],[36.28338,36.00273],[36.29769,35.96086],[36.27678,35.94839],[36.25366,35.96264],[36.19973,35.95195],[36.17441,35.92076],[36.1623,35.80925],[36.14029,35.81015],[36.13919,35.83692],[36.11827,35.85923],[35.99829,35.88242],[36.01844,35.92403],[36.00514,35.94113],[35.98499,35.94107],[35.931,35.92109],[35.51152,36.10954],[35.48515,34.70851]]]}},{"type":"Feature","properties":{"id":"TD"},"geometry":{"type":"Polygon","coordinates":[[[13.47559,14.40881],[13.6302,13.71094],[14.08251,13.0797],[14.46881,13.08259],[14.56101,12.91036],[14.55058,12.78256],[14.83314,12.62963],[14.90827,12.3269],[14.89019,12.16593],[14.96952,12.0925],[15.00146,12.1223],[15.0349,12.10698],[15.05786,12.0608],[15.04808,11.8731],[15.11579,11.79313],[15.06595,11.71126],[15.13149,11.5537],[15.0585,11.40481],[15.10021,11.04101],[15.04957,11.02347],[15.09127,10.87431],[15.06737,10.80921],[15.15532,10.62846],[15.14936,10.53915],[15.23724,10.47764],[15.30874,10.31063],[15.50535,10.1098],[15.68761,9.99344],[15.41408,9.92876],[15.24618,9.99246],[15.14043,9.99246],[15.05999,9.94845],[14.95722,9.97926],[14.80082,9.93818],[14.4673,10.00264],[14.20411,10.00055],[14.1317,9.82413],[14.01793,9.73169],[13.97544,9.6365],[14.37094,9.2954],[14.35707,9.19611],[14.83566,8.80557],[15.09484,8.65982],[15.20426,8.50892],[15.50743,7.79302],[15.59272,7.7696],[15.56964,7.58936],[15.49743,7.52179],[15.73118,7.52006],[15.79942,7.44149],[16.40703,7.68809],[16.41583,7.77971],[16.58315,7.88657],[16.59415,7.76444],[16.658,7.75353],[16.6668,7.67281],[16.8143,7.53971],[17.67288,7.98905],[17.93926,7.95853],[18.02731,8.01085],[18.6085,8.05009],[18.64153,8.08714],[18.62612,8.14163],[18.67455,8.22226],[18.79783,8.25929],[19.11044,8.68172],[18.86388,8.87971],[19.06421,9.00367],[20.36748,9.11019],[20.82979,9.44696],[21.26348,9.97642],[21.34934,9.95907],[21.52766,10.2105],[21.63553,10.217],[21.71479,10.29932],[21.72139,10.64136],[22.45889,11.00246],[22.87758,10.91915],[22.97249,11.21955],[22.93124,11.41645],[22.7997,11.40424],[22.54907,11.64372],[22.64092,12.07485],[22.48369,12.02766],[22.50548,12.16769],[22.38873,12.45514],[22.46345,12.61925],[22.22684,12.74682],[22.15679,12.66634],[21.98711,12.63292],[21.89371,12.68001],[21.81432,12.81362],[21.94819,13.05637],[22.02914,13.13976],[22.1599,13.19281],[22.29689,13.3731],[22.08674,13.77863],[22.22995,13.96754],[22.5553,14.11704],[22.55997,14.23024],[22.44944,14.24986],[22.38562,14.58907],[22.70474,14.69149],[22.66115,14.86308],[22.99584,15.22989],[22.99584,15.40105],[22.92579,15.47007],[22.93201,15.55107],[23.10792,15.71297],[23.38812,15.69649],[23.62785,15.7804],[23.99997,15.69575],[23.99539,19.49944],[15.99566,23.49639],[14.99751,23.00539],[15.19692,21.99339],[15.20213,21.49365],[15.28332,21.44557],[15.62515,20.95395],[15.57248,20.92138],[15.55382,20.86507],[15.56004,20.79488],[15.59841,20.74039],[15.6721,20.70069],[15.99632,20.35364],[15.75098,19.93002],[15.6032,18.77402],[15.50373,16.89649],[14.37425,15.72591],[13.86301,15.04043],[13.78991,14.87519],[13.809,14.72915],[13.67878,14.64013],[13.68573,14.55276],[13.48259,14.46704],[13.47559,14.40881]]]}},{"type":"Feature","properties":{"id":"IN-GA"},"geometry":{"type":"Polygon","coordinates":[[[73.34747,15.61245],[73.87481,14.75496],[74.16217,14.94976],[74.20543,14.92819],[74.29195,15.02869],[74.2741,15.09996],[74.31838,15.1805],[74.25075,15.25371],[74.33486,15.28352],[74.24903,15.49206],[74.25659,15.64551],[74.11617,15.65411],[74.01592,15.60253],[73.97472,15.62799],[73.94691,15.74236],[73.80477,15.74401],[73.68598,15.72484],[73.34747,15.61245]]]}},{"type":"Feature","properties":{"id":"TG"},"geometry":{"type":"Polygon","coordinates":[[[-0.14462,11.10811],[-0.05733,11.08628],[-0.0275,11.11202],[-0.00514,11.10763],[0.00342,11.08317],[0.02395,11.06229],[0.03355,10.9807],[-0.0063,10.96417],[-0.00908,10.91644],[-0.02685,10.8783],[-0.0228,10.81916],[-0.07183,10.76794],[-0.07327,10.71845],[-0.09141,10.7147],[-0.05945,10.63458],[0.12886,10.53149],[0.18846,10.4096],[0.29453,10.41546],[0.33028,10.30408],[0.39584,10.31112],[0.35293,10.09412],[0.41371,10.06361],[0.41252,10.02018],[0.36366,10.03309],[0.32075,9.72781],[0.34816,9.71607],[0.34816,9.66907],[0.32313,9.6491],[0.28261,9.69022],[0.26712,9.66437],[0.29334,9.59387],[0.36008,9.6256],[0.38153,9.58682],[0.23851,9.57389],[0.2409,9.52335],[0.30406,9.521],[0.31241,9.50337],[0.2254,9.47869],[0.25758,9.42696],[0.33148,9.44812],[0.36485,9.49749],[0.49118,9.48339],[0.56388,9.40697],[0.45424,9.04581],[0.52455,8.87746],[0.37319,8.75262],[0.47211,8.59945],[0.64731,8.48866],[0.73432,8.29529],[0.63897,8.25873],[0.5913,8.19622],[0.61156,8.18324],[0.6056,8.13959],[0.58891,8.12779],[0.62943,7.85751],[0.58295,7.62368],[0.51979,7.58706],[0.52455,7.45354],[0.57223,7.39326],[0.62943,7.41099],[0.65327,7.31643],[0.59606,7.01252],[0.52217,6.9723],[0.52098,6.94391],[0.56508,6.92971],[0.52853,6.82921],[0.57406,6.80348],[0.58176,6.76049],[0.6497,6.73682],[0.63659,6.63857],[0.74862,6.56517],[0.71048,6.53083],[0.89283,6.33779],[0.99652,6.33779],[1.03108,6.24064],[1.05969,6.22998],[1.09187,6.17074],[1.19966,6.17069],[1.19771,6.11522],[1.27574,5.93551],[1.67336,6.02702],[1.62913,6.24075],[1.79826,6.28221],[1.76906,6.43189],[1.58105,6.68619],[1.61812,6.74843],[1.55877,6.99737],[1.64249,6.99562],[1.61838,9.0527],[1.5649,9.16941],[1.41746,9.3226],[1.33675,9.54765],[1.36624,9.5951],[1.35507,9.99525],[0.77666,10.37665],[0.80358,10.71459],[0.8804,10.803],[0.91245,10.99597],[0.66104,10.99964],[0.4958,10.93269],[0.50521,10.98035],[0.48852,10.98561],[0.50388,11.01011],[-0.13493,11.14075],[-0.14462,11.10811]]]}},{"type":"Feature","properties":{"id":"TH"},"geometry":{"type":"Polygon","coordinates":[[[97.19814,8.18901],[99.31854,5.99868],[99.50117,6.44501],[99.91873,6.50233],[100.0756,6.4045],[100.12,6.42105],[100.19511,6.72559],[100.29651,6.68439],[100.30828,6.66462],[100.31618,6.66781],[100.31884,6.66423],[100.32671,6.66526],[100.32607,6.65933],[100.31929,6.65413],[100.35413,6.54932],[100.41152,6.52299],[100.41791,6.5189],[100.42351,6.51762],[100.43027,6.52389],[100.66986,6.45086],[100.74361,6.50811],[100.74822,6.46231],[100.81045,6.45086],[100.85884,6.24929],[101.10313,6.25617],[101.12618,6.19431],[101.06165,6.14161],[101.12388,6.11411],[101.087,5.9193],[101.02708,5.91013],[100.98815,5.79464],[101.14062,5.61613],[101.25755,5.71065],[101.25524,5.78633],[101.58019,5.93534],[101.69773,5.75881],[101.75074,5.79091],[101.80144,5.74505],[101.89188,5.8386],[101.91776,5.84269],[101.92819,5.85511],[101.94712,5.98421],[101.9714,6.00575],[101.97114,6.01992],[101.99209,6.04075],[102.01835,6.05407],[102.09182,6.14161],[102.07732,6.193],[102.08127,6.22679],[102.09086,6.23546],[102.46318,7.22462],[102.47649,9.66162],[102.52395,11.25257],[102.91449,11.65512],[102.90973,11.75613],[102.83957,11.8519],[102.78427,11.98746],[102.77026,12.06815],[102.70176,12.1686],[102.73134,12.37091],[102.78116,12.40284],[102.7796,12.43781],[102.57567,12.65358],[102.51963,12.66117],[102.4994,12.71736],[102.53053,12.77506],[102.49335,12.92711],[102.48694,12.97537],[102.52275,12.99813],[102.46011,13.08057],[102.43422,13.09061],[102.36146,13.26006],[102.36001,13.31142],[102.34611,13.35618],[102.35692,13.38274],[102.35563,13.47307],[102.361,13.50551],[102.33828,13.55613],[102.36859,13.57488],[102.44601,13.5637],[102.5358,13.56933],[102.57573,13.60461],[102.62483,13.60883],[102.58635,13.6286],[102.5481,13.6589],[102.56848,13.69366],[102.72727,13.77806],[102.77864,13.93374],[102.91251,14.01531],[102.93275,14.19044],[103.16469,14.33075],[103.39353,14.35639],[103.53518,14.42575],[103.71109,14.4348],[103.70175,14.38052],[103.93836,14.3398],[104.27616,14.39861],[104.55014,14.36091],[104.69335,14.42726],[104.97667,14.38806],[105.02804,14.23722],[105.08408,14.20402],[105.14012,14.23873],[105.17748,14.34432],[105.20894,14.34967],[105.43783,14.43865],[105.53864,14.55731],[105.5121,14.80802],[105.61162,15.00037],[105.46661,15.13132],[105.58043,15.32724],[105.50662,15.32054],[105.4692,15.33709],[105.47635,15.3796],[105.58191,15.41031],[105.60446,15.53301],[105.61756,15.68792],[105.46573,15.74742],[105.42285,15.76971],[105.37959,15.84074],[105.34115,15.92737],[105.38508,15.987],[105.42001,16.00657],[105.06204,16.09792],[105.00262,16.25627],[104.88057,16.37311],[104.73349,16.565],[104.76099,16.69302],[104.7397,16.81005],[104.76442,16.84752],[104.7373,16.91125],[104.73712,17.01404],[104.80716,17.19025],[104.80061,17.39367],[104.69867,17.53038],[104.45404,17.66788],[104.35432,17.82871],[104.2757,17.86139],[104.21776,17.99335],[104.10927,18.10826],[104.06533,18.21656],[103.97725,18.33631],[103.93916,18.33914],[103.85642,18.28666],[103.82449,18.33979],[103.699,18.34125],[103.60957,18.40528],[103.47773,18.42841],[103.41044,18.4486],[103.30977,18.4341],[103.24779,18.37807],[103.23818,18.34875],[103.29757,18.30475],[103.17093,18.2618],[103.14994,18.23172],[103.1493,18.17799],[103.07343,18.12351],[103.07823,18.03833],[103.0566,18.00144],[103.01998,17.97095],[102.9912,17.9949],[102.95812,18.0054],[102.86323,17.97531],[102.81988,17.94233],[102.79044,17.93612],[102.75954,17.89561],[102.68538,17.86653],[102.67543,17.84529],[102.69946,17.81686],[102.68194,17.80151],[102.59485,17.83537],[102.5896,17.84889],[102.61432,17.92273],[102.60971,17.95411],[102.59234,17.96127],[102.45523,17.97106],[102.11359,18.21532],[101.88485,18.02474],[101.78087,18.07559],[101.72294,17.92867],[101.44667,17.7392],[101.15108,17.47586],[100.96541,17.57926],[101.02185,17.87637],[101.1793,18.0544],[101.19118,18.2125],[101.15108,18.25624],[101.18227,18.34367],[101.06047,18.43247],[101.27585,18.68875],[101.22832,18.73377],[101.25803,18.89545],[101.35606,19.04716],[101.261,19.12717],[101.24911,19.33334],[101.20604,19.35296],[101.21347,19.46223],[101.26991,19.48324],[101.26545,19.59242],[101.08928,19.59748],[100.90302,19.61901],[100.77231,19.48324],[100.64606,19.55884],[100.58219,19.49164],[100.49604,19.53504],[100.398,19.75047],[100.5094,19.87904],[100.58808,20.15791],[100.55218,20.17741],[100.51052,20.14928],[100.47567,20.19133],[100.4537,20.19971],[100.44992,20.23644],[100.41473,20.25625],[100.37439,20.35156],[100.33383,20.4028],[100.25769,20.3992],[100.22076,20.31598],[100.16668,20.2986],[100.1712,20.24324],[100.11785,20.24787],[100.09337,20.26293],[100.09999,20.31614],[100.08404,20.36626],[99.95721,20.46301],[99.91616,20.44986],[99.90499,20.4487],[99.89692,20.44789],[99.89301,20.44311],[99.89168,20.44548],[99.88451,20.44596],[99.88211,20.44488],[99.86383,20.44371],[99.81096,20.33687],[99.68255,20.32077],[99.46008,20.39673],[99.46077,20.36198],[99.5569,20.20676],[99.52943,20.14811],[99.416,20.08614],[99.20328,20.12877],[99.0735,20.10298],[98.98679,19.7419],[98.83661,19.80931],[98.56065,19.67807],[98.51182,19.71303],[98.24884,19.67876],[98.13829,19.78541],[98.03314,19.80941],[98.04364,19.65755],[97.84715,19.55782],[97.88423,19.5041],[97.78769,19.39429],[97.84186,19.29526],[97.78606,19.26769],[97.84024,19.22217],[97.83479,19.09972],[97.73797,19.04261],[97.73654,18.9812],[97.66487,18.9371],[97.73836,18.88478],[97.76752,18.58097],[97.5258,18.4939],[97.36444,18.57138],[97.34522,18.54596],[97.50383,18.26844],[97.56219,18.33885],[97.64116,18.29778],[97.60841,18.23846],[97.73723,17.97912],[97.66794,17.88005],[97.76407,17.71595],[97.91829,17.54504],[98.11185,17.36829],[98.10439,17.33847],[98.34566,17.04822],[98.39441,17.06266],[98.52624,16.89979],[98.49603,16.8446],[98.53833,16.81934],[98.46994,16.73613],[98.50253,16.7139],[98.49713,16.69022],[98.51043,16.70107],[98.51579,16.69433],[98.51472,16.68521],[98.51833,16.676],[98.51113,16.64503],[98.5695,16.62826],[98.57912,16.55983],[98.63817,16.47424],[98.68074,16.27068],[98.84485,16.42354],[98.92656,16.36425],[98.8376,16.11706],[98.69585,16.13353],[98.57019,16.04578],[98.59853,15.87197],[98.541,15.65406],[98.58598,15.46821],[98.56027,15.33471],[98.4866,15.39154],[98.39351,15.34177],[98.41906,15.27103],[98.40522,15.25268],[98.30446,15.30667],[98.22,15.21327],[98.18821,15.13125],[98.24874,14.83013],[98.56762,14.37701],[98.97356,14.04868],[99.16695,13.72621],[99.20617,13.20575],[99.12225,13.19847],[99.10646,13.05804],[99.18748,12.9898],[99.18905,12.84799],[99.29254,12.68921],[99.409,12.60603],[99.47519,12.1353],[99.56445,12.14805],[99.53424,12.02317],[99.64891,11.82699],[99.64108,11.78948],[99.5672,11.62732],[99.47598,11.62434],[99.39485,11.3925],[99.31573,11.32081],[99.32756,11.28545],[99.06938,10.94857],[99.02337,10.97217],[98.99701,10.92962],[99.0069,10.85485],[98.86819,10.78336],[98.78511,10.68351],[98.77275,10.62548],[98.81944,10.52761],[98.7391,10.31488],[98.55174,9.92804],[98.52291,9.92389],[98.47298,9.95782],[98.33094,9.91973],[98.21525,9.56576],[97.63455,9.60854],[97.19814,8.18901]]]}},{"type":"Feature","properties":{"id":"IN-RJ"},"geometry":{"type":"Polygon","coordinates":[[[69.50904,26.74892],[69.88555,26.56836],[70.05584,26.60398],[70.17532,26.55362],[70.17532,26.24118],[70.08193,26.08094],[70.0985,25.93238],[70.2687,25.71156],[70.37444,25.67443],[70.53649,25.68928],[70.60378,25.71898],[70.67382,25.68186],[70.66695,25.39314],[70.89148,25.15064],[70.94002,24.92843],[71.09405,24.69017],[72.18292,24.60894],[72.44144,24.49214],[72.50083,24.40088],[72.69996,24.44527],[72.74116,24.35491],[72.97084,24.34866],[73.00277,24.48777],[73.2328,24.36476],[73.0783,24.20829],[73.2431,24.00319],[73.37425,24.13359],[73.42231,23.92601],[73.35639,23.77591],[73.6211,23.66024],[73.64135,23.4393],[73.83172,23.44749],[73.89953,23.33248],[73.99017,23.33405],[74.24182,23.19244],[74.3201,23.0573],[74.39769,23.11004],[74.53502,23.09868],[74.75063,23.22841],[74.69707,23.27572],[74.53605,23.30095],[74.61124,23.45694],[74.78702,23.54321],[74.93877,23.6376],[74.90924,23.86166],[74.98958,24.03235],[74.88143,24.21816],[74.91371,24.24226],[74.87182,24.27607],[74.75269,24.27576],[74.81002,24.41714],[74.90375,24.46058],[74.86907,24.48996],[74.74616,24.539],[74.80659,24.67322],[74.80522,24.79483],[74.90066,24.65107],[75.03318,24.75431],[74.84607,24.81135],[74.83543,24.98294],[75.03078,24.84563],[75.11352,24.88986],[75.17291,25.05356],[75.35659,25.03397],[75.30715,24.90138],[75.42629,24.8855],[75.18527,24.75057],[75.46989,24.68944],[75.58593,24.72562],[75.62919,24.68632],[75.78712,24.7699],[75.92101,24.51838],[75.88737,24.42401],[75.78987,24.47183],[75.73081,24.38118],[75.82076,24.24727],[75.7418,24.13766],[75.84377,24.10257],[75.70953,23.96617],[75.53203,24.0659],[75.45993,23.9144],[75.6879,23.7602],[75.73596,23.89651],[75.87879,23.88489],[75.98384,23.93574],[75.96324,24.02357],[76.0889,24.08564],[76.18846,24.33364],[76.21215,24.21283],[76.35875,24.25103],[76.91768,24.13735],[76.94343,24.20751],[76.83494,24.3718],[76.82224,24.544],[76.91802,24.54181],[76.94789,24.4509],[77.05879,24.52838],[77.06737,24.64639],[76.95304,24.75805],[76.85211,24.74745],[76.78481,24.83098],[76.94274,24.86401],[76.85554,25.03646],[77.00248,25.07316],[77.27439,25.11482],[77.31044,25.07938],[77.39559,25.11979],[77.41069,25.22109],[77.35507,25.2776],[77.35645,25.42932],[77.27508,25.42746],[77.19646,25.30647],[77.0763,25.33813],[76.93691,25.28071],[76.83734,25.32944],[76.68079,25.34309],[76.59942,25.39304],[76.52046,25.5319],[76.48544,25.71795],[76.59187,25.87745],[76.79408,25.94353],[76.91493,26.09533],[78.21029,26.82407],[78.26042,26.92941],[78.14575,26.94961],[78.04275,26.87001],[77.78045,26.92819],[77.45704,26.74315],[77.41722,26.85776],[77.75848,27.00469],[77.49412,27.08297],[77.67127,27.17341],[77.6493,27.24303],[77.44331,27.3931],[77.28126,27.80689],[77.03235,27.78623],[76.96884,27.65555],[76.88575,27.67075],[76.96317,28.14269],[76.86635,28.22681],[76.80747,28.21562],[76.79958,28.16796],[76.65607,28.09954],[76.66225,28.01774],[76.54792,27.97393],[76.49333,28.15404],[76.27,28.17538],[76.36184,28.12543],[76.30262,28.09999],[76.35618,28.07591],[76.34519,28.02804],[76.31635,28.01925],[76.24958,28.07031],[76.22451,28.04986],[76.17216,28.05546],[76.20614,28.02486],[76.17267,28.01849],[76.22177,27.81296],[75.99449,27.85789],[75.91964,27.93329],[76.04032,28.08561],[75.93097,28.0959],[76.01886,28.16948],[76.09336,28.1498],[75.94333,28.36663],[75.80223,28.40227],[75.56877,28.61225],[75.4656,28.92689],[75.51744,28.9733],[75.51315,29.01504],[75.44448,29.01609],[75.36294,29.14301],[75.41084,29.19982],[75.33462,29.28909],[75.08159,29.22889],[75.05207,29.281],[74.80453,29.39563],[74.59785,29.32472],[74.57382,29.45992],[74.62188,29.52985],[74.57244,29.56449],[74.61158,29.76199],[74.50035,29.74053],[74.46464,29.78106],[74.55116,29.85553],[74.52094,29.94303],[73.88992,29.96921],[73.92219,30.06196],[73.97225,30.19829],[73.80299,30.06969],[73.58665,30.01848],[73.3962,29.94707],[73.28094,29.56646],[73.05886,29.1878],[73.01337,29.16422],[72.94272,29.02487],[72.40402,28.78283],[72.29495,28.66367],[72.20329,28.3869],[71.9244,28.11555],[71.89921,27.96035],[70.79054,27.68423],[70.60927,28.02178],[70.37307,28.01208],[70.12502,27.8057],[70.03136,27.56627],[69.58519,27.18109],[69.50904,26.74892]]]}},{"type":"Feature","properties":{"id":"TM"},"geometry":{"type":"Polygon","coordinates":[[[51.7708,40.29239],[53.89734,37.3464],[54.24565,37.32047],[54.36211,37.34912],[54.58664,37.45809],[54.67247,37.43532],[54.77822,37.51597],[54.81804,37.61285],[54.77684,37.62264],[54.851,37.75739],[55.13412,37.94705],[55.44152,38.08564],[55.76561,38.12238],[55.97847,38.08024],[56.33278,38.08132],[56.32454,38.18502],[56.43303,38.26054],[56.62255,38.24005],[56.73928,38.27887],[57.03453,38.18717],[57.21169,38.28965],[57.37236,38.09321],[57.35042,37.98546],[57.79534,37.89299],[58.21399,37.77281],[58.22999,37.6856],[58.39959,37.63134],[58.47786,37.6433],[58.5479,37.70526],[58.6921,37.64548],[58.9338,37.67374],[59.22905,37.51161],[59.33507,37.53146],[59.39797,37.47892],[59.39385,37.34257],[59.55178,37.13594],[59.74678,37.12499],[60.00768,37.04102],[60.34767,36.63214],[61.14516,36.64644],[61.18187,36.55348],[61.1393,36.38782],[61.22719,36.12759],[61.12007,35.95992],[61.22444,35.92879],[61.26152,35.80749],[61.22719,35.67038],[61.27371,35.61482],[61.58742,35.43803],[61.77693,35.41341],[61.97743,35.4604],[62.05709,35.43803],[62.15871,35.33278],[62.29191,35.25964],[62.29878,35.13312],[62.48006,35.28796],[62.62288,35.22067],[62.74098,35.25432],[62.90853,35.37086],[63.0898,35.43131],[63.12276,35.53196],[63.10079,35.63024],[63.23262,35.67487],[63.10318,35.81782],[63.12276,35.86208],[63.29579,35.85985],[63.53475,35.90881],[63.56496,35.95106],[63.98519,36.03773],[64.05385,36.10433],[64.43288,36.24401],[64.57295,36.34362],[64.62514,36.44311],[64.61141,36.6351],[64.97945,37.21913],[65.51778,37.23881],[65.64263,37.34388],[65.64137,37.45061],[65.72274,37.55438],[66.30993,37.32409],[66.55743,37.35409],[66.52303,37.39827],[66.65761,37.45497],[66.52852,37.58568],[66.53676,37.80084],[66.67684,37.96776],[66.56697,38.0435],[66.41042,38.02403],[66.24013,38.16238],[65.83913,38.25733],[65.55873,38.29052],[64.32576,38.98691],[64.19086,38.95561],[63.70778,39.22349],[63.6913,39.27666],[62.43337,39.98528],[62.34273,40.43206],[62.11751,40.58242],[61.87856,41.12257],[61.4446,41.29407],[61.39732,41.19873],[61.33199,41.14946],[61.22212,41.14946],[61.03261,41.25691],[60.5078,41.21694],[60.06581,41.4363],[60.18117,41.60082],[60.06032,41.76287],[60.08504,41.80997],[60.33223,41.75058],[59.95046,41.97966],[60.0356,42.01028],[60.04659,42.08982],[59.96419,42.1428],[60.00539,42.212],[59.94633,42.27655],[59.4341,42.29738],[59.2955,42.37064],[59.17317,42.52248],[58.93422,42.5407],[58.6266,42.79314],[58.57991,42.64988],[58.27504,42.69632],[58.14321,42.62159],[58.29427,42.56497],[58.51674,42.30348],[58.40688,42.29535],[58.3492,42.43335],[57.99214,42.50021],[57.90975,42.4374],[57.92897,42.24047],[57.84932,42.18555],[57.6296,42.16519],[57.30275,42.14076],[57.03633,41.92043],[56.96218,41.80383],[57.03359,41.41777],[57.13796,41.36625],[57.03423,41.25435],[56.00314,41.32584],[55.45471,41.25609],[54.95182,41.92424],[54.20635,42.38477],[52.97575,42.1308],[52.47884,41.78034],[52.26048,41.69249],[51.7708,40.29239]]]}},{"type":"Feature","properties":{"id":"TL"},"geometry":{"type":"Polygon","coordinates":[[[124.04286,-9.34243],[124.10539,-9.41206],[124.14517,-9.42324],[124.21247,-9.36904],[124.28115,-9.42189],[124.28115,-9.50453],[124.3535,-9.48493],[124.35258,-9.43002],[124.38554,-9.3582],[124.45971,-9.30263],[124.46701,-9.13002],[124.94011,-8.85617],[124.97742,-9.08128],[125.11764,-8.96359],[125.18632,-9.03142],[125.18907,-9.16434],[125.09434,-9.19669],[125.04044,-9.17093],[124.97892,-9.19281],[125.09025,-9.46406],[125.68138,-9.85176],[127.55165,-9.05052],[127.53551,-8.41485],[127.21788,-8.22363],[125.87691,-8.31789],[125.65946,-8.06136],[125.31127,-8.22976],[124.92337,-8.75859],[124.33472,-9.11416],[124.04628,-9.22671],[124.04286,-9.34243]]]}},{"type":"Feature","properties":{"id":"TN"},"geometry":{"type":"Polygon","coordinates":[[[7.52851,34.06493],[7.54088,33.7726],[7.73687,33.42114],[7.83028,33.18851],[8.11433,33.10175],[8.1179,33.05086],[8.31895,32.83483],[8.35999,32.50101],[9.07483,32.07865],[9.55544,30.23971],[9.76848,30.34366],[9.88152,30.34074],[10.29516,30.90337],[10.12239,31.42098],[10.31364,31.72648],[10.48497,31.72956],[10.62788,31.96629],[10.7315,31.97235],[11.04234,32.2145],[11.53898,32.4138],[11.57828,32.48013],[11.46037,32.6307],[11.51549,33.09826],[11.55852,33.1409],[11.56255,33.16754],[11.66543,33.34642],[12.02012,35.25036],[11.2718,37.6713],[7.89009,38.19924],[8.59123,37.14286],[8.64044,36.9401],[8.62972,36.86499],[8.67706,36.8364],[8.57613,36.78062],[8.46537,36.7706],[8.47609,36.66607],[8.16167,36.48817],[8.18936,36.44939],[8.40731,36.42208],[8.2626,35.91733],[8.26472,35.73669],[8.35371,35.66373],[8.36086,35.47774],[8.30329,35.29884],[8.47318,35.23376],[8.3555,35.10007],[8.30727,34.95378],[8.25189,34.92009],[8.29655,34.72798],[8.20482,34.57575],[7.86264,34.3987],[7.81242,34.21841],[7.74207,34.16492],[7.66174,34.20167],[7.52851,34.06493]]]}},{"type":"Feature","properties":{"id":"TR"},"geometry":{"type":"Polygon","coordinates":[[[25.61285,40.17161],[25.94257,39.39358],[26.43357,39.43096],[26.70773,39.0312],[26.61814,38.81372],[26.21136,38.65436],[26.32173,38.48731],[26.24183,38.44695],[26.21136,38.17558],[27.05537,37.9131],[27.16428,37.72343],[26.99377,37.69034],[26.95583,37.64989],[27.14757,37.32],[27.20312,36.94571],[27.45627,36.9008],[27.24613,36.71622],[27.46117,36.53789],[27.89482,36.69898],[27.95037,36.46155],[28.23708,36.56812],[29.30783,36.01033],[29.48192,36.18377],[29.61002,36.1731],[29.61805,36.14179],[29.69611,36.10365],[29.73302,35.92555],[32.82353,35.70297],[35.51152,36.10954],[35.931,35.92109],[35.98499,35.94107],[36.00514,35.94113],[36.01844,35.92403],[35.99829,35.88242],[36.11827,35.85923],[36.13919,35.83692],[36.14029,35.81015],[36.1623,35.80925],[36.17441,35.92076],[36.19973,35.95195],[36.25366,35.96264],[36.27678,35.94839],[36.29769,35.96086],[36.28338,36.00273],[36.30099,36.00985],[36.33956,35.98687],[36.37474,36.01163],[36.39206,36.22088],[36.4617,36.20461],[36.50463,36.2419],[36.6125,36.22592],[36.68672,36.23677],[36.65653,36.33861],[36.6081,36.33772],[36.54206,36.49539],[36.58829,36.58295],[36.57398,36.65186],[36.62681,36.71189],[36.61581,36.74629],[36.66727,36.82901],[36.99557,36.75997],[36.99886,36.74012],[37.04399,36.73483],[37.04619,36.71101],[37.01647,36.69512],[37.02088,36.66422],[37.08279,36.63495],[37.10894,36.6704],[37.16177,36.66069],[37.21988,36.6736],[37.47253,36.63243],[37.49103,36.66904],[37.68048,36.75065],[37.81974,36.76055],[38.21064,36.91842],[38.38859,36.90064],[38.55908,36.84429],[38.74042,36.70629],[39.03217,36.70911],[39.21538,36.66834],[39.81589,36.75538],[40.69136,37.0996],[40.90856,37.13147],[41.21937,37.07665],[41.515,37.08084],[42.00894,37.17209],[42.18225,37.28569],[42.19301,37.31323],[42.2112,37.32491],[42.22257,37.31395],[42.22381,37.30238],[42.20454,37.28715],[42.21548,37.28026],[42.23683,37.2863],[42.26039,37.27017],[42.2824,37.2798],[42.34735,37.22548],[42.32313,37.17814],[42.35724,37.10998],[42.56725,37.14878],[42.78887,37.38615],[42.93705,37.32015],[43.11403,37.37436],[43.30083,37.30629],[43.33508,37.33105],[43.50787,37.24436],[43.56702,37.25675],[43.63085,37.21957],[43.7009,37.23692],[43.8052,37.22825],[43.82699,37.19477],[43.84878,37.22205],[43.90949,37.22453],[44.02002,37.33229],[44.13521,37.32486],[44.2613,37.25055],[44.27998,37.16501],[44.22239,37.15756],[44.18503,37.09551],[44.25975,36.98119],[44.30645,36.97373],[44.35937,37.02843],[44.35315,37.04955],[44.38117,37.05825],[44.42631,37.05825],[44.63179,37.19229],[44.76698,37.16162],[44.78319,37.1431],[44.7868,37.16644],[44.75986,37.21549],[44.81021,37.2915],[44.58449,37.45018],[44.61401,37.60165],[44.56887,37.6429],[44.62096,37.71985],[44.55498,37.783],[44.45948,37.77065],[44.3883,37.85433],[44.22509,37.88859],[44.42476,38.25763],[44.50115,38.33939],[44.44386,38.38295],[44.38309,38.36117],[44.3119,38.37887],[44.3207,38.49799],[44.32058,38.62752],[44.28065,38.6465],[44.26155,38.71427],[44.30322,38.81581],[44.18863,38.93881],[44.20946,39.13975],[44.1043,39.19842],[44.03667,39.39223],[44.22452,39.4169],[44.29818,39.378],[44.37921,39.4131],[44.42832,39.4131],[44.41849,39.56659],[44.48111,39.61579],[44.47298,39.68788],[44.6137,39.78393],[44.65422,39.72163],[44.71806,39.71124],[44.81043,39.62677],[44.80977,39.65768],[44.75779,39.7148],[44.61845,39.8281],[44.46635,39.97733],[44.26973,40.04866],[44.1778,40.02845],[44.1057,40.03555],[43.92307,40.01787],[43.65688,40.11199],[43.65221,40.14889],[43.71136,40.16673],[43.59928,40.34019],[43.60862,40.43267],[43.54791,40.47413],[43.63664,40.54159],[43.7425,40.66805],[43.74872,40.7365],[43.67712,40.84846],[43.67712,40.93084],[43.58683,40.98961],[43.47319,41.02251],[43.44984,41.0988],[43.4717,41.12611],[43.44973,41.17666],[43.36118,41.2028],[43.23096,41.17536],[43.1945,41.25242],[43.13373,41.25503],[43.21707,41.30331],[43.02956,41.37891],[42.8785,41.50516],[42.84899,41.47265],[42.78995,41.50126],[42.84471,41.58912],[42.72794,41.59714],[42.59202,41.58183],[42.51772,41.43606],[42.26387,41.49346],[41.95134,41.52466],[41.81939,41.43621],[41.7124,41.47417],[41.7148,41.4932],[41.54366,41.52185],[40.89217,41.72528],[34.8305,42.4581],[28.32297,41.98371],[28.02971,41.98066],[27.91479,41.97902],[27.83492,41.99709],[27.81235,41.94803],[27.69949,41.97515],[27.55191,41.90928],[27.52379,41.93756],[27.45478,41.96591],[27.27411,42.10409],[27.22376,42.10152],[27.19251,42.06028],[27.08486,42.08735],[27.03277,42.0809],[26.95638,42.00741],[26.79143,41.97386],[26.62996,41.97644],[26.56051,41.92995],[26.57961,41.90024],[26.53968,41.82653],[26.36952,41.82265],[26.33589,41.76802],[26.32952,41.73637],[26.35957,41.71149],[26.47958,41.67037],[26.5209,41.62592],[26.59196,41.60491],[26.59742,41.48058],[26.61767,41.42281],[26.62997,41.34613],[26.5837,41.32131],[26.5209,41.33993],[26.39861,41.25053],[26.32259,41.24929],[26.31928,41.07386],[26.3606,41.02027],[26.33297,40.98388],[26.35894,40.94292],[26.32259,40.94042],[26.28623,40.93005],[26.29441,40.89119],[26.26169,40.9168],[26.20856,40.86048],[26.21351,40.83298],[26.15685,40.80709],[26.12854,40.77339],[26.12495,40.74283],[26.08638,40.73214],[26.0754,40.72772],[26.03489,40.73051],[25.94795,40.72797],[26.04292,40.3958],[25.61285,40.17161]]]}},{"type":"Feature","properties":{"id":"TW"},"geometry":{"type":"Polygon","coordinates":[[[118.09488,24.38193],[118.179,24.33015],[118.41371,24.06775],[120.69238,21.52331],[121.8109,21.77688],[121.75634,23.51406],[122.32924,25.44232],[122.26612,25.98197],[120.49232,25.22863],[118.56434,24.49266],[118.42453,24.54644],[118.35291,24.51645],[118.28244,24.51231],[118.09488,24.38193]]]}},{"type":"Feature","properties":{"id":"TZ"},"geometry":{"type":"Polygon","coordinates":[[[29.43673,-4.44845],[29.52552,-6.2731],[30.2567,-7.14121],[30.79243,-8.27382],[31.00796,-8.58615],[31.37533,-8.60769],[31.57147,-8.70619],[31.57147,-8.81388],[31.71158,-8.91386],[31.81587,-8.88618],[31.94663,-8.93846],[31.94196,-9.02303],[31.98866,-9.07069],[32.08206,-9.04609],[32.16146,-9.05993],[32.25486,-9.13371],[32.43543,-9.11988],[32.49147,-9.14754],[32.53661,-9.24281],[32.75611,-9.28583],[32.76233,-9.31963],[32.95389,-9.40138],[32.99397,-9.36712],[33.14925,-9.49322],[33.31581,-9.48554],[33.48052,-9.62442],[33.76677,-9.58516],[33.93298,-9.71647],[33.9638,-9.62206],[33.95829,-9.54066],[34.03865,-9.49398],[34.54499,-10.0678],[34.51911,-10.12279],[34.57581,-10.56271],[34.65946,-10.6828],[34.67047,-10.93796],[34.61161,-11.01611],[34.63305,-11.11731],[34.79375,-11.32245],[34.91153,-11.39799],[34.96296,-11.57354],[35.63599,-11.55927],[35.82767,-11.41081],[36.19094,-11.57593],[36.19094,-11.70008],[36.62068,-11.72884],[36.80309,-11.56836],[37.3936,-11.68949],[37.76614,-11.53352],[37.8388,-11.3123],[37.93618,-11.26228],[38.21598,-11.27289],[38.47258,-11.4199],[38.88996,-11.16978],[39.24395,-11.17433],[39.58249,-10.96043],[40.00295,-10.80255],[40.44265,-10.4618],[40.74206,-10.25691],[40.14328,-4.64201],[39.62121,-4.68136],[39.44306,-4.93877],[39.21631,-4.67835],[37.81321,-3.69179],[37.75036,-3.54243],[37.63099,-3.50723],[37.5903,-3.42735],[37.71745,-3.304],[37.67199,-3.06222],[34.0824,-1.02264],[34.03084,-1.05101],[34.02286,-1.00779],[33.93107,-0.99298],[30.80408,-0.99911],[30.76635,-0.9852],[30.70631,-1.01175],[30.64166,-1.06601],[30.47194,-1.0555],[30.45116,-1.10641],[30.50889,-1.16412],[30.57123,-1.33264],[30.71974,-1.43244],[30.84079,-1.64652],[30.80802,-1.91477],[30.89303,-2.08223],[30.83915,-2.35795],[30.54501,-2.41404],[30.41789,-2.66266],[30.52747,-2.65841],[30.40662,-2.86151],[30.4987,-2.9573],[30.57926,-2.89791],[30.6675,-2.98987],[30.83823,-2.97837],[30.84165,-3.25152],[30.45915,-3.56532],[30.22042,-4.01738],[30.03323,-4.26631],[29.88172,-4.35743],[29.82885,-4.36153],[29.77289,-4.41733],[29.75109,-4.45836],[29.63827,-4.44681],[29.43673,-4.44845]]]}},{"type":"Feature","properties":{"id":"UG"},"geometry":{"type":"Polygon","coordinates":[[[29.58388,-0.89821],[29.59061,-1.39016],[29.82657,-1.31187],[29.912,-1.48269],[30.16369,-1.34303],[30.35212,-1.06896],[30.47194,-1.0555],[30.64166,-1.06601],[30.70631,-1.01175],[30.76635,-0.9852],[30.80408,-0.99911],[33.93107,-0.99298],[33.9264,-0.54188],[33.98449,-0.13079],[33.90936,0.10581],[34.10067,0.36372],[34.08727,0.44713],[34.11408,0.48884],[34.13493,0.58118],[34.20196,0.62289],[34.27345,0.63182],[34.31516,0.75693],[34.40041,0.80266],[34.43349,0.85254],[34.52369,1.10692],[34.57427,1.09868],[34.58029,1.14712],[34.67562,1.21265],[34.80223,1.22754],[34.82606,1.26626],[34.82606,1.30944],[34.7918,1.36752],[34.87819,1.5596],[34.92734,1.56109],[34.9899,1.6668],[34.98692,1.97348],[34.90947,2.42447],[34.95267,2.47209],[34.77244,2.70272],[34.78137,2.76223],[34.73967,2.85447],[34.65774,2.8753],[34.60114,2.93034],[34.56242,3.11478],[34.45815,3.18319],[34.40006,3.37949],[34.41794,3.44342],[34.39112,3.48802],[34.44922,3.51627],[34.45815,3.67385],[34.15429,3.80464],[34.06046,4.15235],[33.9873,4.23316],[33.51264,3.75068],[33.18356,3.77812],[33.02852,3.89296],[32.89746,3.81339],[32.72021,3.77327],[32.41337,3.748],[32.20782,3.6053],[32.19888,3.50867],[32.08866,3.53543],[32.08491,3.56287],[32.05187,3.589],[31.95907,3.57408],[31.96205,3.6499],[31.86821,3.78664],[31.81459,3.82083],[31.72075,3.74354],[31.50776,3.63652],[31.50478,3.67814],[31.29476,3.8015],[31.16666,3.79853],[30.97601,3.693],[30.85153,3.48867],[30.94081,3.50847],[30.93486,3.40737],[30.84251,3.26908],[30.77101,3.04897],[30.8574,2.9508],[30.8857,2.83923],[30.75612,2.5863],[30.74271,2.43601],[30.83059,2.42559],[30.91102,2.33332],[30.96911,2.41071],[31.06593,2.35862],[31.07934,2.30207],[31.12104,2.27676],[31.1985,2.29462],[31.20148,2.2217],[31.28042,2.17853],[31.30127,2.11006],[30.48503,1.21675],[30.24671,1.14974],[30.22139,0.99635],[30.1484,0.89805],[29.98307,0.84295],[29.95477,0.64486],[29.97413,0.52124],[29.87284,0.39166],[29.81922,0.16824],[29.77454,0.16675],[29.7224,0.07291],[29.72687,-0.08051],[29.65091,-0.46777],[29.67474,-0.47969],[29.67176,-0.55714],[29.62708,-0.71055],[29.63006,-0.8997],[29.58388,-0.89821]]]}},{"type":"Feature","properties":{"id":"UA"},"geometry":{"type":"Polygon","coordinates":[[[22.14689,48.4005],[22.2083,48.42534],[22.38133,48.23726],[22.49806,48.25189],[22.59007,48.15121],[22.58733,48.10813],[22.66835,48.09162],[22.73427,48.12005],[22.81804,48.11363],[22.87847,48.04665],[22.84276,47.98602],[22.89849,47.95851],[22.94301,47.96672],[22.92241,48.02002],[23.0158,47.99338],[23.08858,48.00716],[23.1133,48.08061],[23.15999,48.12188],[23.27397,48.08245],[23.33577,48.0237],[23.4979,47.96858],[23.52803,48.01818],[23.5653,48.00499],[23.63894,48.00293],[23.66262,47.98786],[23.75188,47.99705],[23.80904,47.98142],[23.8602,47.9329],[23.89352,47.94512],[23.94192,47.94868],[23.96337,47.96672],[23.98553,47.96076],[24.00801,47.968],[24.02999,47.95087],[24.06466,47.95317],[24.11281,47.91487],[24.22566,47.90231],[24.34926,47.9244],[24.43578,47.97131],[24.61994,47.95062],[24.70632,47.84428],[24.81893,47.82031],[24.88896,47.7234],[25.11144,47.75203],[25.23778,47.89403],[25.63878,47.94924],[25.77723,47.93919],[26.05901,47.9897],[26.17711,47.99246],[26.33504,48.18418],[26.55202,48.22445],[26.62823,48.25804],[26.6839,48.35828],[26.79239,48.29071],[26.82809,48.31629],[26.71274,48.40388],[26.85556,48.41095],[26.93384,48.36558],[27.03821,48.37653],[27.0231,48.42485],[27.08078,48.43214],[27.13434,48.37288],[27.27855,48.37534],[27.32159,48.4434],[27.37604,48.44398],[27.37741,48.41026],[27.44333,48.41209],[27.46942,48.454],[27.5889,48.49224],[27.59027,48.46311],[27.6658,48.44034],[27.74422,48.45926],[27.79225,48.44244],[27.81902,48.41874],[27.87533,48.4037],[27.88391,48.36699],[27.95883,48.32368],[28.04527,48.32661],[28.09873,48.3124],[28.07504,48.23494],[28.17666,48.25963],[28.19314,48.20749],[28.2856,48.23202],[28.32508,48.23384],[28.35519,48.24957],[28.36996,48.20543],[28.34912,48.1787],[28.30586,48.1597],[28.30609,48.14018],[28.34009,48.13147],[28.38712,48.17567],[28.43701,48.15832],[28.42454,48.12047],[28.48428,48.0737],[28.53921,48.17453],[28.69896,48.13106],[28.85232,48.12506],[28.8414,48.03392],[28.9306,47.96255],[29.1723,47.99013],[29.19839,47.89261],[29.27804,47.88893],[29.20663,47.80367],[29.27255,47.79953],[29.22242,47.73607],[29.22414,47.60012],[29.11743,47.55001],[29.18603,47.43387],[29.3261,47.44664],[29.39889,47.30179],[29.47854,47.30366],[29.48678,47.36043],[29.5733,47.36508],[29.59665,47.25521],[29.54996,47.24962],[29.57696,47.13581],[29.49732,47.12878],[29.53044,47.07851],[29.61038,47.09932],[29.62137,47.05069],[29.57056,46.94766],[29.72986,46.92234],[29.75458,46.8604],[29.87405,46.88199],[29.98814,46.82358],[29.94522,46.80055],[29.9743,46.75325],[29.94409,46.56002],[29.88916,46.54302],[30.02511,46.45132],[30.16794,46.40967],[30.09103,46.38694],[29.94114,46.40114],[29.88329,46.35851],[29.74496,46.45605],[29.66359,46.4215],[29.6763,46.36041],[29.5939,46.35472],[29.49914,46.45889],[29.35357,46.49505],[29.24886,46.37912],[29.23547,46.55435],[29.02409,46.49582],[29.01241,46.46177],[28.9306,46.45699],[29.004,46.31495],[28.98478,46.31803],[28.94953,46.25852],[29.06656,46.19716],[28.94643,46.09176],[29.00613,46.04962],[28.98004,46.00385],[28.74383,45.96664],[28.78503,45.83475],[28.69852,45.81753],[28.70401,45.78019],[28.52823,45.73803],[28.47879,45.66994],[28.51587,45.6613],[28.54196,45.58062],[28.49252,45.56716],[28.51449,45.49982],[28.43072,45.48538],[28.41836,45.51715],[28.30201,45.54744],[28.21139,45.46895],[28.28504,45.43907],[28.34554,45.32102],[28.5735,45.24759],[28.71358,45.22631],[28.78911,45.24179],[28.81383,45.3384],[28.94292,45.28045],[28.96077,45.33164],[29.24779,45.43388],[29.42632,45.44545],[29.59798,45.38857],[29.68175,45.26885],[29.65428,45.25629],[29.69272,45.19227],[30.04414,45.08461],[31.62627,45.50633],[33.54017,46.0123],[33.59087,46.06013],[33.57318,46.10317],[33.61467,46.13561],[33.63854,46.14147],[33.61517,46.22615],[33.646,46.23028],[33.74047,46.18555],[33.79715,46.20482],[33.85234,46.19863],[33.91549,46.15938],[34.05272,46.10838],[34.07311,46.11769],[34.12929,46.10494],[34.181,46.06804],[34.25111,46.0532],[34.33912,46.06114],[34.41221,46.00245],[34.44155,45.95995],[34.48729,45.94267],[34.52011,45.95097],[34.55889,45.99347],[34.60861,45.99347],[34.66679,45.97136],[34.75479,45.90705],[34.80153,45.90047],[34.79905,45.81009],[34.96015,45.75634],[35.23066,45.79231],[37.62608,46.82615],[38.12112,46.86078],[38.3384,46.98085],[38.22955,47.12069],[38.23049,47.2324],[38.32112,47.2585],[38.33074,47.30508],[38.22225,47.30788],[38.28954,47.39255],[38.28679,47.53552],[38.35062,47.61631],[38.76379,47.69346],[38.79628,47.81109],[38.87979,47.87719],[39.73935,47.82876],[39.82213,47.96396],[39.77544,48.04206],[39.88256,48.04482],[39.83724,48.06501],[39.94847,48.22811],[40.00752,48.22445],[39.99241,48.31768],[39.97325,48.31399],[39.9693,48.29904],[39.95248,48.29972],[39.91465,48.26743],[39.90041,48.3049],[39.84273,48.30947],[39.84136,48.33321],[39.94847,48.35055],[39.88794,48.44226],[39.86196,48.46633],[39.84548,48.57821],[39.79764,48.58668],[39.67226,48.59368],[39.71765,48.68673],[39.73104,48.7325],[39.79466,48.83739],[39.97182,48.79398],[40.08168,48.87443],[40.03636,48.91957],[39.98967,48.86901],[39.78368,48.91596],[39.74874,48.98675],[39.72649,48.9754],[39.71353,48.98959],[39.6683,48.99454],[39.6836,49.05121],[39.93437,49.05709],[40.01988,49.1761],[40.22176,49.25683],[40.18331,49.34996],[40.14912,49.37681],[40.1141,49.38798],[40.03087,49.45452],[40.03636,49.52321],[40.16683,49.56865],[40.13249,49.61672],[39.84548,49.56064],[39.65047,49.61761],[39.59142,49.73758],[39.44496,49.76067],[39.27968,49.75976],[39.1808,49.88911],[38.9391,49.79524],[38.90477,49.86787],[38.73311,49.90238],[38.68677,50.00904],[38.65688,49.97176],[38.35408,50.00664],[38.32524,50.08866],[38.18517,50.08161],[38.21675,49.98104],[38.02999,49.90592],[38.02999,49.94482],[37.90776,50.04194],[37.79515,50.08425],[37.75807,50.07896],[37.61113,50.21976],[37.62879,50.24481],[37.62486,50.29966],[37.47243,50.36277],[37.48204,50.46079],[37.08468,50.34935],[36.91762,50.34963],[36.69377,50.26982],[36.64571,50.218],[36.56655,50.2413],[36.58371,50.28563],[36.47817,50.31457],[36.30101,50.29088],[36.20763,50.3943],[36.06893,50.45205],[35.8926,50.43829],[35.80388,50.41356],[35.73659,50.35489],[35.61711,50.35707],[35.58003,50.45117],[35.47463,50.49247],[35.39464,50.64751],[35.48116,50.66405],[35.47704,50.77274],[35.41367,50.80227],[35.39307,50.92145],[35.32598,50.94524],[35.40837,51.04119],[35.31774,51.08434],[35.20375,51.04723],[35.12685,51.16191],[35.14058,51.23162],[34.97304,51.2342],[34.82472,51.17483],[34.6874,51.18],[34.6613,51.25053],[34.38802,51.2746],[34.31661,51.23936],[34.23009,51.26429],[34.33446,51.363],[34.22048,51.4187],[34.30562,51.5205],[34.17599,51.63253],[34.07765,51.67065],[34.42922,51.72852],[34.41136,51.82793],[34.09413,52.00835],[34.11199,52.14087],[34.05239,52.20132],[33.78789,52.37204],[33.55718,52.30324],[33.48027,52.31499],[33.51323,52.35779],[33.18913,52.3754],[32.89937,52.2461],[32.85405,52.27888],[32.69475,52.25535],[32.54781,52.32423],[32.3528,52.32842],[32.38988,52.24946],[32.33083,52.23685],[32.34044,52.1434],[32.2777,52.10266],[32.23331,52.08085],[32.08813,52.03319],[31.92159,52.05144],[31.96141,52.08015],[31.85018,52.11305],[31.81722,52.09955],[31.7822,52.11406],[31.38326,52.12991],[31.25142,52.04131],[31.13332,52.1004],[30.95589,52.07775],[30.90897,52.00699],[30.76443,51.89739],[30.68804,51.82806],[30.51946,51.59649],[30.64992,51.35014],[30.56203,51.25655],[30.36153,51.33984],[30.34642,51.42555],[30.17888,51.51025],[29.77376,51.4461],[29.7408,51.53417],[29.54372,51.48372],[29.49773,51.39814],[29.42357,51.4187],[29.32881,51.37843],[29.25191,51.49828],[29.25603,51.57089],[29.20659,51.56918],[29.16402,51.64679],[29.1187,51.65872],[28.99098,51.56833],[28.95528,51.59222],[28.81795,51.55552],[28.76027,51.48802],[28.78224,51.45294],[28.75615,51.41442],[28.73143,51.46236],[28.69161,51.44695],[28.64429,51.5664],[28.47051,51.59734],[28.37592,51.54505],[28.23452,51.66988],[28.10658,51.57857],[27.95827,51.56065],[27.91844,51.61952],[27.85253,51.62293],[27.76052,51.47604],[27.67125,51.50854],[27.71932,51.60672],[27.55727,51.63486],[27.51058,51.5854],[27.47212,51.61184],[27.24828,51.60161],[27.26613,51.65957],[27.20948,51.66713],[27.20602,51.77291],[26.99422,51.76933],[26.9489,51.73788],[26.80043,51.75777],[26.69759,51.82284],[26.46962,51.80501],[26.39367,51.87315],[26.19084,51.86781],[26.00408,51.92967],[25.83217,51.92587],[25.80574,51.94556],[25.73673,51.91973],[25.46163,51.92205],[25.20228,51.97143],[24.98784,51.91273],[24.37123,51.88222],[24.29021,51.80841],[24.3163,51.75063],[24.13075,51.66979],[23.99907,51.58369],[23.8741,51.59734],[23.91118,51.63316],[23.7766,51.66809],[23.60906,51.62122],[23.6736,51.50255],[23.62751,51.50512],[23.69905,51.40871],[23.63858,51.32182],[23.80678,51.18405],[23.90376,51.07697],[23.92217,51.00836],[24.04576,50.90196],[24.14524,50.86128],[24.0952,50.83262],[23.99254,50.83847],[23.95925,50.79271],[24.0595,50.71625],[24.0996,50.60752],[24.07048,50.5071],[24.03668,50.44507],[23.99563,50.41289],[23.79445,50.40481],[23.71382,50.38248],[23.67635,50.33385],[23.28221,50.0957],[22.99329,49.84249],[22.83179,49.69875],[22.80261,49.69098],[22.78304,49.65543],[22.64534,49.53094],[22.69444,49.49378],[22.748,49.32759],[22.72009,49.20288],[22.86336,49.10513],[22.89122,49.00725],[22.56155,49.08865],[22.54338,49.01424],[22.48296,48.99172],[22.42934,48.92857],[22.34151,48.68893],[22.21379,48.6218],[22.16023,48.56548],[22.14689,48.4005]]]}},{"type":"Feature","properties":{"id":"UY"},"geometry":{"type":"Polygon","coordinates":[[[-58.44442,-33.84033],[-58.34425,-34.15035],[-57.83001,-34.69099],[-55.71154,-35.78518],[-53.54511,-34.54062],[-53.18243,-33.86894],[-53.37138,-33.74313],[-53.39593,-33.75169],[-53.44031,-33.69344],[-53.52794,-33.68908],[-53.53459,-33.16843],[-53.1111,-32.71147],[-53.37671,-32.57005],[-53.39572,-32.58596],[-53.76024,-32.0751],[-54.17384,-31.86168],[-55.50821,-30.91349],[-55.50841,-30.9027],[-55.51862,-30.89828],[-55.52712,-30.89997],[-55.53276,-30.90218],[-55.53431,-30.89714],[-55.54572,-30.89051],[-55.55218,-30.88193],[-55.55373,-30.8732],[-55.5634,-30.8686],[-55.58866,-30.84117],[-55.87388,-31.05053],[-56.4619,-30.38457],[-56.4795,-30.3899],[-56.49267,-30.39471],[-56.90236,-30.02578],[-57.22502,-30.26121],[-57.65132,-30.19229],[-57.61478,-30.25165],[-57.64859,-30.35095],[-57.89115,-30.49572],[-57.8024,-30.77193],[-57.89476,-30.95994],[-57.86729,-31.06352],[-57.9908,-31.34924],[-57.98127,-31.3872],[-58.07569,-31.44916],[-58.0023,-31.53084],[-58.00076,-31.65016],[-58.20252,-31.86966],[-58.10036,-32.25338],[-58.22362,-32.52416],[-58.1224,-32.98842],[-58.40475,-33.11777],[-58.44442,-33.84033]]]}},{"type":"Feature","properties":{"id":"IN-UT"},"geometry":{"type":"Polygon","coordinates":[[[77.5542,30.40278],[77.92945,30.24661],[77.70629,29.8722],[77.80792,29.67075],[77.94181,29.71489],[77.98507,29.5418],[78.33732,29.79536],[78.49044,29.73874],[78.52683,29.6248],[78.91067,29.45335],[78.71429,29.32053],[79.00268,29.12127],[79.0686,29.15276],[79.29416,28.95858],[79.40711,28.92854],[79.41089,28.85339],[79.66529,28.85369],[79.79026,28.88736],[79.96673,28.70233],[80.06957,28.82763],[80.05743,28.91479],[80.18085,29.13649],[80.23178,29.11626],[80.26602,29.13938],[80.24112,29.21414],[80.28626,29.20327],[80.31428,29.30784],[80.24322,29.44299],[80.37939,29.57098],[80.41858,29.63581],[80.38428,29.68513],[80.36803,29.73865],[80.41554,29.79451],[80.43458,29.80466],[80.48997,29.79566],[80.56247,29.86661],[80.56957,29.88176],[80.60226,29.95732],[80.67076,29.95732],[80.8778,30.13384],[80.93695,30.18229],[81.03953,30.20059],[80.83343,30.32023],[80.54504,30.44936],[80.20721,30.58541],[79.93255,30.88288],[79.59884,30.93943],[79.22805,31.34963],[79.14016,31.43403],[79.01931,31.42817],[78.77898,31.31209],[78.99581,31.10821],[78.292,31.28676],[77.80174,31.05822],[77.69016,30.76927],[77.76946,30.63111],[77.7238,30.59359],[77.80757,30.52086],[77.5542,30.40278]]]}},{"type":"Feature","properties":{"id":"VA"},"geometry":{"type":"Polygon","coordinates":[[[12.44582,41.90194],[12.44834,41.90095],[12.45181,41.90056],[12.45446,41.90028],[12.45435,41.90143],[12.45626,41.90172],[12.45691,41.90125],[12.4577,41.90115],[12.45834,41.90174],[12.45826,41.90281],[12.45755,41.9033],[12.45762,41.9058],[12.45561,41.90629],[12.45543,41.90738],[12.45091,41.90625],[12.44984,41.90545],[12.44815,41.90326],[12.44582,41.90194]]]}},{"type":"Feature","properties":{"id":"VE"},"geometry":{"type":"Polygon","coordinates":[[[-73.36905,9.16636],[-73.02119,9.27584],[-72.94052,9.10663],[-72.77415,9.10165],[-72.65474,8.61428],[-72.4042,8.36513],[-72.36987,8.19976],[-72.35163,8.01163],[-72.39137,8.03534],[-72.47213,7.96106],[-72.48801,7.94329],[-72.48183,7.92909],[-72.47042,7.92306],[-72.45806,7.91141],[-72.46183,7.90682],[-72.44454,7.86031],[-72.46763,7.79518],[-72.47827,7.65604],[-72.45321,7.57232],[-72.47415,7.48928],[-72.43132,7.40034],[-72.19437,7.37034],[-72.04895,7.03837],[-71.82441,7.04314],[-71.44118,7.02116],[-71.42212,7.03854],[-71.37234,7.01588],[-71.03941,6.98163],[-70.7596,7.09799],[-70.10716,6.96516],[-69.41843,6.1072],[-67.60654,6.2891],[-67.4625,6.20625],[-67.43513,5.98835],[-67.58558,5.84537],[-67.63914,5.64963],[-67.59141,5.5369],[-67.83341,5.31104],[-67.85358,4.53249],[-67.62671,3.74303],[-67.50067,3.75812],[-67.30945,3.38393],[-67.85862,2.86727],[-67.85862,2.79173],[-67.65696,2.81691],[-67.21967,2.35778],[-66.85795,1.22998],[-66.28507,0.74585],[-65.6727,1.01353],[-65.50158,0.92086],[-65.57288,0.62856],[-65.11657,1.12046],[-64.38932,1.5125],[-64.34654,1.35569],[-64.08274,1.64792],[-64.06135,1.94722],[-63.39827,2.16098],[-63.39114,2.4317],[-64.0257,2.48156],[-64.02908,2.79797],[-64.48379,3.7879],[-64.84028,4.24665],[-64.72977,4.28931],[-64.57648,4.12576],[-64.14512,4.12932],[-63.99183,3.90172],[-63.86082,3.94796],[-63.70218,3.91417],[-63.67099,4.01731],[-63.50611,3.83592],[-63.42233,3.89995],[-63.4464,3.9693],[-63.21111,3.96219],[-62.98296,3.59935],[-62.7655,3.73099],[-62.74411,4.03331],[-62.57656,4.04754],[-62.44822,4.18621],[-62.13094,4.08309],[-61.54629,4.2822],[-61.48569,4.43149],[-61.29675,4.44216],[-61.31457,4.54167],[-61.15703,4.49839],[-60.98303,4.54167],[-60.86539,4.70512],[-60.5802,4.94312],[-60.73204,5.20931],[-61.4041,5.95304],[-61.15058,6.19558],[-61.20762,6.58174],[-61.13632,6.70922],[-60.54873,6.8631],[-60.39419,6.94847],[-60.28074,7.1162],[-60.44116,7.20817],[-60.54098,7.14804],[-60.63367,7.25061],[-60.59802,7.33194],[-60.71923,7.55817],[-60.64793,7.56877],[-60.51959,7.83373],[-60.38056,7.8302],[-60.02407,8.04557],[-59.97059,8.20791],[-59.83156,8.23261],[-59.80661,8.28906],[-59.85562,8.35213],[-59.98508,8.53046],[-59.54058,8.6862],[-60.89962,9.81445],[-62.08693,10.04435],[-61.62505,11.18974],[-63.73917,11.92623],[-63.25348,15.97077],[-63.85239,16.0256],[-65.4181,12.36848],[-67.89186,12.4116],[-68.01417,11.77722],[-68.33524,11.78151],[-68.99639,11.79035],[-69.4514,12.18025],[-70.24399,12.38063],[-70.34259,12.92535],[-71.19849,12.65801],[-70.92579,11.96275],[-71.3275,11.85],[-71.9675,11.65536],[-72.24983,11.14138],[-72.4767,11.1117],[-72.88002,10.44309],[-72.98085,9.85253],[-73.36905,9.16636]]]}},{"type":"Feature","properties":{"id":"VN"},"geometry":{"type":"Polygon","coordinates":[[[102.14099,22.40092],[102.18712,22.30403],[102.51734,22.02676],[102.49092,21.99002],[102.62301,21.91447],[102.67145,21.65894],[102.74189,21.66713],[102.82115,21.73667],[102.81894,21.83888],[102.85637,21.84501],[102.86077,21.71213],[102.97965,21.74076],[102.98846,21.58936],[102.86297,21.4255],[102.94223,21.46034],[102.88939,21.3107],[102.80794,21.25736],[102.89825,21.24707],[102.97745,21.05821],[103.03469,21.05821],[103.12055,20.89994],[103.21497,20.89832],[103.38032,20.79501],[103.45737,20.82382],[103.68633,20.66324],[103.73478,20.6669],[103.82282,20.8732],[103.98024,20.91531],[104.11121,20.96779],[104.27412,20.91433],[104.63957,20.6653],[104.38199,20.47155],[104.40621,20.3849],[104.47886,20.37459],[104.66158,20.47774],[104.72102,20.40554],[104.62195,20.36633],[104.61315,20.24452],[104.86852,20.14121],[104.91695,20.15567],[104.9874,20.09573],[104.8465,19.91783],[104.8355,19.80395],[104.68359,19.72729],[104.64837,19.62365],[104.53169,19.61743],[104.41281,19.70035],[104.23229,19.70242],[104.06498,19.66926],[104.05617,19.61743],[104.10832,19.51575],[104.06058,19.43484],[103.87125,19.31854],[104.5361,18.97747],[104.64617,18.85668],[105.12829,18.70453],[105.19654,18.64196],[105.1327,18.58355],[105.10408,18.43533],[105.15942,18.38691],[105.38366,18.15315],[105.46292,18.22008],[105.64784,17.96687],[105.60381,17.89356],[105.76612,17.67147],[105.85744,17.63221],[106.09019,17.36399],[106.18991,17.28227],[106.24444,17.24714],[106.29287,17.3018],[106.31929,17.20509],[106.43597,17.01362],[106.50862,16.9673],[106.55045,17.0031],[106.54824,16.92729],[106.51963,16.92097],[106.52183,16.87884],[106.55265,16.86831],[106.55485,16.68704],[106.59013,16.62259],[106.58267,16.6012],[106.61477,16.60713],[106.66052,16.56892],[106.65832,16.47816],[106.74418,16.41904],[106.84104,16.55415],[106.88727,16.52671],[106.88067,16.43594],[106.96638,16.34938],[106.97385,16.30204],[107.02597,16.31132],[107.09091,16.3092],[107.15035,16.26271],[107.14595,16.17816],[107.25822,16.13587],[107.33968,16.05549],[107.44975,16.08511],[107.46296,16.01106],[107.39471,15.88829],[107.34188,15.89464],[107.21419,15.83747],[107.21859,15.74638],[107.27143,15.71459],[107.27583,15.62769],[107.34408,15.62345],[107.3815,15.49832],[107.50699,15.48771],[107.53341,15.40496],[107.62367,15.42193],[107.60605,15.37524],[107.62587,15.2266],[107.58844,15.20111],[107.61926,15.13949],[107.61486,15.0566],[107.46516,15.00982],[107.48277,14.93751],[107.59285,14.87795],[107.51579,14.79282],[107.54361,14.69092],[107.55371,14.628],[107.52102,14.59034],[107.52569,14.54665],[107.48521,14.40346],[107.44941,14.41552],[107.39493,14.32655],[107.40427,14.24509],[107.33577,14.11832],[107.37158,14.07906],[107.35757,14.02319],[107.38247,13.99147],[107.44318,13.99751],[107.46498,13.91593],[107.45252,13.78897],[107.53503,13.73908],[107.61909,13.52577],[107.62843,13.3668],[107.49144,13.01215],[107.49611,12.88926],[107.55993,12.7982],[107.5755,12.52177],[107.55059,12.36824],[107.4463,12.29373],[107.42917,12.24657],[107.34511,12.33327],[107.15831,12.27547],[106.99953,12.08983],[106.92325,12.06548],[106.79405,12.0807],[106.70687,11.96956],[106.4111,11.97413],[106.4687,11.86751],[106.44068,11.86294],[106.44535,11.8279],[106.41577,11.76999],[106.45158,11.68616],[106.44691,11.66787],[106.37219,11.69836],[106.30525,11.67549],[106.26478,11.72122],[106.18539,11.75171],[106.13158,11.73283],[106.06708,11.77761],[106.02038,11.77457],[106.00792,11.7197],[105.95188,11.63738],[105.88962,11.67854],[105.8507,11.66635],[105.80867,11.60536],[105.81645,11.56876],[105.87328,11.55953],[105.88962,11.43605],[105.86782,11.28343],[106.10444,11.07879],[106.1527,11.10476],[106.1757,11.07301],[106.20095,10.97795],[106.14301,10.98176],[106.18539,10.79451],[106.06708,10.8098],[105.94535,10.9168],[105.93403,10.83853],[105.84603,10.85873],[105.86376,10.89839],[105.77751,11.03671],[105.50045,10.94586],[105.42884,10.96878],[105.34011,10.86179],[105.11449,10.96332],[105.08326,10.95656],[105.02722,10.89236],[105.09571,10.72722],[104.95094,10.64003],[104.87933,10.52833],[104.59018,10.53073],[104.49869,10.4057],[104.47963,10.43046],[104.43778,10.42386],[103.99198,10.48391],[102.47649,9.66162],[104.81582,8.03101],[109.55486,8.10026],[110.2534,15.19951],[107.44022,18.66249],[108.26073,20.07614],[108.10003,21.47338],[108.0569,21.53604],[108.02926,21.54997],[107.97932,21.54503],[107.97383,21.53961],[107.97074,21.54072],[107.96774,21.53601],[107.95232,21.5388],[107.92652,21.58906],[107.90006,21.5905],[107.86114,21.65128],[107.80355,21.66141],[107.66967,21.60787],[107.56537,21.61945],[107.54047,21.5934],[107.49065,21.59774],[107.49532,21.62958],[107.47197,21.6672],[107.41593,21.64839],[107.38636,21.59774],[107.35989,21.60063],[107.35834,21.6672],[107.29296,21.74674],[107.24625,21.7077],[107.20734,21.71493],[107.10771,21.79879],[107.02615,21.81981],[107.00964,21.85948],[107.06101,21.88982],[107.05634,21.92303],[106.99252,21.95191],[106.97228,21.92592],[106.92714,21.93459],[106.9178,21.97357],[106.81038,21.97934],[106.74345,22.00965],[106.72551,21.97923],[106.69276,21.96013],[106.68274,21.99811],[106.70142,22.02409],[106.6983,22.15102],[106.67495,22.1885],[106.69986,22.22309],[106.6516,22.33977],[106.55976,22.34841],[106.57221,22.37],[106.55665,22.46498],[106.58395,22.474],[106.61269,22.60301],[106.65316,22.5757],[106.71698,22.58432],[106.72321,22.63606],[106.76293,22.73491],[106.82404,22.7881],[106.83685,22.8098],[106.81271,22.8226],[106.78422,22.81532],[106.71128,22.85982],[106.71387,22.88296],[106.6734,22.89587],[106.6516,22.86862],[106.60179,22.92884],[106.55976,22.92311],[106.51306,22.94891],[106.49749,22.91164],[106.34961,22.86718],[106.27022,22.87722],[106.19705,22.98475],[106.00179,22.99049],[105.99568,22.94178],[105.90119,22.94168],[105.8726,22.92756],[105.72382,23.06641],[105.57594,23.075],[105.56037,23.16806],[105.49966,23.20669],[105.42805,23.30824],[105.40782,23.28107],[105.32376,23.39684],[105.22569,23.27249],[105.17276,23.28679],[105.11672,23.25247],[105.07002,23.26248],[104.98712,23.19176],[104.96532,23.20463],[104.9486,23.17235],[104.91435,23.18666],[104.87992,23.17141],[104.87382,23.12854],[104.79478,23.12934],[104.8334,23.01484],[104.86765,22.95178],[104.84942,22.93631],[104.77114,22.90017],[104.72755,22.81984],[104.65283,22.83419],[104.60457,22.81841],[104.58122,22.85571],[104.47225,22.75813],[104.35593,22.69353],[104.25683,22.76534],[104.27084,22.8457],[104.11384,22.80363],[104.03734,22.72945],[104.01088,22.51823],[103.99247,22.51958],[103.97384,22.50634],[103.96783,22.51173],[103.96352,22.50584],[103.95191,22.5134],[103.94513,22.52553],[103.93286,22.52703],[103.87904,22.56683],[103.64506,22.79979],[103.56255,22.69499],[103.57812,22.65764],[103.52675,22.59155],[103.43646,22.70648],[103.43179,22.75816],[103.32282,22.8127],[103.28079,22.68063],[103.18895,22.64471],[103.15782,22.59873],[103.17961,22.55705],[103.07843,22.50097],[103.0722,22.44775],[102.9321,22.48659],[102.8636,22.60735],[102.60675,22.73376],[102.57095,22.7036],[102.51802,22.77969],[102.46665,22.77108],[102.42618,22.69212],[102.38415,22.67919],[102.41061,22.64184],[102.25339,22.4607],[102.26428,22.41321],[102.16621,22.43336],[102.14099,22.40092]]]}},{"type":"Feature","properties":{"id":"YE"},"geometry":{"type":"Polygon","coordinates":[[[41.29956,15.565],[42.63806,13.58268],[43.29075,12.79154],[43.32909,12.59711],[43.90659,12.3823],[50.51849,13.0483],[51.12877,12.56479],[52.253,11.68582],[55.69862,12.12478],[53.32998,16.16312],[53.09917,16.67084],[52.81185,17.28568],[52.74267,17.29519],[52.78009,17.35124],[52.00311,19.00083],[49.04884,18.59899],[48.19996,18.20584],[47.58351,17.50366],[47.48245,17.10808],[47.00571,16.94765],[46.76494,17.29151],[46.31018,17.20464],[44.50126,17.47475],[43.70631,17.35762],[43.43005,17.56148],[43.29185,17.53224],[43.22533,17.38343],[43.32653,17.31179],[43.20156,17.25901],[43.17787,17.14717],[43.23967,17.03428],[43.18233,17.02673],[43.1813,16.98438],[43.19328,16.94703],[43.1398,16.90696],[43.18338,16.84852],[43.22012,16.83932],[43.22956,16.80613],[43.24801,16.80613],[43.26303,16.79479],[43.25857,16.75304],[43.21325,16.74416],[43.22066,16.65179],[43.15274,16.67248],[43.11601,16.53166],[42.97215,16.51093],[42.94351,16.49467],[42.94625,16.39721],[42.76801,16.40371],[42.15205,16.40211],[41.37609,16.19728],[41.29956,15.565]]]}},{"type":"Feature","properties":{"id":"ZM"},"geometry":{"type":"Polygon","coordinates":[[[21.97988,-13.00148],[22.00323,-16.18028],[22.17217,-16.50269],[23.20038,-17.47563],[23.47474,-17.62877],[24.23619,-17.47489],[24.32811,-17.49082],[24.38712,-17.46818],[24.5621,-17.52963],[24.70864,-17.49501],[25.00198,-17.58221],[25.26433,-17.79571],[25.51646,-17.86232],[25.6827,-17.81987],[25.85738,-17.91403],[25.85892,-17.97726],[26.08925,-17.98168],[26.0908,-17.93021],[26.21601,-17.88608],[26.55918,-17.99638],[26.68403,-18.07411],[26.74314,-18.0199],[26.89926,-17.98756],[27.14196,-17.81398],[27.30736,-17.60487],[27.61377,-17.34378],[27.62795,-17.24365],[27.83141,-16.96274],[28.73725,-16.5528],[28.76199,-16.51575],[28.81454,-16.48611],[28.8501,-16.04537],[28.9243,-15.93987],[29.01298,-15.93805],[29.21955,-15.76589],[29.4437,-15.68702],[29.8317,-15.6126],[30.35574,-15.6513],[30.41902,-15.62269],[30.22098,-14.99447],[33.24249,-14.00019],[33.16749,-13.93992],[33.07568,-13.98447],[33.02977,-14.05022],[32.99042,-13.95689],[32.88985,-13.82956],[32.79015,-13.80755],[32.76962,-13.77224],[32.84528,-13.71576],[32.7828,-13.64805],[32.68654,-13.64268],[32.66468,-13.60019],[32.68436,-13.55769],[32.73683,-13.57682],[32.84176,-13.52794],[32.86113,-13.47292],[33.0078,-13.19492],[32.98289,-13.12671],[33.02181,-12.88707],[32.96733,-12.88251],[32.94397,-12.76868],[33.05917,-12.59554],[33.18837,-12.61377],[33.28177,-12.54692],[33.37517,-12.54085],[33.54485,-12.35996],[33.47636,-12.32498],[33.3705,-12.34931],[33.25998,-12.14242],[33.33937,-11.91252],[33.32692,-11.59248],[33.24252,-11.59302],[33.23663,-11.40637],[33.29267,-11.43536],[33.29267,-11.3789],[33.39697,-11.15296],[33.25998,-10.88862],[33.28022,-10.84428],[33.47636,-10.78465],[33.70675,-10.56896],[33.54797,-10.36077],[33.53863,-10.20148],[33.31297,-10.05133],[33.37902,-9.9104],[33.36581,-9.81063],[33.31517,-9.82364],[33.2095,-9.61099],[33.12144,-9.58929],[33.10163,-9.66525],[33.05485,-9.61316],[33.00256,-9.63053],[33.00476,-9.5133],[32.95389,-9.40138],[32.76233,-9.31963],[32.75611,-9.28583],[32.53661,-9.24281],[32.49147,-9.14754],[32.43543,-9.11988],[32.25486,-9.13371],[32.16146,-9.05993],[32.08206,-9.04609],[31.98866,-9.07069],[31.94196,-9.02303],[31.94663,-8.93846],[31.81587,-8.88618],[31.71158,-8.91386],[31.57147,-8.81388],[31.57147,-8.70619],[31.37533,-8.60769],[31.00796,-8.58615],[30.79243,-8.27382],[28.88917,-8.4831],[28.9711,-8.66935],[28.38526,-9.23393],[28.36562,-9.30091],[28.52636,-9.35379],[28.51627,-9.44726],[28.56208,-9.49122],[28.68532,-9.78],[28.62795,-9.92942],[28.65032,-10.65133],[28.37241,-11.57848],[28.48357,-11.87532],[29.18592,-12.37921],[29.4992,-12.43843],[29.48404,-12.23604],[29.8139,-12.14898],[29.81551,-13.44683],[29.65078,-13.41844],[29.60531,-13.21685],[29.01918,-13.41353],[28.33199,-12.41375],[27.59932,-12.22123],[27.21025,-11.76157],[27.22541,-11.60323],[27.04351,-11.61312],[26.88687,-12.01868],[26.01777,-11.91488],[25.33058,-11.65767],[25.34069,-11.19707],[24.42612,-11.44975],[24.34528,-11.06816],[24.00027,-10.89356],[24.02603,-11.15368],[23.98804,-12.13149],[24.06672,-12.29058],[23.90937,-12.844],[24.03339,-12.99091],[21.97988,-13.00148]]]}},{"type":"Feature","properties":{"id":"ZW"},"geometry":{"type":"Polygon","coordinates":[[[25.23909,-17.90832],[25.31799,-18.07091],[25.39972,-18.12691],[25.53465,-18.39041],[25.68859,-18.56165],[25.79217,-18.6355],[25.82353,-18.82808],[25.94326,-18.90362],[25.99837,-19.02943],[25.96226,-19.08152],[26.17227,-19.53709],[26.72246,-19.92707],[27.21278,-20.08244],[27.29831,-20.28935],[27.28865,-20.49873],[27.69361,-20.48531],[27.72972,-20.51735],[27.69171,-21.08409],[27.91407,-21.31621],[28.01669,-21.57624],[28.29416,-21.59037],[28.49942,-21.66634],[28.58114,-21.63455],[29.07763,-21.81877],[29.04023,-21.85864],[29.02191,-21.90647],[29.02191,-21.95665],[29.04108,-22.00563],[29.08495,-22.04867],[29.14501,-22.07275],[29.1974,-22.07472],[29.24648,-22.05967],[29.3533,-22.18363],[29.37703,-22.19581],[29.64609,-22.12917],[29.76848,-22.14128],[29.92242,-22.19408],[30.13147,-22.30841],[30.2265,-22.2961],[30.28351,-22.35587],[30.38614,-22.34533],[30.48686,-22.31368],[30.6294,-22.32599],[30.86696,-22.28907],[31.08932,-22.34884],[31.16344,-22.32599],[31.30611,-22.422],[31.38336,-22.36919],[32.41234,-21.31246],[32.48236,-21.32873],[32.37115,-21.133],[32.51644,-20.91929],[32.48122,-20.63319],[32.55167,-20.56312],[32.66174,-20.56106],[32.85987,-20.27841],[32.85987,-20.16686],[32.93032,-20.03868],[33.01178,-20.02007],[33.06461,-19.77787],[32.95013,-19.67219],[32.84666,-19.68462],[32.84446,-19.48343],[32.78282,-19.47513],[32.77966,-19.36098],[32.85107,-19.29238],[32.87088,-19.09279],[32.84006,-19.0262],[32.72118,-19.02204],[32.69917,-18.94293],[32.73439,-18.92628],[32.70137,-18.84712],[32.82465,-18.77419],[32.9017,-18.7992],[32.95013,-18.69079],[32.88629,-18.58023],[32.88629,-18.51344],[33.02278,-18.4696],[33.03159,-18.35054],[32.94133,-17.99705],[33.0492,-17.60298],[32.98536,-17.55891],[32.96554,-17.48964],[33.0426,-17.3468],[33.00517,-17.30477],[32.96554,-17.11971],[32.84113,-16.92259],[32.91051,-16.89446],[32.97655,-16.70689],[32.78943,-16.70267],[32.69917,-16.66893],[32.71017,-16.59932],[32.42838,-16.4727],[32.28529,-16.43892],[32.02772,-16.43892],[31.91324,-16.41569],[31.90223,-16.34388],[31.67988,-16.19595],[31.42451,-16.15154],[31.30563,-16.01193],[31.13171,-15.98019],[30.97761,-16.05848],[30.91597,-15.99924],[30.42568,-15.9962],[30.41902,-15.62269],[30.35574,-15.6513],[29.8317,-15.6126],[29.4437,-15.68702],[29.21955,-15.76589],[29.01298,-15.93805],[28.9243,-15.93987],[28.8501,-16.04537],[28.81454,-16.48611],[28.76199,-16.51575],[28.73725,-16.5528],[27.83141,-16.96274],[27.62795,-17.24365],[27.61377,-17.34378],[27.30736,-17.60487],[27.14196,-17.81398],[26.89926,-17.98756],[26.74314,-18.0199],[26.68403,-18.07411],[26.55918,-17.99638],[26.21601,-17.88608],[26.0908,-17.93021],[26.08925,-17.98168],[25.85892,-17.97726],[25.85738,-17.91403],[25.6827,-17.81987],[25.51646,-17.86232],[25.26433,-17.79571],[25.23909,-17.90832]]]}},{"type":"Feature","properties":{"id":"US-AK"},"geometry":{"type":"Polygon","coordinates":[[[-179.55295,50.81807],[-133.92876,54.62289],[-130.61931,54.70835],[-130.64499,54.76912],[-130.44184,54.85377],[-130.27203,54.97174],[-130.18765,55.07744],[-130.08035,55.21556],[-129.97513,55.28029],[-130.15373,55.74895],[-130.00857,55.91344],[-130.00093,56.00325],[-130.10173,56.12178],[-130.33965,56.10849],[-130.77769,56.36185],[-131.8271,56.62247],[-133.38523,58.42773],[-133.84645,58.73543],[-134.27175,58.8634],[-134.48059,59.13231],[-134.55699,59.1297],[-134.7047,59.2458],[-135.00267,59.28745],[-135.03069,59.56208],[-135.48007,59.79937],[-136.31566,59.59083],[-136.22381,59.55526],[-136.33727,59.44466],[-136.47323,59.46617],[-136.52365,59.16752],[-136.82619,59.16198],[-137.4925,58.89415],[-137.60623,59.24465],[-138.62145,59.76431],[-138.71149,59.90728],[-139.05365,59.99655],[-139.20603,60.08896],[-139.05831,60.35205],[-139.68991,60.33693],[-139.98024,60.18027],[-140.45648,60.30919],[-140.5227,60.22077],[-141.00116,60.30648],[-141.00555,72.20369],[-168.25765,71.99091],[-168.95635,65.98512],[-169.03888,65.48473],[-172.76104,63.77445],[-179.55295,57.62081],[-179.55295,50.81807]]]}},{"type":"Feature","properties":{"id":"GL"},"geometry":{"type":"Polygon","coordinates":[[[-74.12379,75.70014],[-53.68108,62.9266],[-45.64471,55.43944],[-25.70385,67.46637],[-10.71459,70.09565],[-9.68082,72.73731],[-3.52068,82.6752],[-34.32457,84.11035],[-59.93819,82.31398],[-63.1988,81.66522],[-67.48417,80.75493],[-73.91222,78.42484],[-74.12379,75.70014]]]}},{"type":"Feature","properties":{"id":"IN-HP"},"geometry":{"type":"Polygon","coordinates":[[[75.58284,32.07384],[75.71948,32.06541],[75.89904,31.94808],[75.96977,31.81281],[75.92822,31.80522],[76.16546,31.39526],[76.18743,31.28999],[76.34056,31.34278],[76.31996,31.40082],[76.37283,31.43569],[76.50672,31.27913],[76.64817,31.21015],[76.61384,31.00144],[76.7704,30.9087],[76.90017,30.89751],[76.92386,30.83415],[76.99802,30.80024],[77.22083,30.49246],[77.5542,30.40278],[77.80757,30.52086],[77.7238,30.59359],[77.76946,30.63111],[77.69016,30.76927],[77.80174,31.05822],[78.292,31.28676],[78.99581,31.10821],[78.77898,31.31209],[78.71032,31.50197],[78.84516,31.60631],[78.69933,31.78723],[78.78036,31.99478],[78.74404,32.00384],[78.68754,32.10256],[78.49609,32.2762],[78.4645,32.45367],[78.38897,32.53938],[78.32565,32.75263],[77.97477,32.58905],[77.88345,32.7942],[77.66784,32.97007],[77.32795,32.82305],[76.76902,33.26337],[76.31584,33.1341],[75.95329,32.88362],[75.77888,32.9355],[75.92513,32.64689],[75.82936,32.52664],[75.93921,32.41474],[75.7521,32.28364],[75.64756,32.24707],[75.62885,32.10148],[75.58284,32.07384]]]}},{"type":"Feature","properties":{"id":"LK"},"geometry":{"type":"Polygon","coordinates":[[[79.37385,8.98767],[79.9245,5.46963],[82.74996,6.54691],[80.48418,10.20786],[79.42124,9.80115],[79.45362,9.159],[79.37385,8.98767]]]}},{"type":"Feature","properties":{"id":"CN-GZ"},"geometry":{"type":"Polygon","coordinates":[[[103.61377,27.00591],[103.68621,27.06248],[103.78063,26.949],[103.7051,26.83173],[103.8153,26.53417],[104.00104,26.51389],[104.15279,26.67323],[104.34162,26.62444],[104.40925,26.73028],[104.47963,26.58422],[104.56031,26.59405],[104.68666,26.3691],[104.59258,26.31926],[104.47002,26.02007],[104.309,25.65947],[104.44633,25.47861],[104.52152,25.52695],[104.66022,25.2745],[104.78622,25.28195],[104.83119,25.172],[104.72545,25.19096],[104.71206,25.00348],[104.60151,24.89889],[104.52804,24.73498],[104.73884,24.62017],[105.02792,24.79982],[105.10345,24.94186],[105.21606,24.9985],[105.4502,24.91135],[105.49621,24.80917],[105.8052,24.70254],[105.93875,24.72936],[106.016,24.63079],[106.1856,24.78954],[106.19522,24.87491],[106.13994,24.95618],[106.43726,25.02121],[106.63913,25.13533],[106.63621,25.16734],[106.69595,25.18148],[106.89216,25.18723],[106.91757,25.25214],[106.99722,25.245],[106.96083,25.44203],[107.07206,25.56226],[107.23171,25.57682],[107.35153,25.39428],[107.43118,25.28909],[107.48233,25.30414],[107.46568,25.21736],[107.6061,25.2641],[107.6497,25.32106],[107.69313,25.19282],[107.75218,25.24314],[107.77622,25.11979],[108.11096,25.21363],[108.18923,25.45319],[108.34648,25.535],[108.61976,25.30306],[108.60671,25.49348],[108.73237,25.64709],[108.80721,25.53067],[109.07501,25.53376],[109.06745,25.72877],[108.88275,25.68299],[109.03312,25.79865],[109.19929,25.7665],[109.26521,25.71702],[109.47669,26.03334],[109.45129,26.30449],[109.28649,26.28664],[109.38056,26.57931],[109.28581,26.70022],[109.51652,26.75726],[109.49043,27.06524],[109.40734,27.15264],[109.13268,27.06462],[108.87451,27.00652],[108.78936,27.08908],[109.0472,27.29643],[109.04651,27.33578],[109.10659,27.3495],[109.14642,27.45344],[109.2937,27.42541],[109.45197,27.5722],[109.46708,27.69508],[109.33799,27.78198],[109.31533,27.9856],[109.38056,28.03683],[109.29336,28.05531],[109.39807,28.27717],[109.26881,28.3145],[109.28495,28.3772],[109.2616,28.39034],[109.25302,28.4711],[109.26993,28.49849],[109.26898,28.50437],[109.24324,28.4969],[109.22753,28.48317],[109.20075,28.48642],[109.17054,28.4582],[109.15981,28.42597],[109.08531,28.18884],[109.02385,28.20972],[108.99707,28.15828],[108.88137,28.22182],[108.75091,28.2073],[108.72001,28.29228],[108.76739,28.31465],[108.77872,28.42492],[108.70731,28.50098],[108.63178,28.46506],[108.68911,28.40136],[108.65959,28.3343],[108.60706,28.32523],[108.56414,28.37931],[108.60603,28.44092],[108.56552,28.5423],[108.60877,28.54924],[108.63109,28.6457],[108.52981,28.65112],[108.45874,28.62852],[108.33103,28.68637],[108.38012,28.80647],[108.28708,29.09577],[108.22837,29.02405],[108.18477,29.07207],[108.14323,29.05737],[108.0622,29.09037],[108.00899,29.04206],[107.88574,29.01114],[107.84042,28.96309],[107.75184,29.21031],[107.67219,29.14916],[107.4044,29.19472],[107.36457,29.00333],[107.44148,28.93845],[107.26089,28.76103],[107.1833,28.88977],[106.98211,28.86031],[106.96838,28.76585],[106.88804,28.80436],[106.81182,28.7514],[106.86881,28.62611],[106.81251,28.58934],[106.76238,28.62732],[106.77406,28.55919],[106.70677,28.44997],[106.55914,28.51153],[106.65183,28.66649],[106.5715,28.70624],[106.46781,28.84106],[106.44927,28.78631],[106.51245,28.67673],[106.47743,28.53567],[106.36756,28.52662],[105.96588,28.7496],[105.88142,28.602],[105.68778,28.57547],[105.61981,28.43488],[105.65895,28.308],[105.8876,28.23785],[105.9233,28.1277],[106.13857,28.16948],[106.20826,28.13497],[106.35864,27.83907],[105.92262,27.72973],[105.60676,27.69751],[105.31219,27.71088],[105.21057,27.37603],[105.08079,27.42114],[104.86312,27.28362],[104.85145,27.34523],[104.6046,27.30528],[104.50984,27.40712],[104.37217,27.47233],[104.18334,27.2708],[103.94165,27.45375],[103.83865,27.27202],[103.62716,27.11872],[103.61377,27.00591]]]}},{"type":"Feature","properties":{"id":"IS"},"geometry":{"type":"Polygon","coordinates":[[[-25.70385,67.46637],[-25.58144,62.99867],[-12.08632,63.06873],[-12.20873,67.52551],[-25.70385,67.46637]]]}},{"type":"Feature","properties":{"id":"BS"},"geometry":{"type":"Polygon","coordinates":[[[-80.16442,23.44484],[-73.62304,20.6935],[-72.94479,20.79216],[-72.41726,22.40371],[-76.80329,26.86841],[-78.4311,27.53866],[-79.36558,27.02964],[-80.16442,23.44484]]]}},{"type":"Feature","properties":{"id":"VI"},"geometry":{"type":"Polygon","coordinates":[[[-65.27974,17.56928],[-64.35558,17.48384],[-64.646,18.10286],[-64.64067,18.36478],[-64.86049,18.39954],[-65.02435,18.73231],[-65.27974,17.56928]]]}},{"type":"Feature","properties":{"id":"MV"},"geometry":{"type":"Polygon","coordinates":[[[72.15131,7.6285],[72.64576,-1.32013],[74.20495,-1.22734],[74.03744,7.70688],[72.15131,7.6285]]]}},{"type":"Feature","properties":{"id":"IO"},"geometry":{"type":"Polygon","coordinates":[[[70.64391,-7.71751],[72.09053,-7.71938],[73.19616,-7.72081],[73.19718,-6.94577],[73.19979,-4.96078],[70.64754,-4.95745],[70.64391,-7.71751]]]}},{"type":"Feature","properties":{"id":"CN-ZJ"},"geometry":{"type":"Polygon","coordinates":[[[118.0323,29.10057],[118.25134,28.92523],[118.3509,28.8164],[118.4254,28.68486],[118.40343,28.57457],[118.47381,28.47925],[118.43364,28.41193],[118.48308,28.32856],[118.42121,28.29239],[118.47759,28.23876],[118.53973,28.28594],[118.79035,28.24572],[118.71002,27.98773],[118.79722,27.94164],[118.89678,27.64643],[118.89541,27.47294],[119.12132,27.44186],[119.25247,27.43425],[119.61845,27.67988],[119.65999,27.5381],[119.76985,27.30741],[120.03662,27.34371],[120.13275,27.42053],[120.25909,27.43089],[120.42011,27.26531],[120.40603,27.20273],[120.43281,27.17219],[121.03532,26.8787],[123.5458,31.01942],[121.26434,30.68516],[121.21456,30.78726],[121.12152,30.78018],[121.12529,30.86509],[121.03122,30.82265],[120.98865,30.89603],[120.98659,31.01939],[120.89424,31.01822],[120.84754,30.99173],[120.7703,30.9988],[120.73459,30.96289],[120.67485,30.957],[120.70747,30.88572],[120.64739,30.85095],[120.5801,30.85566],[120.49873,30.75953],[120.42835,30.91459],[120.35007,30.88896],[120.36208,30.97054],[119.91439,31.17051],[119.63149,31.1329],[119.58412,30.97407],[119.57382,30.85743],[119.43134,30.64145],[119.38671,30.69018],[119.24663,30.61575],[119.23393,30.53062],[119.31152,30.53032],[119.38705,30.37761],[119.22912,30.28871],[118.89404,30.35391],[118.90914,30.20508],[118.85627,30.16709],[118.89816,30.02332],[118.75808,29.76258],[118.48548,29.52447],[118.33717,29.48503],[118.20052,29.38178],[118.07281,29.2852],[118.0323,29.10057]]]}},{"type":"Feature","properties":{"id":"RE"},"geometry":{"type":"Polygon","coordinates":[[[54.32269,-20.37973],[54.43368,-22.02482],[56.73473,-21.9174],[56.62373,-20.2711],[54.32269,-20.37973]]]}},{"type":"Feature","properties":{"id":"MU"},"geometry":{"type":"Polygon","coordinates":[[[56.09755,-9.55401],[56.62373,-20.2711],[56.73473,-21.9174],[64.11105,-21.5783],[63.47388,-9.1938],[56.09755,-9.55401]]]}},{"type":"Feature","properties":{"id":"SC"},"geometry":{"type":"Polygon","coordinates":[[[45.39948,-9.09441],[46.52682,-10.83678],[48.86266,-10.8109],[51.51407,-10.78153],[57.144,-7.697],[56.94974,-2.9998],[53.06458,-3.53165],[45.39948,-9.09441]]]}},{"type":"Feature","properties":{"id":"KM"},"geometry":{"type":"Polygon","coordinates":[[[42.93552,-11.11413],[42.99868,-12.65261],[44.75722,-12.58368],[44.69407,-11.04481],[42.93552,-11.11413]]]}},{"type":"Feature","properties":{"id":"FO"},"geometry":{"type":"Polygon","coordinates":[[[-8.51774,62.35338],[-6.51083,60.95272],[-5.70102,62.77194],[-8.51774,62.35338]]]}},{"type":"Feature","properties":{"id":"CN-XJ"},"geometry":{"type":"Polygon","coordinates":[[[73.5004,39.38402],[73.55396,39.3543],[73.54572,39.27567],[73.60638,39.24534],[73.75823,39.023],[73.81728,39.04007],[73.82964,38.91517],[73.7445,38.93867],[73.7033,38.84782],[73.80656,38.66449],[73.79806,38.61106],[73.97933,38.52945],[74.17022,38.65504],[74.51217,38.47034],[74.69619,38.42947],[74.69894,38.22155],[74.80331,38.19889],[74.82665,38.07359],[74.9063,38.03033],[74.92416,37.83428],[75.00935,37.77486],[74.8912,37.67576],[74.94338,37.55501],[75.06011,37.52779],[75.15899,37.41443],[75.09719,37.37297],[75.12328,37.31839],[74.88887,37.23275],[74.80605,37.21565],[74.49981,37.24518],[74.56453,37.03023],[75.13839,37.02622],[75.40481,36.95382],[75.45562,36.71971],[75.72737,36.7529],[75.92391,36.56986],[76.0324,36.41198],[76.00906,36.17511],[75.93028,36.13136],[76.15325,35.9264],[76.14913,35.82848],[76.33453,35.84296],[76.50961,35.8908],[76.77323,35.66062],[76.84539,35.67356],[76.96624,35.5932],[77.44277,35.46132],[77.70232,35.46244],[77.80532,35.52058],[78.11664,35.48022],[78.03466,35.3785],[78.00033,35.23954],[78.22692,34.88771],[78.18435,34.7998],[78.27781,34.61484],[78.54964,34.57283],[78.56475,34.50835],[78.74465,34.45174],[79.05364,34.32482],[79.05418,34.4154],[79.83283,34.48958],[80.45287,35.45172],[81.66961,35.24337],[82.01568,35.34201],[82.45238,35.7309],[82.966,35.62716],[83.1253,35.39688],[84.19784,35.35881],[85.26489,35.80333],[85.57662,35.64055],[86.09298,35.8679],[86.2619,36.19995],[88.53332,36.48755],[88.76438,36.29077],[88.94119,36.35716],[89.691,36.0935],[89.95056,36.08018],[90.01922,36.2631],[90.86379,36.02577],[91.10961,36.10792],[91.02035,36.54053],[90.7196,36.59347],[90.84869,36.93342],[91.31149,37.02887],[91.06567,37.48575],[90.5136,37.74465],[90.52322,38.31903],[90.31585,38.22955],[90.14076,38.33734],[90.18127,38.39764],[90.09406,38.49014],[90.44975,38.49928],[92.40874,39.03625],[92.92785,40.58058],[93.76556,40.66605],[94.80789,41.53428],[95.19103,41.7498],[95.35171,41.54559],[96.18324,41.97225],[96.04934,42.38796],[96.37926,42.72055],[96.35658,42.90363],[95.89543,43.2528],[95.52594,43.99353],[95.32891,44.02407],[95.39772,44.2805],[95.01191,44.25274],[94.71959,44.35284],[94.10003,44.71016],[93.51161,44.95964],[91.64048,45.07408],[90.89169,45.19667],[90.65114,45.49314],[90.70907,45.73437],[91.03026,46.04194],[90.99672,46.14207],[90.89639,46.30711],[91.07696,46.57315],[91.0147,46.58171],[91.03649,46.72916],[90.84035,46.99525],[90.76108,46.99399],[90.48542,47.30438],[90.48854,47.41826],[90.33598,47.68303],[90.10871,47.7375],[90.06512,47.88177],[89.76624,47.82745],[89.55453,48.0423],[89.0711,47.98528],[88.93186,48.10263],[88.8011,48.11302],[88.58316,48.21893],[88.58939,48.34531],[87.96361,48.58478],[88.0788,48.71436],[87.73822,48.89582],[87.88171,48.95853],[87.81333,49.17354],[87.48983,49.13794],[87.478,49.07403],[87.28386,49.11626],[86.87238,49.12432],[86.73568,48.99918],[86.75343,48.70331],[86.38069,48.46064],[85.73581,48.3939],[85.5169,48.05493],[85.61067,47.49753],[85.69696,47.2898],[85.54294,47.06171],[85.22443,47.04816],[84.93995,46.87399],[84.73077,47.01394],[83.92184,46.98912],[83.04622,47.19053],[82.21792,45.56619],[82.58474,45.40027],[82.51374,45.1755],[81.73278,45.3504],[80.11169,45.03352],[79.8987,44.89957],[80.38384,44.63073],[80.40229,44.23319],[80.40031,44.10986],[80.75156,43.44948],[80.69718,43.32589],[80.77771,43.30065],[80.78817,43.14235],[80.62913,43.141],[80.3735,43.01557],[80.58999,42.9011],[80.38169,42.83142],[80.26886,42.8366],[80.16892,42.61137],[80.26841,42.23797],[80.17807,42.21166],[80.17842,42.03211],[79.92977,42.04113],[78.3732,41.39603],[78.15757,41.38565],[78.12873,41.23091],[77.81287,41.14307],[77.76206,41.01574],[77.52723,41.00227],[77.3693,41.0375],[77.28004,41.0033],[76.99302,41.0696],[76.75681,40.95354],[76.5261,40.46114],[76.33659,40.3482],[75.96168,40.38064],[75.91361,40.2948],[75.69663,40.28642],[75.5854,40.66874],[75.22834,40.45382],[75.08243,40.43945],[74.82013,40.52197],[74.78168,40.44886],[74.85996,40.32857],[74.69875,40.34668],[74.35063,40.09742],[74.25533,40.13191],[73.97049,40.04378],[73.83006,39.76136],[73.9051,39.75073],[73.92354,39.69565],[73.94683,39.60733],[73.87018,39.47879],[73.59831,39.46425],[73.59241,39.40843],[73.5004,39.38402]]]}},{"type":"Feature","properties":{"id":"PM"},"geometry":{"type":"Polygon","coordinates":[[[-56.70773,46.51478],[-55.8643,46.64935],[-56.25228,47.31192],[-56.67989,47.3339],[-56.70773,46.51478]]]}},{"type":"Feature","properties":{"id":"JP"},"geometry":{"type":"Polygon","coordinates":[[[122.20217,23.54946],[136.0511,20.05526],[155.16731,23.60141],[145.97944,43.07828],[145.76215,43.50342],[145.23667,43.76813],[145.82343,44.571],[140.9182,45.92937],[133.61399,37.41],[129.2669,34.87122],[127.42045,32.33183],[123.1407,27.24938],[122.20217,23.54946]]]}},{"type":"Feature","properties":{"id":"IN-KL"},"geometry":{"type":"Polygon","coordinates":[[[74.66307,12.69394],[75.18012,11.478],[75.52551,11.6993],[75.53821,11.76384],[75.55864,11.72317],[75.53787,11.6872],[75.19042,11.45378],[76.72283,7.82138],[77.22358,8.44758],[77.20024,8.50734],[77.27954,8.52296],[77.17655,8.7385],[77.26135,8.843],[77.15045,9.01496],[77.42065,9.51543],[77.16865,9.61632],[77.24693,9.80447],[77.25997,10.02971],[77.20298,10.11759],[77.27645,10.13382],[77.21465,10.36423],[76.96403,10.221],[76.82327,10.3237],[76.80679,10.63159],[76.87236,10.63226],[76.85691,10.68051],[76.8988,10.77327],[76.84112,10.81138],[76.81777,10.86163],[76.6492,10.924],[76.7237,11.20736],[76.43531,11.19456],[76.53934,11.35079],[76.23344,11.51871],[76.23722,11.58699],[76.37145,11.59136],[76.42948,11.66568],[76.18949,11.87608],[76.11757,11.85105],[76.11259,11.97887],[76.00307,11.93185],[75.86059,11.95502],[75.78987,12.08296],[75.54302,12.20279],[75.49392,12.29103],[75.43109,12.31249],[75.37067,12.45602],[75.43659,12.47144],[75.39882,12.50161],[75.34698,12.46105],[75.27214,12.5502],[75.33393,12.57534],[75.28106,12.61856],[75.23471,12.56662],[75.19798,12.6132],[75.14579,12.63648],[75.16124,12.67969],[75.11438,12.67752],[75.08777,12.70029],[75.06872,12.66228],[75.04108,12.67484],[75.0531,12.71804],[74.98323,12.73998],[75.01327,12.79137],[74.86049,12.76057],[74.66307,12.69394]]]}},{"type":"Feature","properties":{"id":"TV"},"geometry":{"type":"Polygon","coordinates":[[[174,-11.5],[179.99999,-11.5],[179.99999,-5],[174,-5],[174,-11.5]]]}},{"type":"Feature","properties":{"id":"VU"},"geometry":{"type":"Polygon","coordinates":[[[162.93363,-17.28904],[173.07304,-22.54607],[168.14096,-12.74443],[165.31108,-12.67903],[162.93363,-17.28904]]]}},{"type":"Feature","properties":{"id":"SB"},"geometry":{"type":"Polygon","coordinates":[[[154.74815,-7.33315],[160.37269,-13.44534],[165.31108,-12.67903],[168.14096,-12.74443],[171.12712,-12.81344],[171.21374,-9.22564],[159.32766,-4.77078],[157.60997,-5.69776],[156.03296,-6.55528],[156.03993,-6.65703],[155.92557,-6.84664],[155.69784,-6.92661],[155.60735,-6.92266],[154.74815,-7.33315]]]}},{"type":"Feature","properties":{"id":"MP"},"geometry":{"type":"Polygon","coordinates":[[[143.82485,13.92273],[146.25931,13.85876],[146.6755,21.00809],[144.18594,21.03576],[143.82485,13.92273]]]}},{"type":"Feature","properties":{"id":"MH"},"geometry":{"type":"Polygon","coordinates":[[[159.04653,10.59067],[161.58988,8.892633],[165.35175,6.367],[169,3.9],[173.53711,5.70687],[169.29099,15.77133],[159.04653,10.59067]]]}},{"type":"Feature","properties":{"id":"FM"},"geometry":{"type":"Polygon","coordinates":[[[136.04605,12.45908],[136.27107,6.73747],[148.49862,1.920402],[154.49668,-0.44964],[156.88247,-1.39237],[161.87397,3.186785],[165.35175,6.367],[161.58988,8.892633],[159.04653,10.59067],[153.83475,11.01511],[148.42679,11.45488],[136.04605,12.45908]]]}},{"type":"Feature","properties":{"id":"MY"},"geometry":{"type":"Polygon","coordinates":[[[99.31854,5.99868],[99.75778,3.86466],[103.03657,1.30383],[103.56591,1.19719],[103.62738,1.35255],[103.67468,1.43166],[103.7219,1.46108],[103.74161,1.4502],[103.76395,1.45183],[103.81181,1.47953],[103.86383,1.46288],[103.89565,1.42841],[103.93384,1.42926],[104.00131,1.42405],[104.02277,1.4438],[104.04622,1.44691],[104.07348,1.43322],[104.08871,1.42015],[104.09162,1.39694],[104.08072,1.35998],[104.12282,1.27714],[104.34728,1.33529],[104.56723,1.44271],[105.01437,3.24936],[108.10426,5.42408],[109.71058,2.32059],[109.64506,2.08014],[109.62558,1.99182],[109.53794,1.91771],[109.57923,1.80624],[109.66397,1.79972],[109.66397,1.60425],[110.35354,0.98869],[110.49182,0.88088],[110.62374,0.873],[111.22979,1.08326],[111.55434,0.97864],[111.82846,0.99349],[111.94553,1.12016],[112.15679,1.17004],[112.2127,1.44135],[112.48648,1.56516],[113.021,1.57819],[113.01448,1.42832],[113.64677,1.23933],[114.03788,1.44787],[114.57892,1.5],[114.80706,1.92351],[114.80706,2.21665],[115.1721,2.49671],[115.11343,2.82879],[115.53713,3.14776],[115.58276,3.93499],[115.90217,4.37708],[117.25801,4.35108],[117.47313,4.18857],[117.67641,4.16535],[117.89538,4.16637],[118.07935,4.15511],[118.8663,4.44172],[118.75416,4.59798],[119.44841,5.09568],[119.34756,5.53889],[117.89159,6.25755],[117.43832,7.3895],[117.17735,7.52841],[116.79524,7.43869],[115.02521,5.35005],[115.16236,5.01011],[115.15092,4.87604],[115.20737,4.8256],[115.27819,4.63661],[115.2851,4.42295],[115.36346,4.33563],[115.31275,4.30806],[115.09978,4.39123],[115.07737,4.53418],[115.04064,4.63706],[115.02278,4.74137],[115.02955,4.82087],[115.05038,4.90275],[114.99417,4.88201],[114.96982,4.81146],[114.88841,4.81905],[114.8266,4.75062],[114.77303,4.72871],[114.83189,4.42387],[114.88039,4.4257],[114.78539,4.12205],[114.64211,4.00694],[114.49922,4.13108],[114.4416,4.27588],[114.32176,4.2552],[114.32176,4.34942],[114.26876,4.49878],[114.15813,4.57],[114.07448,4.58441],[114.08532,4.64632],[109.55486,8.10026],[104.81582,8.03101],[102.46318,7.22462],[102.09086,6.23546],[102.08127,6.22679],[102.07732,6.193],[102.09182,6.14161],[102.01835,6.05407],[101.99209,6.04075],[101.97114,6.01992],[101.9714,6.00575],[101.94712,5.98421],[101.92819,5.85511],[101.91776,5.84269],[101.89188,5.8386],[101.80144,5.74505],[101.75074,5.79091],[101.69773,5.75881],[101.58019,5.93534],[101.25524,5.78633],[101.25755,5.71065],[101.14062,5.61613],[100.98815,5.79464],[101.02708,5.91013],[101.087,5.9193],[101.12388,6.11411],[101.06165,6.14161],[101.12618,6.19431],[101.10313,6.25617],[100.85884,6.24929],[100.81045,6.45086],[100.74822,6.46231],[100.74361,6.50811],[100.66986,6.45086],[100.43027,6.52389],[100.42351,6.51762],[100.41791,6.5189],[100.41152,6.52299],[100.35413,6.54932],[100.31929,6.65413],[100.32607,6.65933],[100.32671,6.66526],[100.31884,6.66423],[100.31618,6.66781],[100.30828,6.66462],[100.29651,6.68439],[100.19511,6.72559],[100.12,6.42105],[100.0756,6.4045],[99.91873,6.50233],[99.50117,6.44501],[99.31854,5.99868]]]}},{"type":"Feature","properties":{"id":"ID"},"geometry":{"type":"Polygon","coordinates":[[[93.82619,5.95573],[96.82918,-7.16134],[122.91521,-11.65621],[125.68138,-9.85176],[125.09025,-9.46406],[124.97892,-9.19281],[125.04044,-9.17093],[125.09434,-9.19669],[125.18907,-9.16434],[125.18632,-9.03142],[125.11764,-8.96359],[124.97742,-9.08128],[124.94011,-8.85617],[124.46701,-9.13002],[124.45971,-9.30263],[124.38554,-9.3582],[124.35258,-9.43002],[124.3535,-9.48493],[124.28115,-9.50453],[124.28115,-9.42189],[124.21247,-9.36904],[124.14517,-9.42324],[124.10539,-9.41206],[124.04286,-9.34243],[124.04628,-9.22671],[124.33472,-9.11416],[124.92337,-8.75859],[125.31127,-8.22976],[125.65946,-8.06136],[125.87691,-8.31789],[127.21788,-8.22363],[127.53551,-8.41485],[127.55165,-9.05052],[139.41724,-8.97063],[140.88922,-9.34945],[141.00782,-9.1242],[141.01763,-6.90181],[140.85295,-6.72996],[140.99813,-6.3233],[141.00167,0.68547],[134.40878,1.79674],[128.97621,3.08804],[126.69413,6.02692],[124.97752,4.82064],[118.41402,3.99509],[118.07935,4.15511],[117.89538,4.16637],[117.67641,4.16535],[117.47313,4.18857],[117.25801,4.35108],[115.90217,4.37708],[115.58276,3.93499],[115.53713,3.14776],[115.11343,2.82879],[115.1721,2.49671],[114.80706,2.21665],[114.80706,1.92351],[114.57892,1.5],[114.03788,1.44787],[113.64677,1.23933],[113.01448,1.42832],[113.021,1.57819],[112.48648,1.56516],[112.2127,1.44135],[112.15679,1.17004],[111.94553,1.12016],[111.82846,0.99349],[111.55434,0.97864],[111.22979,1.08326],[110.62374,0.873],[110.49182,0.88088],[110.35354,0.98869],[109.66397,1.60425],[109.66397,1.79972],[109.57923,1.80624],[109.53794,1.91771],[109.62558,1.99182],[109.64506,2.08014],[109.71058,2.32059],[108.10426,5.42408],[105.01437,3.24936],[104.56723,1.44271],[104.34728,1.33529],[104.12282,1.27714],[104.03085,1.26954],[103.74084,1.12902],[103.66049,1.18825],[103.56591,1.19719],[103.03657,1.30383],[99.75778,3.86466],[97.65314,5.70549],[94.98735,6.60903],[93.82619,5.95573]]]}},{"type":"Feature","properties":{"id":"IN-BR"},"geometry":{"type":"Polygon","coordinates":[[[83.34125,25.0125],[83.39824,24.78735],[83.5033,24.73747],[83.49883,24.52651],[83.75015,24.50245],[83.94584,24.54899],[83.9994,24.63141],[84.49756,24.28546],[84.57447,24.41151],[84.81994,24.529],[84.89307,24.36304],[85.66108,24.61518],[85.7373,24.8154],[86.12869,24.71876],[86.12491,24.61143],[86.32163,24.5824],[86.28833,24.46027],[86.45004,24.36586],[86.60316,24.60457],[87.05532,24.61237],[87.10235,24.84812],[87.15866,24.89391],[87.1511,25.02246],[87.21668,25.09243],[87.28946,25.09741],[87.32688,25.22016],[87.47348,25.19686],[87.48962,25.29933],[87.53906,25.27947],[87.58369,25.35271],[87.65373,25.28009],[87.86521,25.27139],[87.78865,25.34495],[87.76514,25.42529],[87.92667,25.53717],[88.0688,25.48047],[88.03859,25.54182],[88.04923,25.68794],[87.89989,25.76866],[87.90058,25.85304],[87.78625,25.87559],[87.84393,26.04537],[87.95036,26.06511],[88.0046,26.14341],[88.29093,26.3568],[88.23034,26.37679],[88.26158,26.4263],[88.19137,26.47549],[88.23394,26.55413],[88.14313,26.51097],[88.09963,26.54195],[88.09414,26.43732],[88.00895,26.36029],[87.90115,26.44923],[87.89085,26.48565],[87.84193,26.43663],[87.7918,26.46737],[87.76004,26.40711],[87.67893,26.43501],[87.66803,26.40294],[87.59175,26.38342],[87.55274,26.40596],[87.51571,26.43106],[87.46566,26.44058],[87.37314,26.40815],[87.34568,26.34787],[87.26568,26.37294],[87.26587,26.40592],[87.24682,26.4143],[87.18863,26.40558],[87.14751,26.40542],[87.09147,26.45039],[87.0707,26.58571],[87.04691,26.58685],[87.01559,26.53228],[86.95912,26.52076],[86.94543,26.52076],[86.82898,26.43919],[86.76797,26.45892],[86.74025,26.42386],[86.69124,26.45169],[86.62686,26.46891],[86.61313,26.48658],[86.57073,26.49825],[86.54258,26.53819],[86.49726,26.54218],[86.31564,26.61925],[86.26235,26.61886],[86.22513,26.58863],[86.13596,26.60651],[86.02729,26.66756],[85.8492,26.56667],[85.85126,26.60866],[85.83126,26.61134],[85.76907,26.63076],[85.72315,26.67471],[85.73483,26.79613],[85.66239,26.84822],[85.61621,26.86721],[85.59461,26.85161],[85.5757,26.85955],[85.56471,26.84133],[85.47752,26.79292],[85.34302,26.74954],[85.21159,26.75933],[85.18046,26.80519],[85.19291,26.86909],[85.15883,26.86966],[85.02635,26.85381],[85.05592,26.88991],[85.00536,26.89523],[84.97186,26.9149],[84.96687,26.95599],[84.85754,26.98984],[84.82913,27.01989],[84.793,26.9968],[84.64496,27.04669],[84.69166,27.21294],[84.62161,27.33885],[84.29315,27.39],[84.25735,27.44941],[84.21376,27.45218],[84.10791,27.52399],[84.02229,27.43836],[83.93306,27.44939],[83.86182,27.4241],[83.85595,27.35797],[83.83048,27.29552],[84.02687,27.08847],[84.23973,26.86511],[84.30599,26.75082],[84.39594,26.61554],[84.1254,26.6318],[84.05296,26.54891],[83.87992,26.5225],[83.90258,26.44905],[84.17243,26.37495],[84.17106,26.26139],[84.01245,26.23676],[84.02309,26.13848],[84.15183,26.03334],[84.27955,25.94538],[84.54082,25.85737],[84.66613,25.74022],[84.4749,25.68485],[84.38804,25.76959],[84.32899,25.70588],[84.0921,25.72908],[84.05845,25.64833],[83.88061,25.51765],[83.871,25.49333],[83.81452,25.45459],[83.84593,25.43645],[83.76422,25.3859],[83.74242,25.40792],[83.65917,25.36745],[83.64852,25.34464],[83.49077,25.28614],[83.4621,25.25152],[83.41335,25.24966],[83.34897,25.17744],[83.34125,25.0125]]]}},{"type":"Feature","properties":{"id":"PW"},"geometry":{"type":"Polygon","coordinates":[[[128.97621,3.08804],[134.40878,1.79674],[136.27107,6.73747],[136.04605,12.45908],[128.97621,3.08804]]]}},{"type":"Feature","properties":{"id":"PH"},"geometry":{"type":"Polygon","coordinates":[[[116.28201,8.2483],[116.79524,7.43869],[117.17735,7.52841],[117.43832,7.3895],[117.89159,6.25755],[119.34756,5.53889],[119.44841,5.09568],[118.75416,4.59798],[118.8663,4.44172],[118.07935,4.15511],[118.41402,3.99509],[124.97752,4.82064],[126.69413,6.02692],[129.01382,8.04729],[121.8109,21.77688],[120.69238,21.52331],[116.28201,8.2483]]]}},{"type":"Feature","properties":{"id":"CN-GX"},"geometry":{"type":"Polygon","coordinates":[[[104.4659,24.65044],[104.56718,24.44527],[104.70588,24.31018],[104.71481,24.43777],[105.0238,24.43839],[105.19615,24.34209],[105.16868,24.14988],[105.49552,24.01949],[105.57174,24.13798],[105.64796,24.03831],[106.00364,24.12858],[106.15608,23.90592],[106.14097,23.5772],[106.00089,23.4519],[105.8773,23.53314],[105.59509,23.31766],[105.56037,23.16806],[105.57594,23.075],[105.72382,23.06641],[105.8726,22.92756],[105.90119,22.94168],[105.99568,22.94178],[106.00179,22.99049],[106.19705,22.98475],[106.27022,22.87722],[106.34961,22.86718],[106.49749,22.91164],[106.51306,22.94891],[106.55976,22.92311],[106.60179,22.92884],[106.6516,22.86862],[106.6734,22.89587],[106.71387,22.88296],[106.71128,22.85982],[106.78422,22.81532],[106.81271,22.8226],[106.83685,22.8098],[106.82404,22.7881],[106.76293,22.73491],[106.72321,22.63606],[106.71698,22.58432],[106.65316,22.5757],[106.61269,22.60301],[106.58395,22.474],[106.55665,22.46498],[106.57221,22.37],[106.55976,22.34841],[106.6516,22.33977],[106.69986,22.22309],[106.67495,22.1885],[106.6983,22.15102],[106.70142,22.02409],[106.68274,21.99811],[106.69276,21.96013],[106.72551,21.97923],[106.74345,22.00965],[106.81038,21.97934],[106.9178,21.97357],[106.92714,21.93459],[106.97228,21.92592],[106.99252,21.95191],[107.05634,21.92303],[107.06101,21.88982],[107.00964,21.85948],[107.02615,21.81981],[107.10771,21.79879],[107.20734,21.71493],[107.24625,21.7077],[107.29296,21.74674],[107.35834,21.6672],[107.35989,21.60063],[107.38636,21.59774],[107.41593,21.64839],[107.47197,21.6672],[107.49532,21.62958],[107.49065,21.59774],[107.54047,21.5934],[107.56537,21.61945],[107.66967,21.60787],[107.80355,21.66141],[107.86114,21.65128],[107.90006,21.5905],[107.92652,21.58906],[107.95232,21.5388],[107.96774,21.53601],[107.97074,21.54072],[107.97383,21.53961],[107.97932,21.54503],[108.02926,21.54997],[108.0569,21.53604],[108.10003,21.47338],[108.26073,20.07614],[109.78912,21.47351],[109.73968,21.6054],[109.76371,21.67178],[109.8904,21.65008],[109.92216,21.71198],[109.94585,21.84397],[109.98481,21.87185],[110.19458,21.90148],[110.24642,21.88332],[110.28573,21.91756],[110.32127,21.89351],[110.3841,21.89096],[110.39234,21.91199],[110.36556,21.93476],[110.34702,22.19567],[110.62442,22.15274],[110.67317,22.17659],[110.64193,22.23349],[110.78475,22.27353],[110.67626,22.47576],[110.73738,22.46307],[110.74424,22.56931],[110.99452,22.63682],[111.31965,22.85086],[111.35158,22.96123],[111.42711,23.03013],[111.34094,23.19654],[111.47724,23.62313],[111.63276,23.64641],[111.65645,23.83308],[111.79515,23.8133],[111.92596,23.97339],[111.87171,24.10978],[112.05711,24.35522],[111.93145,24.69506],[112.04166,24.77987],[111.68666,24.78486],[111.52633,24.63952],[111.42745,24.6832],[111.46179,25.02526],[111.28326,25.14528],[110.98525,24.9157],[110.97015,25.10922],[111.31347,25.47613],[111.30042,25.70155],[111.37596,25.73001],[111.48239,25.88208],[111.25579,25.85922],[111.19125,25.94693],[111.29287,26.24662],[111.09306,26.31188],[110.96328,26.3851],[110.9262,26.26694],[110.75317,26.25277],[110.61172,26.33465],[110.31028,25.96915],[110.09811,26.01914],[109.97486,26.19241],[109.81109,26.04259],[109.82276,25.88023],[109.68749,25.88517],[109.72663,26.00001],[109.47669,26.03334],[109.26521,25.71702],[109.19929,25.7665],[109.03312,25.79865],[108.88275,25.68299],[109.06745,25.72877],[109.07501,25.53376],[108.80721,25.53067],[108.73237,25.64709],[108.60671,25.49348],[108.61976,25.30306],[108.34648,25.535],[108.18923,25.45319],[108.11096,25.21363],[107.77622,25.11979],[107.75218,25.24314],[107.69313,25.19282],[107.6497,25.32106],[107.6061,25.2641],[107.46568,25.21736],[107.48233,25.30414],[107.43118,25.28909],[107.35153,25.39428],[107.23171,25.57682],[107.07206,25.56226],[106.96083,25.44203],[106.99722,25.245],[106.91757,25.25214],[106.89216,25.18723],[106.69595,25.18148],[106.63621,25.16734],[106.63913,25.13533],[106.43726,25.02121],[106.13994,24.95618],[106.19522,24.87491],[106.1856,24.78954],[106.016,24.63079],[105.93875,24.72936],[105.8052,24.70254],[105.49621,24.80917],[105.4502,24.91135],[105.21606,24.9985],[105.10345,24.94186],[105.02792,24.79982],[104.73884,24.62017],[104.52804,24.73498],[104.4659,24.65044]]]}},{"type":"Feature","properties":{"id":"JE"},"geometry":{"type":"Polygon","coordinates":[[[-2.56423,49.22209],[-2.5234,48.91595],[-2.00491,48.86706],[-1.83944,49.23037],[-2.09454,49.46288],[-2.56423,49.22209]]]}},{"type":"Feature","properties":{"id":"GG"},"geometry":{"type":"Polygon","coordinates":[[[-3.06097,49.47231],[-2.9511,49.31141],[-2.56423,49.22209],[-2.09454,49.46288],[-2.02963,49.91866],[-2.39388,49.94612],[-3.06097,49.47231]]]}},{"type":"Feature","properties":{"id":"IM"},"geometry":{"type":"Polygon","coordinates":[[[-5.83481,53.87749],[-5.37267,53.63269],[-3.64906,54.12723],[-4.1819,54.57861],[-4.86305,54.44148],[-5.83481,53.87749]]]}},{"type":"Feature","properties":{"id":"CN-HB"},"geometry":{"type":"Polygon","coordinates":[[[108.3657,29.84064],[108.4917,29.71966],[108.6098,29.86863],[108.67641,29.85255],[108.68499,29.70132],[108.90918,29.60331],[108.86455,29.46411],[108.93836,29.43601],[108.90918,29.32651],[108.97647,29.33085],[109.05029,29.4055],[109.10419,29.36691],[109.10745,29.21855],[109.2346,29.11829],[109.45678,29.55613],[109.53609,29.62331],[109.70088,29.60928],[109.78637,29.76556],[110.14514,29.78374],[110.37036,29.63345],[110.64742,29.77272],[110.4943,29.91923],[110.52246,30.06523],[110.75386,30.04888],[110.75729,30.12523],[110.94646,30.06493],[111.24481,30.04383],[111.40274,29.91268],[111.51912,29.93232],[111.6053,29.89006],[111.79447,29.91209],[111.95548,29.82843],[112.06706,29.73188],[112.10861,29.66001],[112.2377,29.65673],[112.28713,29.50147],[112.46566,29.6433],[112.68093,29.59734],[112.80796,29.74917],[112.8955,29.79328],[112.9264,29.69014],[112.94769,29.47188],[113.15505,29.46112],[113.51898,29.82813],[113.56704,29.67254],[113.7363,29.58928],[113.62403,29.52118],[113.7569,29.44767],[113.59588,29.25914],[113.69132,29.2187],[113.69132,29.06637],[113.81835,29.10837],[113.85749,29.04056],[113.9447,29.05196],[114.0525,29.20761],[114.23034,29.2193],[114.25231,29.31993],[114.88128,29.38397],[114.95063,29.55852],[115.16693,29.50176],[115.11199,29.68029],[115.26786,29.63674],[115.40107,29.67492],[115.50064,29.8323],[115.65994,29.85851],[115.93803,29.71906],[116.12823,29.82754],[116.08222,30.12434],[115.91949,30.30472],[115.90061,30.50992],[115.7698,30.6701],[115.85082,30.75865],[115.84156,30.8312],[116.07776,30.96966],[115.87692,31.1423],[115.77461,31.10527],[115.69667,31.20604],[115.57823,31.14465],[115.36165,31.40228],[115.21018,31.58204],[114.83184,31.45092],[114.58465,31.71648],[114.18708,31.85452],[113.9859,31.75736],[113.84582,31.84314],[113.7284,32.08432],[113.75038,32.26855],[113.71467,32.43329],[113.41804,32.27784],[113.2505,32.39213],[112.55149,32.39851],[112.31323,32.32862],[112.0008,32.45864],[111.67293,32.62636],[111.57474,32.59455],[111.46488,32.73732],[111.29665,32.85622],[111.18163,33.10534],[110.9801,33.2605],[110.7003,33.09384],[110.55713,33.26653],[110.46958,33.17721],[110.22926,33.15882],[110.16197,33.20996],[110.03974,33.19158],[109.61299,33.2783],[109.42794,33.15479],[109.79324,33.0691],[109.75925,32.91273],[109.79049,32.87929],[109.86259,32.911],[110.02481,32.87482],[110.03391,32.86041],[110.13725,32.81238],[110.19218,32.62029],[110.04524,32.55144],[109.71393,32.61508],[109.55703,32.48543],[109.49901,32.3028],[109.62364,32.10177],[109.58381,31.72933],[109.72595,31.7083],[109.73522,31.58497],[110.12695,31.41108],[110.20042,31.15405],[110.1139,31.09527],[110.16952,30.9829],[110.06652,30.79729],[109.92919,30.91106],[109.53025,30.66154],[109.35138,30.48625],[109.30469,30.63584],[109.13749,30.52086],[109.08256,30.59418],[109.11861,30.6385],[109.04273,30.65637],[108.96995,30.62934],[108.83056,30.50311],[108.7413,30.4972],[108.6589,30.60098],[108.63796,30.53979],[108.56998,30.48211],[108.41789,30.49306],[108.40072,30.38827],[108.58131,30.25669],[108.5202,29.8722],[108.3657,29.84064]]]}},{"type":"Feature","properties":{"id":"AX"},"geometry":{"type":"Polygon","coordinates":[[[19.08191,60.19152],[20.5104,59.15546],[21.35468,59.67511],[21.02509,60.12142],[21.15143,60.54555],[20.96741,60.71528],[19.23413,60.61414],[19.08191,60.19152]]]}},{"type":"Feature","properties":{"id":"VG"},"geometry":{"type":"Polygon","coordinates":[[[-65.02435,18.73231],[-64.86049,18.39954],[-64.64067,18.36478],[-64.646,18.10286],[-63.95092,18.07976],[-63.90607,18.93262],[-64.62855,18.98678],[-65.02435,18.73231]]]}},{"type":"Feature","properties":{"id":"IN-MH"},"geometry":{"type":"Polygon","coordinates":[[[72.4768,20.16425],[73.34747,15.61245],[73.68598,15.72484],[73.80477,15.74401],[73.94691,15.74236],[73.97472,15.62799],[74.01592,15.60253],[74.11617,15.65411],[74.20165,15.77903],[74.34345,15.76417],[74.33967,15.85171],[74.46292,16.04746],[74.36576,16.03822],[74.41486,16.10255],[74.48249,16.09694],[74.50241,16.22489],[74.32662,16.31816],[74.35958,16.37746],[74.26483,16.51904],[74.37057,16.53748],[74.4691,16.65888],[74.6284,16.57762],[74.69398,16.7138],[74.90341,16.77101],[74.92744,16.93793],[75.07713,16.95566],[75.20553,16.83214],[75.27969,16.95697],[75.68206,16.95303],[75.58439,17.34883],[75.63846,17.49346],[75.69425,17.40994],[75.82609,17.42517],[75.892,17.36816],[75.92428,17.32835],[76.05525,17.36178],[76.07276,17.33081],[76.11928,17.37242],[76.18606,17.30459],[76.17439,17.34654],[76.24031,17.37652],[76.27326,17.32917],[76.31429,17.33998],[76.36545,17.30885],[76.40596,17.3526],[76.37592,17.37259],[76.37128,17.42943],[76.33043,17.4558],[76.34674,17.49468],[76.32974,17.57939],[76.40201,17.60426],[76.48424,17.71206],[76.5172,17.71925],[76.52166,17.75833],[76.56423,17.76928],[76.57402,17.70061],[76.61178,17.77631],[76.6892,17.68082],[76.69898,17.72792],[76.79134,17.82289],[76.7383,17.88269],[76.76868,17.90001],[76.88129,17.89413],[76.92043,17.91291],[76.91305,17.96779],[76.91596,18.03489],[76.94978,18.03848],[76.94532,18.08042],[76.9666,18.09592],[76.91871,18.12431],[76.98205,18.19355],[77.00351,18.16053],[77.03733,18.18189],[77.05355,18.15955],[77.11758,18.15775],[77.11741,18.19869],[77.17234,18.28453],[77.20822,18.2785],[77.24899,18.40714],[77.30718,18.44053],[77.324,18.41838],[77.36108,18.45225],[77.37138,18.40095],[77.41653,18.39427],[77.35988,18.30857],[77.42752,18.31118],[77.49343,18.26847],[77.5082,18.31183],[77.56785,18.28849],[77.60965,18.5522],[77.73994,18.55204],[77.74251,18.68267],[77.85684,18.81905],[77.94353,18.82604],[77.74251,19.02577],[77.84379,19.18359],[77.86422,19.3207],[78.00653,19.30158],[78.14695,19.22898],[78.19141,19.42903],[78.3011,19.46885],[78.26694,19.69544],[78.35861,19.75604],[78.27449,19.90476],[78.82793,19.75749],[78.84613,19.65778],[78.95839,19.66037],[78.94432,19.54943],[79.19048,19.46044],[79.23408,19.61219],[79.47578,19.49928],[79.63165,19.57887],[79.804,19.60054],[79.86991,19.5009],[79.93858,19.47792],[79.97051,19.35358],[79.96261,18.86145],[79.9094,18.82474],[80.11196,18.6869],[80.27503,18.72104],[80.36636,18.83091],[80.24825,18.95565],[80.55587,19.40313],[80.72341,19.27355],[80.8937,19.47306],[80.51467,19.92687],[80.54488,20.07528],[80.38284,20.23256],[80.60806,20.32273],[80.5799,20.6694],[80.54214,20.92681],[80.46935,20.92681],[80.42129,21.09923],[80.64308,21.25418],[80.65784,21.32967],[80.56377,21.36037],[80.40824,21.3738],[80.38696,21.49619],[80.28499,21.59742],[80.20946,21.6354],[80.13427,21.61115],[80.06732,21.55528],[79.95025,21.5572],[79.91489,21.52207],[79.75662,21.60062],[79.53861,21.54634],[79.4914,21.67306],[79.33021,21.70719],[79.14585,21.62583],[78.91136,21.59295],[78.9347,21.49268],[78.43654,21.50099],[78.37989,21.62296],[78.00842,21.41887],[77.56484,21.38019],[77.49893,21.76332],[77.23422,21.7158],[77.07733,21.72474],[76.90429,21.60349],[76.80198,21.59519],[76.65847,21.28201],[76.38656,21.07809],[76.17301,21.08386],[76.15653,21.2625],[75.96324,21.39618],[75.22304,21.40928],[75.11455,21.45306],[75.07575,21.55209],[74.91783,21.63062],[74.64317,21.65583],[74.51408,21.72314],[74.52609,21.90769],[74.43717,22.03282],[74.14947,21.95323],[73.83636,21.84684],[73.79379,21.62041],[73.85662,21.49939],[74.10724,21.5639],[74.21161,21.53101],[74.30911,21.5655],[74.33332,21.50945],[74.26328,21.46265],[74.11085,21.44492],[74.06776,21.48118],[74.05265,21.41983],[74.01712,21.42047],[73.94897,21.29737],[73.83619,21.26953],[73.83447,21.19337],[73.74315,21.16568],[73.74538,21.14279],[73.58573,21.15591],[73.7325,21.10163],[73.91635,20.92232],[73.93901,20.73588],[73.88854,20.72946],[73.7495,20.5624],[73.6695,20.56208],[73.44978,20.71726],[73.4333,20.2055],[73.29769,20.20453],[73.29048,20.1549],[73.23383,20.14266],[73.19675,20.05625],[72.98492,20.11784],[72.9808,20.21323],[72.8754,20.22869],[72.80021,20.12622],[72.4768,20.16425]]]}},{"type":"Feature","properties":{"id":"AQ"},"geometry":{"type":"Polygon","coordinates":[[[-180,-85],[180,-85],[180,-60],[-180,-60],[-180,-85]]]}},{"type":"Feature","properties":{"id":"AW"},"geometry":{"type":"Polygon","coordinates":[[[-70.34259,12.92535],[-70.24399,12.38063],[-69.4514,12.18025],[-69.5195,12.75292],[-70.34259,12.92535]]]}},{"type":"Feature","properties":{"id":"CX"},"geometry":{"type":"Polygon","coordinates":[[[105.29647,-10.80676],[105.9699,-10.81333],[105.97626,-10.18257],[105.30283,-10.176],[105.29647,-10.80676]]]}},{"type":"Feature","properties":{"id":"CC"},"geometry":{"type":"Polygon","coordinates":[[[96.53724,-12.46709],[97.24513,-12.47233],[97.25212,-11.57036],[96.54423,-11.5651],[96.53724,-12.46709]]]}},{"type":"Feature","properties":{"id":"TK"},"geometry":{"type":"Polygon","coordinates":[[[-174.18707,-7.54408],[-174.17993,-10.13616],[-167.75195,-10.12005],[-167.75329,-7.52784],[-174.18707,-7.54408]]]}},{"type":"Feature","properties":{"id":"CK"},"geometry":{"type":"Polygon","coordinates":[[[-167.75329,-7.52784],[-167.75195,-10.12005],[-167.73854,-14.92809],[-167.73129,-23.22266],[-156.46451,-23.21255],[-156.4957,-12.32002],[-156.50903,-7.4975],[-167.75329,-7.52784]]]}},{"type":"Feature","properties":{"id":"IN-PB"},"geometry":{"type":"Polygon","coordinates":[[[73.88992,29.96921],[74.52094,29.94303],[74.63115,29.90494],[74.69467,29.9695],[74.80144,29.99092],[74.87285,29.96296],[74.98821,29.86357],[75.09155,29.91774],[75.1015,29.81294],[75.14099,29.77063],[75.17806,29.83826],[75.20072,29.832],[75.22991,29.75603],[75.1966,29.69073],[75.15747,29.66747],[75.21858,29.61495],[75.22716,29.54598],[75.31162,29.58122],[75.27934,29.60779],[75.34046,29.66747],[75.33908,29.68984],[75.37891,29.71161],[75.39813,29.76378],[75.44448,29.80788],[75.60173,29.7456],[75.65288,29.77987],[75.69339,29.75573],[75.70232,29.81145],[75.83003,29.81354],[75.86162,29.75424],[75.9423,29.73069],[76.04049,29.74798],[76.08169,29.80877],[76.11602,29.80222],[76.24168,29.85761],[76.2355,29.88649],[76.18057,29.88947],[76.2276,30.12656],[76.43394,30.15195],[76.44664,30.10385],[76.50089,30.0783],[76.60903,30.07978],[76.6238,30.14497],[76.63873,30.20493],[76.56045,30.26381],[76.73375,30.36458],[76.69864,30.39257],[76.75769,30.43727],[76.80747,30.41196],[76.83151,30.43328],[76.84885,30.40604],[76.88438,30.38516],[76.877,30.36265],[76.88833,30.35569],[76.92987,30.39686],[76.91579,30.40145],[76.88798,30.44038],[76.92026,30.52618],[76.89571,30.53579],[76.89931,30.55206],[76.91193,30.60504],[76.87133,30.63997],[76.85846,30.6416],[76.85923,30.65762],[76.84902,30.66693],[76.83794,30.65755],[76.81949,30.66139],[76.81468,30.68693],[76.8025,30.67527],[76.78945,30.67054],[76.76095,30.68619],[76.7513,30.68512],[76.73894,30.70136],[76.73014,30.72479],[76.71967,30.73202],[76.71306,30.7419],[76.69014,30.74699],[76.69066,30.75968],[76.70808,30.77045],[76.72285,30.77067],[76.73864,30.7908],[76.75975,30.79928],[76.777,30.78483],[76.76911,30.77683],[76.78104,30.77392],[76.79632,30.78623],[76.80413,30.779],[76.79254,30.76647],[76.82825,30.76411],[76.7704,30.9087],[76.61384,31.00144],[76.64817,31.21015],[76.50672,31.27913],[76.37283,31.43569],[76.31996,31.40082],[76.34056,31.34278],[76.18743,31.28999],[76.16546,31.39526],[75.92822,31.80522],[75.96977,31.81281],[75.89904,31.94808],[75.71948,32.06541],[75.58284,32.07384],[75.62885,32.10148],[75.64756,32.24707],[75.7521,32.28364],[75.93921,32.41474],[75.82936,32.52664],[75.78506,32.47095],[75.57083,32.36836],[75.49941,32.28074],[75.28259,32.36556],[75.38046,32.26836],[75.25649,32.10187],[75.00793,32.03786],[74.9269,32.0658],[74.86236,32.04485],[74.79919,31.95983],[74.58907,31.87824],[74.47771,31.72227],[74.57498,31.60382],[74.61517,31.55698],[74.59319,31.50197],[74.64713,31.45605],[74.59773,31.4136],[74.53223,31.30321],[74.51629,31.13829],[74.56023,31.08303],[74.60281,31.10419],[74.60006,31.13711],[74.6852,31.12771],[74.67971,31.05479],[74.5616,31.04153],[73.88993,30.36305],[73.95736,30.28466],[73.97225,30.19829],[73.88992,29.96921]]]}},{"type":"Feature","properties":{"id":"FK"},"geometry":{"type":"Polygon","coordinates":[[[-62.78369,-53.1401],[-58.84651,-53.93403],[-55.76919,-51.15168],[-62.3754,-50.36819],[-62.78369,-53.1401]]]}},{"type":"Feature","properties":{"id":"GS"},"geometry":{"type":"Polygon","coordinates":[[[-43.57991,-52.56305],[-40.68557,-57.40649],[-26.52505,-59.90465],[-23.50385,-54.792],[-43.57991,-52.56305]]]}},{"type":"Feature","properties":{"id":"GU"},"geometry":{"type":"Polygon","coordinates":[[[143.82485,13.92273],[144.61642,12.82462],[146.25931,13.85876],[143.82485,13.92273]]]}},{"type":"Feature","properties":{"id":"HM"},"geometry":{"type":"Polygon","coordinates":[[[71.08716,-53.87687],[75.44182,-53.99822],[72.87012,-51.48322],[71.08716,-53.87687]]]}},{"type":"Feature","properties":{"id":"HK"},"geometry":{"type":"Polygon","coordinates":[[[113.81621,22.2163],[113.83338,22.1826],[113.92195,22.13873],[114.50148,22.15017],[114.44998,22.55977],[114.25154,22.55977],[114.22888,22.5436],[114.22185,22.55343],[114.20655,22.55706],[114.18338,22.55444],[114.17247,22.55944],[114.1597,22.56041],[114.15123,22.55163],[114.1482,22.54091],[114.13823,22.54319],[114.12665,22.54003],[114.11656,22.53415],[114.11181,22.52878],[114.1034,22.5352],[114.09692,22.53435],[114.09048,22.53716],[114.08606,22.53276],[114.07817,22.52997],[114.07267,22.51855],[114.06272,22.51617],[114.05729,22.51104],[114.05438,22.5026],[114.03113,22.5065],[113.86771,22.42972],[113.81621,22.2163]]]}},{"type":"Feature","properties":{"id":"PR"},"geometry":{"type":"Polygon","coordinates":[[[-68.20301,17.83927],[-65.27974,17.56928],[-65.02435,18.73231],[-67.99519,18.97186],[-68.20301,17.83927]]]}},{"type":"Feature","properties":{"id":"MO"},"geometry":{"type":"Polygon","coordinates":[[[113.52659,22.18271],[113.54093,22.15497],[113.54942,22.14519],[113.54839,22.10909],[113.57191,22.07696],[113.63011,22.10782],[113.60504,22.20464],[113.57123,22.20416],[113.56865,22.20973],[113.5508,22.21672],[113.54333,22.21688],[113.54093,22.21314],[113.53593,22.2137],[113.53301,22.21235],[113.53552,22.20607],[113.52659,22.18271]]]}},{"type":"Feature","properties":{"id":"CN-CQ"},"geometry":{"type":"Polygon","coordinates":[[[105.29193,29.58122],[105.37261,29.42285],[105.44128,29.40131],[105.43888,29.31783],[105.65036,29.24357],[105.7101,29.30047],[105.74958,29.01894],[105.90408,28.9054],[106.0033,28.97811],[106.26113,28.83715],[106.36756,28.52662],[106.47743,28.53567],[106.51245,28.67673],[106.44927,28.78631],[106.46781,28.84106],[106.5715,28.70624],[106.65183,28.66649],[106.55914,28.51153],[106.70677,28.44997],[106.77406,28.55919],[106.76238,28.62732],[106.81251,28.58934],[106.86881,28.62611],[106.81182,28.7514],[106.88804,28.80436],[106.96838,28.76585],[106.98211,28.86031],[107.1833,28.88977],[107.26089,28.76103],[107.44148,28.93845],[107.36457,29.00333],[107.4044,29.19472],[107.67219,29.14916],[107.75184,29.21031],[107.84042,28.96309],[107.88574,29.01114],[108.00899,29.04206],[108.0622,29.09037],[108.14323,29.05737],[108.18477,29.07207],[108.22837,29.02405],[108.28708,29.09577],[108.38012,28.80647],[108.33103,28.68637],[108.45874,28.62852],[108.52981,28.65112],[108.63109,28.6457],[108.60877,28.54924],[108.56552,28.5423],[108.60603,28.44092],[108.56414,28.37931],[108.60706,28.32523],[108.65959,28.3343],[108.68911,28.40136],[108.63178,28.46506],[108.70731,28.50098],[108.77872,28.42492],[108.76739,28.31465],[108.72001,28.29228],[108.75091,28.2073],[108.88137,28.22182],[108.99707,28.15828],[109.02385,28.20972],[109.08531,28.18884],[109.15981,28.42597],[109.17054,28.4582],[109.20075,28.48642],[109.22753,28.48317],[109.24324,28.4969],[109.26898,28.50437],[109.26727,28.51787],[109.27645,28.52202],[109.29121,28.57268],[109.31576,28.58595],[109.29671,28.62913],[109.19466,28.60275],[109.18556,28.63184],[109.29885,28.73018],[109.23688,28.78255],[109.23208,28.87985],[109.31877,29.05827],[109.2346,29.11829],[109.22916,29.12217],[109.10745,29.21855],[109.10419,29.36691],[109.05029,29.4055],[108.97647,29.33085],[108.90918,29.32651],[108.93836,29.43601],[108.86455,29.46411],[108.90918,29.60331],[108.68499,29.70132],[108.67641,29.85255],[108.6098,29.86863],[108.4917,29.71966],[108.3657,29.84064],[108.5202,29.8722],[108.58131,30.25669],[108.40072,30.38827],[108.41789,30.49306],[108.56998,30.48211],[108.63796,30.53979],[108.6589,30.60098],[108.7413,30.4972],[108.83056,30.50311],[108.96995,30.62934],[109.04273,30.65637],[109.11861,30.6385],[109.08256,30.59418],[109.13749,30.52086],[109.30469,30.63584],[109.35138,30.48625],[109.53025,30.66154],[109.92919,30.91106],[110.06652,30.79729],[110.16952,30.9829],[110.1139,31.09527],[110.20042,31.15405],[110.12695,31.41108],[109.73522,31.58497],[109.72595,31.7083],[109.58381,31.72933],[109.27619,31.71823],[109.20066,31.85248],[108.50234,32.20641],[108.27369,31.92885],[108.54011,31.67559],[108.02032,31.24803],[108.07182,31.18049],[107.92282,30.92578],[107.99114,30.91076],[107.84866,30.79405],[107.69657,30.876],[107.63683,30.81233],[107.48508,30.84476],[107.42637,30.7318],[107.50431,30.63732],[107.19291,30.18727],[107.03155,30.04532],[106.96941,30.08484],[106.76582,30.01738],[106.54643,30.32843],[106.21067,30.18134],[106.16397,30.30413],[105.79627,30.44393],[105.68778,30.26618],[105.61431,30.26381],[105.59062,30.11216],[105.75302,30.01619],[105.70667,29.83796],[105.60127,29.83707],[105.45707,29.67612],[105.29193,29.58122]]]}},{"type":"Feature","properties":{"id":"YT"},"geometry":{"type":"Polygon","coordinates":[[[44.75722,-12.58368],[44.82644,-13.30845],[45.54824,-13.22353],[45.45962,-12.30345],[44.75722,-12.58368]]]}},{"type":"Feature","properties":{"id":"NU"},"geometry":{"type":"Polygon","coordinates":[[[-173.13438,-14.94228],[-173.11048,-23.23027],[-167.73129,-23.22266],[-167.73854,-14.92809],[-171.14262,-14.93704],[-173.13438,-14.94228]]]}},{"type":"Feature","properties":{"id":"NF"},"geometry":{"type":"Polygon","coordinates":[[[165.46901,-28.32101],[169.35326,-30.60259],[169.6687,-29.09191],[166.75333,-27.25416],[165.46901,-28.32101]]]}},{"type":"Feature","properties":{"id":"CN-JS"},"geometry":{"type":"Polygon","coordinates":[[[116.37405,34.64676],[116.95014,34.39841],[117.02567,34.15556],[117.50907,34.06176],[117.74322,33.89264],[117.71026,33.74718],[118.18405,33.74546],[117.96775,33.33052],[118.03024,33.12777],[118.20121,33.2203],[118.22456,32.9332],[118.37425,32.72144],[118.7059,32.71624],[118.85559,32.95855],[119.17762,32.83286],[119.22225,32.60756],[119.08218,32.45531],[118.89747,32.58905],[118.54659,32.58442],[118.68049,32.45647],[118.6956,32.36198],[118.64444,32.21657],[118.50299,32.19479],[118.50128,32.14393],[118.39004,32.02612],[118.35605,31.93788],[118.49716,31.84373],[118.47587,31.78246],[118.69354,31.7194],[118.64204,31.64812],[118.71894,31.62415],[118.73954,31.67851],[118.8652,31.62415],[118.86314,31.43745],[118.71482,31.29967],[118.78761,31.23394],[119.07257,31.23394],[119.17968,31.29908],[119.27238,31.25272],[119.35134,31.30143],[119.36782,31.19107],[119.50378,31.1611],[119.57656,31.11174],[119.63149,31.1329],[119.91439,31.17051],[120.36208,30.97054],[120.35007,30.88896],[120.42835,30.91459],[120.49873,30.75953],[120.5801,30.85566],[120.64739,30.85095],[120.70747,30.88572],[120.67485,30.957],[120.73459,30.96289],[120.7703,30.9988],[120.84754,30.99173],[120.89424,31.01822],[120.89836,31.09028],[120.85475,31.10938],[120.88325,31.14083],[121.03671,31.14171],[121.07276,31.16345],[121.05903,31.23658],[121.06006,31.27356],[121.11808,31.28529],[121.1495,31.27752],[121.15722,31.28515],[121.14057,31.31038],[121.12581,31.30348],[121.11173,31.37459],[121.15447,31.41108],[121.14246,31.44492],[121.23722,31.49704],[121.25335,31.47933],[121.3821,31.54723],[121.09954,31.75853],[121.30554,31.88338],[121.44286,31.76086],[121.76147,31.63818],[122.29378,31.76513],[122.80525,33.30571],[119.30259,35.07833],[118.88442,35.04349],[118.76495,34.73822],[118.51226,34.69194],[118.42849,34.44655],[118.17718,34.36837],[118.14559,34.54389],[117.93411,34.68404],[117.78991,34.51447],[117.15957,34.52692],[116.96456,34.88367],[116.43859,34.89944],[116.37405,34.64676]]]}},{"type":"Feature","properties":{"id":"MF"},"geometry":{"type":"Polygon","coordinates":[[[-63.35989,18.06012],[-63.33064,17.9615],[-63.13584,18.0541],[-63.11096,18.05368],[-63.09686,18.04608],[-63.07759,18.04943],[-63.0579,18.06614],[-63.04039,18.05619],[-63.02323,18.05757],[-62.93924,18.02904],[-62.75637,18.13489],[-62.86666,18.19278],[-63.35989,18.06012]]]}},{"type":"Feature","properties":{"id":"BL"},"geometry":{"type":"Polygon","coordinates":[[[-63.07669,17.79659],[-62.76692,17.64353],[-62.54836,17.8636],[-62.75637,18.13489],[-62.93924,18.02904],[-63.07669,17.79659]]]}},{"type":"Feature","properties":{"id":"AC"},"geometry":{"type":"Polygon","coordinates":[[[-14.91926,-6.63386],[-14.82771,-8.70814],[-13.33271,-8.07391],[-14.91926,-6.63386]]]}},{"type":"Feature","properties":{"id":"IC"},"geometry":{"type":"Polygon","coordinates":[[[-18.8556,26.96222],[-14.43883,27.02969],[-12.42686,29.61659],[-14.33337,30.94071],[-15.92339,29.50503],[-18.67893,29.62861],[-18.8556,26.96222]]]}},{"type":"Feature","properties":{"id":"ES-CE"},"geometry":{"type":"Polygon","coordinates":[[[-5.38491,35.92591],[-5.37338,35.88417],[-5.35844,35.87375],[-5.34379,35.8711],[-5.27056,35.88794],[-5.27635,35.91222],[-5.38491,35.92591]]]}},{"type":"Feature","properties":{"id":"ES-ML"},"geometry":{"type":"Polygon","coordinates":[[[-2.97035,35.28852],[-2.96507,35.28801],[-2.96826,35.28296],[-2.96516,35.27967],[-2.95431,35.2728],[-2.95065,35.26576],[-2.93893,35.26737],[-2.92674,35.27313],[-2.92181,35.28599],[-2.92224,35.3401],[-2.96038,35.31609],[-2.96648,35.30475],[-2.96978,35.29459],[-2.97035,35.28852]]]}},{"type":"Feature","properties":{"id":"IN-AP"},"geometry":{"type":"Polygon","coordinates":[[[76.76336,14.98442],[76.86996,14.97082],[76.86309,14.8401],[76.76628,14.67925],[76.80335,14.53257],[76.98205,14.48288],[76.89125,14.3966],[76.93862,14.24624],[77.04952,14.24774],[77.0866,14.191],[77.13466,14.33923],[77.27045,14.33241],[77.3767,14.21113],[77.4282,14.19998],[77.3628,14.34954],[77.48502,14.29332],[77.51472,14.15754],[77.38924,14.17419],[77.40949,14.12026],[77.33791,14.03967],[77.43215,13.98237],[77.45017,13.94772],[77.41172,13.88791],[77.35765,13.9339],[77.31954,14.03084],[77.15234,13.99187],[77.13415,14.03684],[77.0284,14.05432],[77.02651,14.16736],[76.95339,14.19233],[76.88112,14.14156],[76.98326,14.08596],[76.94463,14.05233],[76.93948,14.01719],[76.98772,14.0002],[77.05141,13.9214],[76.97862,13.82941],[77.00265,13.72837],[77.03081,13.78256],[77.05278,13.76739],[77.18393,13.76189],[77.1302,13.84891],[77.16556,13.9034],[77.23766,13.90824],[77.26221,13.85341],[77.41945,13.84541],[77.45206,13.81057],[77.47369,13.76289],[77.45326,13.70236],[77.48022,13.67751],[77.49206,13.71303],[77.52983,13.69585],[77.5245,13.75839],[77.56227,13.72721],[77.6711,13.78773],[77.71934,13.74021],[77.72209,13.7889],[77.83229,13.86158],[77.81839,13.93523],[77.9516,13.9344],[78.06146,13.88791],[78.12635,13.86208],[78.11485,13.76456],[78.11622,13.65716],[78.19965,13.64348],[78.19879,13.58592],[78.40461,13.59343],[78.37818,13.50748],[78.38058,13.40581],[78.36547,13.3634],[78.57439,13.31512],[78.57181,13.18061],[78.47637,12.98699],[78.40908,12.94517],[78.43809,12.92743],[78.46744,12.90334],[78.46813,12.86971],[78.39054,12.90652],[78.35706,12.93965],[78.315,12.85966],[78.26076,12.86469],[78.23295,12.76526],[78.2151,12.68991],[78.46401,12.61454],[78.63292,12.97143],[78.84681,13.07613],[79.14997,13.00422],[79.2152,13.13063],[79.40025,13.142],[79.36832,13.30711],[79.53414,13.30944],[79.77996,13.21254],[79.92484,13.33884],[80.02784,13.52718],[80.2153,13.48512],[80.29632,13.37626],[80.32104,13.44372],[80.67259,13.46443],[82.9708,16.27499],[82.19078,16.71874],[82.21618,16.76378],[83.0072,16.31915],[85.11932,18.86211],[84.76158,19.07055],[84.70287,19.15068],[84.59335,19.11662],[84.65961,19.06925],[84.59369,19.02317],[84.50408,19.04394],[84.31594,18.78411],[84.2284,18.78883],[84.08128,18.74478],[84.03991,18.79581],[83.88748,18.80296],[83.65058,19.12311],[83.33816,19.01084],[83.37936,18.83481],[83.11019,18.77176],[83.08822,18.54081],[82.96737,18.33692],[82.82592,18.43727],[82.63366,18.23522],[82.42835,18.49198],[82.35351,18.14193],[82.25532,17.98199],[82.04383,18.06622],[81.77398,17.88596],[81.40594,17.3585],[80.91499,17.20081],[80.83671,17.02494],[80.68771,17.06794],[80.61733,17.13226],[80.50369,17.1083],[80.4467,17.01772],[80.38867,17.08139],[80.36189,16.96683],[80.57544,16.91855],[80.58711,16.772],[80.45871,16.7881],[80.45391,16.81702],[80.40138,16.85613],[80.31864,16.87387],[80.26027,17.0082],[80.18199,17.04628],[80.04432,16.96256],[79.99248,16.8604],[80.0735,16.81242],[80.02544,16.71249],[79.94476,16.62731],[79.79507,16.72137],[79.31304,16.57302],[79.21485,16.48251],[79.21623,16.21665],[78.68408,16.04581],[78.41766,16.08012],[78.12,15.82892],[78.03108,15.90421],[77.65754,15.88869],[77.51197,15.92864],[77.44039,15.94548],[77.27113,15.96132],[77.11818,15.94036],[77.03613,15.856],[77.09278,15.71625],[76.97227,15.50992],[77.03201,15.43614],[77.04145,15.37292],[77.16281,15.29279],[77.17037,15.17619],[77.1132,15.02985],[76.98308,15.00945],[76.80044,15.09681],[76.76336,14.98442]]]}},{"type":"Feature","properties":{"id":"DG"},"geometry":{"type":"Polygon","coordinates":[[[72.0768,-6.94304],[72.09053,-7.71938],[73.19616,-7.72081],[73.19718,-6.94577],[72.0768,-6.94304]]]}},{"type":"Feature","properties":{"id":"TA"},"geometry":{"type":"Polygon","coordinates":[[[-13.48367,-36.6746],[-13.41694,-37.88844],[-11.48092,-37.8367],[-11.55782,-36.60319],[-13.48367,-36.6746]]]}},{"type":"Feature","properties":{"id":"AU-WA"},"geometry":{"type":"Polygon","coordinates":[[[99.6403,-26.70736],[129,-43.08851],[129.00183,-25.99861],[129.00057,-25.99861],[129,-14.42902],[127.55165,-9.05052],[125.68138,-9.85176],[122.91521,-11.65621],[99.6403,-26.70736]]]}},{"type":"Feature","properties":{"id":"AU-NT"},"geometry":{"type":"Polygon","coordinates":[[[127.55165,-9.05052],[129,-14.42902],[129.00057,-25.99861],[129.00183,-25.99861],[137.99993,-25.99819],[138.00067,-16.23841],[139.41724,-8.97063],[127.55165,-9.05052]]]}},{"type":"Feature","properties":{"id":"AU-SA"},"geometry":{"type":"Polygon","coordinates":[[[129,-43.08851],[137.66184,-47.26522],[140.64335,-39.23791],[140.96369,-38.2743],[140.9638,-33.98067],[141.00268,-34.02172],[140.99934,-28.99903],[141.00013,-26.00101],[137.99993,-25.99819],[129.00183,-25.99861],[129,-43.08851]]]}},{"type":"Feature","properties":{"id":"AU-TAS"},"geometry":{"type":"Polygon","coordinates":[[[137.66184,-47.26522],[158.89388,-55.66823],[159.92772,-54.25538],[152.57175,-39.16128],[140.64335,-39.23791],[137.66184,-47.26522]]]}},{"type":"Feature","properties":{"id":"AU-VIC"},"geometry":{"type":"Polygon","coordinates":[[[140.64335,-39.23791],[152.57175,-39.16128],[150.19768,-37.59458],[148.19405,-36.79602],[148.10753,-36.79272],[148.20366,-36.59782],[148.04573,-36.39248],[147.99217,-36.0457],[147.71202,-35.94237],[147.39616,-35.94681],[147.31926,-36.05458],[147.10503,-36.00683],[147.04185,-36.09898],[146.85097,-36.08788],[146.59966,-35.97349],[146.42387,-35.96794],[146.36894,-36.03571],[145.80589,-35.98461],[145.50789,-35.80772],[145.34447,-35.86005],[145.1165,-35.81774],[144.9778,-35.86673],[144.94896,-36.05236],[144.74022,-36.11895],[144.08241,-35.57238],[143.56743,-35.33634],[143.57155,-35.20741],[143.39027,-35.18047],[143.3271,-34.99618],[143.34221,-34.79344],[142.76268,-34.56871],[142.61711,-34.77765],[142.50999,-34.74267],[142.37404,-34.34563],[142.24495,-34.3014],[142.22023,-34.18334],[142.0211,-34.12651],[141.71349,-34.0924],[141.5377,-34.18902],[141.00268,-34.02172],[140.9638,-33.98067],[140.96369,-38.2743],[140.64335,-39.23791]]]}},{"type":"Feature","properties":{"id":"AU-QLD"},"geometry":{"type":"Polygon","coordinates":[[[137.99993,-25.99819],[141.00013,-26.00101],[140.99934,-28.99903],[148.95806,-28.99906],[149.19248,-28.77479],[149.37788,-28.68628],[149.48705,-28.58202],[149.58044,-28.57056],[149.67519,-28.62723],[150.30004,-28.53558],[150.43737,-28.66098],[150.74499,-28.63446],[151.27508,-28.94017],[151.30392,-29.15627],[151.39318,-29.17186],[151.55248,-28.94858],[151.72552,-28.86683],[151.7777,-28.9606],[152.01391,-28.89449],[152.06472,-28.69351],[151.94662,-28.54282],[152.49731,-28.25168],[152.57696,-28.3327],[152.60442,-28.27466],[152.74725,-28.35929],[152.87222,-28.30852],[153.10293,-28.35445],[153.18121,-28.25289],[153.36385,-28.242],[153.47715,-28.15789],[153.53414,-28.17635],[153.55096,-28.16364],[155.3142,-27.34698],[153.5901,-24.72709],[149.03078,-19.80828],[144.30183,-9.48146],[142.81927,-9.31709],[142.5723,-9.35994],[142.31447,-9.24611],[142.23304,-9.19253],[142.1462,-9.19923],[142.0953,-9.23534],[142.0601,-9.56571],[140.88922,-9.34945],[139.41724,-8.97063],[138.00067,-16.23841],[137.99993,-25.99819]]]}},{"type":"Feature","properties":{"id":"AU-ACT"},"geometry":{"type":"Polygon","coordinates":[[[148.76247,-35.49504],[148.78891,-35.69995],[148.85723,-35.76043],[148.8768,-35.715],[148.89431,-35.75095],[148.89602,-35.82504],[148.96194,-35.8971],[149.04811,-35.91684],[149.09824,-35.81223],[149.09549,-35.6411],[149.07936,-35.58193],[149.14219,-35.59337],[149.12983,-35.55288],[149.15283,-35.50566],[149.13429,-35.45338],[149.2057,-35.34732],[149.25136,-35.33024],[149.33719,-35.33976],[149.35058,-35.3518],[149.39796,-35.32435],[149.39418,-35.30362],[149.23488,-35.24336],[149.2469,-35.2285],[149.18956,-35.20157],[149.19746,-35.18502],[149.12159,-35.1241],[148.80951,-35.30698],[148.76247,-35.49504]]]}},{"type":"Feature","properties":{"id":"IN-KA"},"geometry":{"type":"Polygon","coordinates":[[[73.87481,14.75496],[74.66307,12.69394],[74.86049,12.76057],[75.01327,12.79137],[74.98323,12.73998],[75.0531,12.71804],[75.04108,12.67484],[75.06872,12.66228],[75.08777,12.70029],[75.11438,12.67752],[75.16124,12.67969],[75.14579,12.63648],[75.19798,12.6132],[75.23471,12.56662],[75.28106,12.61856],[75.33393,12.57534],[75.27214,12.5502],[75.34698,12.46105],[75.39882,12.50161],[75.43659,12.47144],[75.37067,12.45602],[75.43109,12.31249],[75.49392,12.29103],[75.54302,12.20279],[75.78987,12.08296],[75.86059,11.95502],[76.00307,11.93185],[76.11259,11.97887],[76.11757,11.85105],[76.18949,11.87608],[76.42948,11.66568],[76.539,11.69123],[76.61281,11.60717],[76.85623,11.59506],[76.84249,11.66938],[76.91013,11.79308],[76.97193,11.77628],[76.98772,11.81291],[77.12059,11.71863],[77.25465,11.81241],[77.42906,11.76199],[77.46665,11.84887],[77.49704,11.9426],[77.67917,11.94898],[77.72724,12.05409],[77.77925,12.112],[77.725,12.17963],[77.45738,12.20681],[77.48931,12.2766],[77.61806,12.36649],[77.63523,12.49088],[77.58853,12.51803],[77.60089,12.66579],[77.67514,12.68363],[77.67943,12.65625],[77.71265,12.66395],[77.71196,12.6817],[77.74114,12.67065],[77.73994,12.69962],[77.76329,12.6956],[77.77582,12.72273],[77.76277,12.72658],[77.7662,12.73663],[77.79384,12.74768],[77.78062,12.76928],[77.81032,12.82987],[77.79247,12.84092],[77.83281,12.86151],[77.93683,12.88192],[77.91975,12.82828],[77.94782,12.8354],[77.95349,12.85966],[77.97005,12.83105],[78.00215,12.80359],[78.03322,12.85406],[78.08678,12.83146],[78.12309,12.76861],[78.23295,12.76526],[78.26076,12.86469],[78.315,12.85966],[78.35706,12.93965],[78.39054,12.90652],[78.46813,12.86971],[78.46744,12.90334],[78.43809,12.92743],[78.40908,12.94517],[78.47637,12.98699],[78.57181,13.18061],[78.57439,13.31512],[78.36547,13.3634],[78.38058,13.40581],[78.37818,13.50748],[78.40461,13.59343],[78.19879,13.58592],[78.19965,13.64348],[78.11622,13.65716],[78.11485,13.76456],[78.12635,13.86208],[78.06146,13.88791],[77.9516,13.9344],[77.81839,13.93523],[77.83229,13.86158],[77.72209,13.7889],[77.71934,13.74021],[77.6711,13.78773],[77.56227,13.72721],[77.5245,13.75839],[77.52983,13.69585],[77.49206,13.71303],[77.48022,13.67751],[77.45326,13.70236],[77.47369,13.76289],[77.45206,13.81057],[77.41945,13.84541],[77.26221,13.85341],[77.23766,13.90824],[77.16556,13.9034],[77.1302,13.84891],[77.18393,13.76189],[77.05278,13.76739],[77.03081,13.78256],[77.00265,13.72837],[76.97862,13.82941],[77.05141,13.9214],[76.98772,14.0002],[76.93948,14.01719],[76.94463,14.05233],[76.98326,14.08596],[76.88112,14.14156],[76.95339,14.19233],[77.02651,14.16736],[77.0284,14.05432],[77.13415,14.03684],[77.15234,13.99187],[77.31954,14.03084],[77.35765,13.9339],[77.41172,13.88791],[77.45017,13.94772],[77.43215,13.98237],[77.33791,14.03967],[77.40949,14.12026],[77.38924,14.17419],[77.51472,14.15754],[77.48502,14.29332],[77.3628,14.34954],[77.4282,14.19998],[77.3767,14.21113],[77.27045,14.33241],[77.13466,14.33923],[77.0866,14.191],[77.04952,14.24774],[76.93862,14.24624],[76.89125,14.3966],[76.98205,14.48288],[76.80335,14.53257],[76.76628,14.67925],[76.86309,14.8401],[76.86996,14.97082],[76.76336,14.98442],[76.80044,15.09681],[76.98308,15.00945],[77.1132,15.02985],[77.17037,15.17619],[77.16281,15.29279],[77.04145,15.37292],[77.03201,15.43614],[76.97227,15.50992],[77.09278,15.71625],[77.03613,15.856],[77.11818,15.94036],[77.27113,15.96132],[77.44039,15.94548],[77.51197,15.92864],[77.49893,16.26906],[77.60364,16.29657],[77.59523,16.34336],[77.48451,16.38223],[77.28847,16.40595],[77.2344,16.47658],[77.32366,16.48942],[77.37636,16.48481],[77.47198,16.587],[77.47095,16.71216],[77.427,16.72252],[77.44279,16.78712],[77.47489,16.77956],[77.45532,16.92068],[77.50099,17.01657],[77.46356,17.11454],[77.36623,17.15883],[77.46202,17.28607],[77.45155,17.3721],[77.52244,17.35178],[77.53583,17.44007],[77.62716,17.44335],[77.67608,17.52751],[77.52571,17.57693],[77.44005,17.57693],[77.45841,17.70568],[77.57823,17.74378],[77.50837,17.79298],[77.66166,17.9686],[77.56296,18.04925],[77.61377,18.10033],[77.56785,18.28849],[77.5082,18.31183],[77.49343,18.26847],[77.42752,18.31118],[77.35988,18.30857],[77.41653,18.39427],[77.37138,18.40095],[77.36108,18.45225],[77.324,18.41838],[77.30718,18.44053],[77.24899,18.40714],[77.20822,18.2785],[77.17234,18.28453],[77.11741,18.19869],[77.11758,18.15775],[77.05355,18.15955],[77.03733,18.18189],[77.00351,18.16053],[76.98205,18.19355],[76.91871,18.12431],[76.9666,18.09592],[76.94532,18.08042],[76.94978,18.03848],[76.91596,18.03489],[76.91305,17.96779],[76.92043,17.91291],[76.88129,17.89413],[76.76868,17.90001],[76.7383,17.88269],[76.79134,17.82289],[76.69898,17.72792],[76.6892,17.68082],[76.61178,17.77631],[76.57402,17.70061],[76.56423,17.76928],[76.52166,17.75833],[76.5172,17.71925],[76.48424,17.71206],[76.40201,17.60426],[76.32974,17.57939],[76.34674,17.49468],[76.33043,17.4558],[76.37128,17.42943],[76.37592,17.37259],[76.40596,17.3526],[76.36545,17.30885],[76.31429,17.33998],[76.27326,17.32917],[76.24031,17.37652],[76.17439,17.34654],[76.18606,17.30459],[76.11928,17.37242],[76.07276,17.33081],[76.05525,17.36178],[75.92428,17.32835],[75.892,17.36816],[75.82609,17.42517],[75.69425,17.40994],[75.63846,17.49346],[75.58439,17.34883],[75.68206,16.95303],[75.27969,16.95697],[75.20553,16.83214],[75.07713,16.95566],[74.92744,16.93793],[74.90341,16.77101],[74.69398,16.7138],[74.6284,16.57762],[74.4691,16.65888],[74.37057,16.53748],[74.26483,16.51904],[74.35958,16.37746],[74.32662,16.31816],[74.50241,16.22489],[74.48249,16.09694],[74.41486,16.10255],[74.36576,16.03822],[74.46292,16.04746],[74.33967,15.85171],[74.34345,15.76417],[74.20165,15.77903],[74.11617,15.65411],[74.25659,15.64551],[74.24903,15.49206],[74.33486,15.28352],[74.25075,15.25371],[74.31838,15.1805],[74.2741,15.09996],[74.29195,15.02869],[74.20543,14.92819],[74.16217,14.94976],[73.87481,14.75496]]]}},{"type":"Feature","properties":{"id":"NL-BQ3"},"geometry":{"type":"Polygon","coordinates":[[[-63.22932,17.32592],[-63.11114,17.23125],[-62.76692,17.64353],[-63.07669,17.79659],[-63.22932,17.32592]]]}},{"type":"Feature","properties":{"id":"CA-BC"},"geometry":{"type":"Polygon","coordinates":[[[-139.05365,59.99655],[-138.71149,59.90728],[-138.62145,59.76431],[-137.60623,59.24465],[-137.4925,58.89415],[-136.82619,59.16198],[-136.52365,59.16752],[-136.47323,59.46617],[-136.33727,59.44466],[-136.22381,59.55526],[-136.31566,59.59083],[-135.48007,59.79937],[-135.03069,59.56208],[-135.00267,59.28745],[-134.7047,59.2458],[-134.55699,59.1297],[-134.48059,59.13231],[-134.27175,58.8634],[-133.84645,58.73543],[-133.38523,58.42773],[-131.8271,56.62247],[-130.77769,56.36185],[-130.33965,56.10849],[-130.10173,56.12178],[-130.00093,56.00325],[-130.00857,55.91344],[-130.15373,55.74895],[-129.97513,55.28029],[-130.08035,55.21556],[-130.18765,55.07744],[-130.27203,54.97174],[-130.44184,54.85377],[-130.64499,54.76912],[-130.61931,54.70835],[-133.92876,54.62289],[-133.36909,48.51151],[-125.03842,48.53282],[-123.50039,48.21223],[-123.15614,48.35395],[-123.26565,48.6959],[-123.0093,48.76586],[-123.0093,48.83186],[-123.32163,49.00419],[-117.03266,49.00056],[-116.04938,48.99999],[-114.0683,48.99885],[-114.74352,49.57064],[-114.70812,50.29534],[-120.00143,53.79861],[-120.00135,60.00043],[-123.86203,59.99964],[-139.05365,59.99655]]]}},{"type":"Feature","properties":{"id":"CA-AB"},"geometry":{"type":"Polygon","coordinates":[[[-120.00143,53.79861],[-114.70812,50.29534],[-114.74352,49.57064],[-114.0683,48.99885],[-110.0051,48.99901],[-110.00637,59.99944],[-120.00135,60.00043],[-120.00143,53.79861]]]}},{"type":"Feature","properties":{"id":"CA-SK"},"geometry":{"type":"Polygon","coordinates":[[[-110.00637,59.99944],[-110.0051,48.99901],[-104.05004,48.99925],[-101.36198,48.99935],[-101.98961,55.81762],[-102.00759,59.99922],[-110.00637,59.99944]]]}},{"type":"Feature","properties":{"id":"CA-MB"},"geometry":{"type":"Polygon","coordinates":[[[-102.00759,59.99922],[-101.98961,55.81762],[-101.36198,48.99935],[-97.24024,48.99952],[-95.15355,48.9996],[-95.15357,49.384],[-95.15374,52.84191],[-88.99084,56.84662],[-94.78348,59.99917],[-102.00759,59.99922]]]}},{"type":"Feature","properties":{"id":"IN-WB"},"geometry":{"type":"Polygon","coordinates":[[[85.82279,23.26784],[85.92269,23.12236],[86.04423,23.14572],[86.21761,22.98747],[86.5472,22.97641],[86.42188,22.77586],[86.78031,22.56487],[86.88812,22.24715],[86.7247,22.21569],[86.71371,22.1448],[86.94992,22.08627],[87.05635,21.85066],[87.22457,21.96024],[87.27264,21.81114],[87.43709,21.76045],[87.74779,20.78694],[89.13606,21.42955],[89.13927,21.60785],[89.03553,21.77397],[89.07114,22.15335],[88.9367,22.58527],[88.94614,22.66941],[88.9151,22.75228],[88.96713,22.83346],[88.87063,22.95235],[88.88327,23.03885],[88.86377,23.08759],[88.99148,23.21134],[88.71133,23.2492],[88.79254,23.46028],[88.79351,23.50535],[88.74841,23.47361],[88.56507,23.64044],[88.58087,23.87105],[88.66189,23.87607],[88.73743,23.91751],[88.6976,24.14703],[88.74841,24.1959],[88.68801,24.31464],[88.50934,24.32474],[88.12296,24.51301],[88.08786,24.63232],[88.00683,24.66477],[88.15515,24.85806],[88.14004,24.93529],[88.21832,24.96642],[88.27325,24.88796],[88.33917,24.86803],[88.46277,25.07468],[88.44766,25.20149],[88.94067,25.18534],[89.00463,25.26583],[89.01105,25.30303],[88.85278,25.34679],[88.81296,25.51546],[88.677,25.46959],[88.4559,25.59227],[88.45103,25.66245],[88.242,25.80811],[88.13138,25.78773],[88.08804,25.91334],[88.16581,26.0238],[88.1844,26.14417],[88.34757,26.22216],[88.35153,26.29123],[88.51649,26.35923],[88.48749,26.45855],[88.36938,26.48683],[88.35153,26.45241],[88.33093,26.48929],[88.41196,26.63837],[88.4298,26.54489],[88.62144,26.46783],[88.69485,26.38353],[88.67837,26.26291],[88.78961,26.31093],[88.85004,26.23211],[89.05328,26.2469],[88.91321,26.37984],[88.92357,26.40711],[88.95612,26.4564],[89.08899,26.38845],[89.15869,26.13708],[89.35953,26.0077],[89.53515,26.00382],[89.57101,25.9682],[89.63968,26.22595],[89.70201,26.15138],[89.73581,26.15818],[89.71298,26.26755],[89.84653,26.40078],[89.86885,26.46258],[89.86124,26.73307],[89.63369,26.74402],[89.42349,26.83727],[89.3901,26.84225],[89.38319,26.85963],[89.37913,26.86224],[89.1926,26.81329],[89.12825,26.81661],[89.09554,26.89089],[88.95807,26.92668],[88.92301,26.99286],[88.8714,26.97488],[88.86984,27.10937],[88.74219,27.144],[88.62842,27.1731],[88.52045,27.17753],[88.45161,27.07655],[88.33505,27.10176],[88.30226,27.12682],[88.22656,27.11842],[88.08563,27.14072],[88.08331,27.16501],[88.04932,27.21631],[88.01587,27.21388],[87.9887,27.11045],[88.11719,26.98758],[88.13422,26.98705],[88.12302,26.95324],[88.19107,26.75516],[88.1659,26.68177],[88.16452,26.64111],[88.09963,26.54195],[88.14313,26.51097],[88.23394,26.55413],[88.19137,26.47549],[88.26158,26.4263],[88.23034,26.37679],[88.29093,26.3568],[88.0046,26.14341],[87.95036,26.06511],[87.84393,26.04537],[87.78625,25.87559],[87.90058,25.85304],[87.89989,25.76866],[88.04923,25.68794],[88.03859,25.54182],[88.0688,25.48047],[87.92667,25.53717],[87.76514,25.42529],[87.78865,25.34495],[87.86521,25.27139],[87.78831,25.15212],[87.95173,24.97174],[87.83431,24.7381],[87.89543,24.57616],[87.77904,24.57147],[87.7811,24.34928],[87.63587,24.21628],[87.71038,24.14048],[87.4443,23.97527],[87.23281,24.04238],[87.27933,23.87516],[87.08312,23.80639],[86.89704,23.89054],[86.79748,23.82806],[86.8198,23.76115],[86.79508,23.68351],[86.7374,23.68194],[86.59286,23.67156],[86.5242,23.62628],[86.44214,23.62816],[86.31374,23.42544],[86.14208,23.47647],[86.14311,23.56619],[86.0123,23.56619],[86.04389,23.48245],[85.87223,23.47489],[85.8633,23.41316],[85.89969,23.37062],[85.82279,23.26784]]]}},{"type":"Feature","properties":{"id":"FM-KSA"},"geometry":{"type":"Polygon","coordinates":[[[161.58988,8.892633],[161.87397,3.186785],[165.35175,6.367],[161.58988,8.892633]]]}},{"type":"Feature","properties":{"id":"CA-ON"},"geometry":{"type":"Polygon","coordinates":[[[-95.15374,52.84191],[-95.15357,49.384],[-95.12903,49.37056],[-95.05825,49.35311],[-95.01419,49.35647],[-94.99532,49.36579],[-94.95681,49.37035],[-94.85381,49.32492],[-94.8159,49.32299],[-94.82487,49.29483],[-94.77355,49.11998],[-94.75017,49.09931],[-94.687,48.84077],[-94.70087,48.8339],[-94.70486,48.82365],[-94.69669,48.80918],[-94.69335,48.77883],[-94.58903,48.71803],[-94.54885,48.71543],[-94.53826,48.70216],[-94.44258,48.69223],[-94.4174,48.71049],[-94.27153,48.70232],[-94.25172,48.68404],[-94.25104,48.65729],[-94.23215,48.65202],[-93.85769,48.63284],[-93.83288,48.62745],[-93.80676,48.58232],[-93.80939,48.52439],[-93.79267,48.51631],[-93.66382,48.51845],[-93.47022,48.54357],[-93.44472,48.59147],[-93.40693,48.60948],[-93.39758,48.60364],[-93.3712,48.60599],[-93.33946,48.62787],[-93.25391,48.64266],[-92.94973,48.60866],[-92.7287,48.54005],[-92.6342,48.54133],[-92.62747,48.50278],[-92.69927,48.49573],[-92.71323,48.46081],[-92.65606,48.43471],[-92.50712,48.44921],[-92.45588,48.40624],[-92.48147,48.36609],[-92.37185,48.22259],[-92.27167,48.25046],[-92.30939,48.31251],[-92.26662,48.35651],[-92.202,48.35252],[-92.14732,48.36578],[-92.05339,48.35958],[-91.98929,48.25409],[-91.86125,48.21278],[-91.71231,48.19875],[-91.70451,48.11805],[-91.55649,48.10611],[-91.58025,48.04339],[-91.45829,48.07454],[-91.43248,48.04912],[-91.25025,48.08522],[-91.08016,48.18096],[-90.87588,48.2484],[-90.75045,48.09143],[-90.56444,48.12184],[-90.56312,48.09488],[-90.07418,48.11043],[-89.89974,47.98109],[-89.77248,48.02607],[-89.57972,48.00023],[-89.48837,48.01412],[-88.37033,48.30586],[-84.85871,46.88881],[-84.55635,46.45974],[-84.47607,46.45225],[-84.4481,46.48972],[-84.42101,46.49853],[-84.34174,46.50683],[-84.29893,46.49127],[-84.26351,46.49508],[-84.2264,46.53337],[-84.1945,46.54061],[-84.17723,46.52753],[-84.12885,46.53068],[-84.11196,46.50248],[-84.13451,46.39218],[-84.11254,46.32329],[-84.11615,46.2681],[-84.09756,46.25512],[-84.1096,46.23987],[-83.95399,46.05634],[-83.90453,46.05922],[-83.83329,46.12169],[-83.57017,46.105],[-83.43746,45.99749],[-83.59589,45.82131],[-82.48419,45.30225],[-82.42469,42.992],[-82.4146,42.97626],[-82.4253,42.95423],[-82.45331,42.93139],[-82.4826,42.8068],[-82.46613,42.76615],[-82.51063,42.66025],[-82.51858,42.611],[-82.57583,42.5718],[-82.58873,42.54984],[-82.64242,42.55594],[-82.82964,42.37355],[-83.02253,42.33045],[-83.07837,42.30978],[-83.09837,42.28877],[-83.12724,42.2376],[-83.14962,42.04089],[-83.11184,41.95671],[-82.67862,41.67615],[-80.53581,42.29896],[-79.77073,42.55308],[-78.93684,42.82887],[-78.90712,42.89733],[-78.90905,42.93022],[-78.93224,42.95229],[-78.96312,42.95509],[-78.98126,42.97],[-79.02074,42.98444],[-79.02424,43.01983],[-78.99941,43.05612],[-79.01055,43.06659],[-79.07486,43.07845],[-79.05671,43.10937],[-79.06881,43.12029],[-79.0427,43.13934],[-79.04652,43.16396],[-79.05384,43.17418],[-79.05002,43.20133],[-79.05544,43.21224],[-79.05512,43.25375],[-79.06921,43.26183],[-79.25796,43.54052],[-76.79706,43.63099],[-76.43859,44.09393],[-76.35324,44.13493],[-76.31222,44.19894],[-76.244,44.19643],[-76.1664,44.23051],[-76.16285,44.28262],[-76.00018,44.34896],[-75.95947,44.34463],[-75.8217,44.43176],[-75.76813,44.51537],[-75.41441,44.76614],[-75.2193,44.87821],[-75.01363,44.95608],[-74.99101,44.98051],[-74.8447,45.00606],[-74.66689,45.00646],[-74.50073,45.06927],[-74.32151,45.18844],[-74.47257,45.30253],[-74.38125,45.56562],[-74.48081,45.59638],[-74.52819,45.59157],[-74.61059,45.62183],[-74.64286,45.64008],[-74.71427,45.62856],[-74.94087,45.64536],[-75.02601,45.59589],[-75.13038,45.57715],[-75.24162,45.58677],[-75.33981,45.53581],[-75.4804,45.51368],[-75.58151,45.47361],[-75.61876,45.47192],[-75.68562,45.45826],[-75.69334,45.45187],[-75.70287,45.43712],[-75.70424,45.42706],[-75.70957,45.42345],[-75.71772,45.42158],[-75.7239,45.422],[-75.72381,45.41766],[-75.72982,45.41736],[-75.75935,45.4114],[-75.80535,45.3762],[-75.8493,45.37716],[-75.91522,45.41477],[-75.97152,45.47643],[-76.091,45.51735],[-76.19468,45.52023],[-76.23245,45.51109],[-76.24343,45.46873],[-76.36772,45.45814],[-76.5023,45.51638],[-76.60873,45.53226],[-76.65885,45.56015],[-76.67602,45.58466],[-76.6719,45.62357],[-76.71653,45.66438],[-76.68975,45.69172],[-76.77558,45.75308],[-76.7646,45.84978],[-76.77696,45.87273],[-76.893,45.90141],[-76.93077,45.89137],[-76.91085,45.80624],[-76.94107,45.789],[-76.98982,45.78613],[-77.01866,45.80863],[-77.04681,45.8072],[-77.07565,45.83543],[-77.12097,45.84165],[-77.19238,45.86556],[-77.2734,45.93962],[-77.28782,45.98258],[-77.27683,46.0174],[-77.31803,46.02741],[-77.35717,46.05696],[-77.67852,46.19925],[-77.69363,46.18452],[-78.24981,46.27714],[-78.31642,46.25388],[-78.38508,46.29138],[-78.703,46.32173],[-78.72703,46.3407],[-78.73115,46.38619],[-78.88496,46.45624],[-78.98796,46.54604],[-79.01817,46.63664],[-79.04563,46.64607],[-79.08958,46.68518],[-79.17335,46.82678],[-79.21317,46.83335],[-79.44869,47.10512],[-79.42535,47.25307],[-79.48921,47.30711],[-79.58877,47.42941],[-79.55581,47.51342],[-79.51787,47.53313],[-79.51659,51.46031],[-82.09357,52.92137],[-82.10455,55.13419],[-88.99084,56.84662],[-95.15374,52.84191]]]}},{"type":"Feature","properties":{"id":"CA-NS"},"geometry":{"type":"Polygon","coordinates":[[[-67.16117,44.20069],[-65.81187,42.91911],[-59.437,43.71683],[-57.60106,47.38123],[-60.49316,47.38172],[-61.75622,46.42845],[-62.49954,45.85952],[-63.58306,46.10096],[-64.03625,46.01901],[-64.04861,45.97703],[-64.1571,45.9799],[-64.28619,45.83274],[-64.33563,45.8557],[-64.54574,45.68615],[-67.16117,44.20069]]]}},{"type":"Feature","properties":{"id":"CA-PE"},"geometry":{"type":"Polygon","coordinates":[[[-64.66796,46.6005],[-63.75335,46.22936],[-63.58306,46.10096],[-62.49954,45.85952],[-61.75622,46.42845],[-64.01702,47.11511],[-64.66796,46.6005]]]}},{"type":"Feature","properties":{"id":"FM-PNI"},"geometry":{"type":"Polygon","coordinates":[[[153.83475,11.01511],[154.49668,-0.44964],[156.88247,-1.39237],[161.87397,3.186785],[161.58988,8.892633],[159.04653,10.59067],[153.83475,11.01511]]]}},{"type":"Feature","properties":{"id":"CA-NB"},"geometry":{"type":"Polygon","coordinates":[[[-69.05073,47.30076],[-69.05039,47.2456],[-68.89222,47.1807],[-68.70125,47.24399],[-68.60575,47.24659],[-68.57914,47.28431],[-68.38332,47.28723],[-68.37458,47.35851],[-68.23244,47.35712],[-67.94843,47.1925],[-67.87993,47.10377],[-67.78578,47.06473],[-67.78111,45.9392],[-67.75196,45.91814],[-67.80961,45.87531],[-67.75654,45.82324],[-67.80653,45.80022],[-67.80705,45.69528],[-67.6049,45.60725],[-67.43815,45.59162],[-67.42144,45.50584],[-67.50578,45.48971],[-67.42394,45.37969],[-67.48201,45.27351],[-67.34927,45.122],[-67.29754,45.14865],[-67.29748,45.18173],[-67.27039,45.1934],[-67.22751,45.16344],[-67.20349,45.1722],[-67.19603,45.16771],[-67.15965,45.16179],[-67.11316,45.11176],[-67.0216,44.95333],[-66.96824,44.90965],[-66.98249,44.87071],[-66.96824,44.83078],[-66.93432,44.82597],[-67.16117,44.20069],[-64.54574,45.68615],[-64.33563,45.8557],[-64.28619,45.83274],[-64.1571,45.9799],[-64.04861,45.97703],[-64.03625,46.01901],[-63.58306,46.10096],[-63.75335,46.22936],[-64.66796,46.6005],[-64.01702,47.11511],[-64.29855,48.12037],[-65.19393,47.92013],[-66.28295,48.03872],[-66.37908,48.08919],[-66.50405,48.0791],[-66.52465,48.04698],[-66.69012,48.00944],[-66.93664,47.97716],[-66.96548,47.89159],[-67.06161,47.93117],[-67.37884,47.85751],[-67.60406,47.93209],[-67.60543,48.00014],[-68.12316,48.00014],[-68.12454,47.91461],[-68.38409,47.91553],[-68.38546,47.55439],[-68.57223,47.42727],[-68.80844,47.34824],[-69.05073,47.30076]]]}},{"type":"Feature","properties":{"id":"CA-NL"},"geometry":{"type":"Polygon","coordinates":[[[-67.83623,54.45267],[-67.35283,52.90413],[-64.55407,51.57984],[-63.8082,51.99787],[-57.10774,51.99856],[-57.10928,51.37809],[-60.49316,47.38172],[-57.60106,47.38123],[-56.67989,47.3339],[-56.25228,47.31192],[-55.8643,46.64935],[-51.16966,45.93987],[-45.64471,55.43944],[-53.68108,62.9266],[-64.79302,60.27404],[-63.7603,54.63112],[-66.84746,55.09899],[-67.83623,54.45267]]]}},{"type":"Feature","properties":{"id":"CA-QC"},"geometry":{"type":"Polygon","coordinates":[[[-79.92119,54.66449],[-78.74291,52.08899],[-79.56139,51.58666],[-79.51659,51.46031],[-79.51787,47.53313],[-79.55581,47.51342],[-79.58877,47.42941],[-79.48921,47.30711],[-79.42535,47.25307],[-79.44869,47.10512],[-79.21317,46.83335],[-79.17335,46.82678],[-79.08958,46.68518],[-79.04563,46.64607],[-79.01817,46.63664],[-78.98796,46.54604],[-78.88496,46.45624],[-78.73115,46.38619],[-78.72703,46.3407],[-78.703,46.32173],[-78.38508,46.29138],[-78.31642,46.25388],[-78.24981,46.27714],[-77.69363,46.18452],[-77.67852,46.19925],[-77.35717,46.05696],[-77.31803,46.02741],[-77.27683,46.0174],[-77.28782,45.98258],[-77.2734,45.93962],[-77.19238,45.86556],[-77.12097,45.84165],[-77.07565,45.83543],[-77.04681,45.8072],[-77.01866,45.80863],[-76.98982,45.78613],[-76.94107,45.789],[-76.91085,45.80624],[-76.93077,45.89137],[-76.893,45.90141],[-76.77696,45.87273],[-76.7646,45.84978],[-76.77558,45.75308],[-76.68975,45.69172],[-76.71653,45.66438],[-76.6719,45.62357],[-76.67602,45.58466],[-76.65885,45.56015],[-76.60873,45.53226],[-76.5023,45.51638],[-76.36772,45.45814],[-76.24343,45.46873],[-76.23245,45.51109],[-76.19468,45.52023],[-76.091,45.51735],[-75.97152,45.47643],[-75.91522,45.41477],[-75.8493,45.37716],[-75.80535,45.3762],[-75.75935,45.4114],[-75.72982,45.41736],[-75.72381,45.41766],[-75.7239,45.422],[-75.71772,45.42158],[-75.70957,45.42345],[-75.70424,45.42706],[-75.70287,45.43712],[-75.69334,45.45187],[-75.68562,45.45826],[-75.61876,45.47192],[-75.58151,45.47361],[-75.4804,45.51368],[-75.33981,45.53581],[-75.24162,45.58677],[-75.13038,45.57715],[-75.02601,45.59589],[-74.94087,45.64536],[-74.71427,45.62856],[-74.64286,45.64008],[-74.61059,45.62183],[-74.52819,45.59157],[-74.48081,45.59638],[-74.38125,45.56562],[-74.47257,45.30253],[-74.32151,45.18844],[-74.50073,45.06927],[-74.66689,45.00646],[-74.32699,44.99029],[-73.35025,45.00942],[-71.50067,45.01357],[-71.48735,45.07784],[-71.42778,45.12624],[-71.40364,45.21382],[-71.44252,45.2361],[-71.37133,45.24624],[-71.29371,45.29996],[-71.22338,45.25184],[-71.19723,45.25438],[-71.14568,45.24128],[-71.08364,45.30623],[-71.01866,45.31573],[-71.0107,45.34819],[-70.95193,45.33895],[-70.91169,45.29849],[-70.89864,45.2398],[-70.84816,45.22698],[-70.80236,45.37444],[-70.82638,45.39828],[-70.78372,45.43269],[-70.65383,45.37592],[-70.62518,45.42286],[-70.72651,45.49771],[-70.68516,45.56964],[-70.54019,45.67291],[-70.38934,45.73215],[-70.41523,45.79497],[-70.25976,45.89675],[-70.24694,45.95138],[-70.31025,45.96424],[-70.23855,46.1453],[-70.29078,46.18832],[-70.18547,46.35357],[-70.05812,46.41768],[-69.99966,46.69543],[-69.22119,47.46461],[-69.05148,47.42012],[-69.05073,47.30076],[-68.80844,47.34824],[-68.57223,47.42727],[-68.38546,47.55439],[-68.38409,47.91553],[-68.12454,47.91461],[-68.12316,48.00014],[-67.60543,48.00014],[-67.60406,47.93209],[-67.37884,47.85751],[-67.06161,47.93117],[-66.96548,47.89159],[-66.93664,47.97716],[-66.69012,48.00944],[-66.52465,48.04698],[-66.50405,48.0791],[-66.37908,48.08919],[-66.28295,48.03872],[-65.19393,47.92013],[-64.29855,48.12037],[-64.01702,47.11511],[-61.75622,46.42845],[-60.49316,47.38172],[-57.10928,51.37809],[-57.10774,51.99856],[-63.8082,51.99787],[-64.55407,51.57984],[-67.35283,52.90413],[-67.83623,54.45267],[-66.84746,55.09899],[-63.7603,54.63112],[-64.79302,60.27404],[-68.84423,60.00396],[-69.3496,61.12682],[-73.67821,62.57769],[-78.62206,62.65853],[-79.23729,58.64783],[-77.32567,57.55654],[-77.17186,56.02851],[-78.98049,54.90049],[-79.92119,54.66449]]]}},{"type":"Feature","properties":{"id":"CA-YT"},"geometry":{"type":"Polygon","coordinates":[[[-141.00555,72.20369],[-141.00116,60.30648],[-140.5227,60.22077],[-140.45648,60.30919],[-139.98024,60.18027],[-139.68991,60.33693],[-139.05831,60.35205],[-139.20603,60.08896],[-139.05365,59.99655],[-123.86203,59.99964],[-124.71898,60.9114],[-126.80638,60.77223],[-134.09581,67.00286],[-136.15918,67.00031],[-136.20381,67.0511],[-136.21788,67.59731],[-136.4486,67.65377],[-136.4486,68.88195],[-136.61065,69.40701],[-141.00555,72.20369]]]}},{"type":"Feature","properties":{"id":"CA-NT"},"geometry":{"type":"Polygon","coordinates":[[[-141.00555,72.20369],[-136.61065,69.40701],[-136.4486,68.88195],[-136.4486,67.65377],[-136.21788,67.59731],[-136.20381,67.0511],[-136.15918,67.00031],[-134.09581,67.00286],[-126.80638,60.77223],[-124.71898,60.9114],[-123.86203,59.99964],[-120.00135,60.00043],[-110.00637,59.99944],[-102.00759,59.99922],[-102.08165,64.2419],[-111.26622,65.10654],[-120.71446,68.0217],[-120.71446,69.63965],[-110.07969,70.00345],[-110.08928,79.74266],[-141.00555,72.20369]]]}},{"type":"Feature","properties":{"id":"CA-NU"},"geometry":{"type":"Polygon","coordinates":[[[-120.71446,68.0217],[-111.26622,65.10654],[-102.08165,64.2419],[-102.00759,59.99922],[-94.78348,59.99917],[-88.99084,56.84662],[-82.10455,55.13419],[-82.09357,52.92137],[-79.51659,51.46031],[-79.56139,51.58666],[-78.74291,52.08899],[-79.92119,54.66449],[-78.98049,54.90049],[-77.17186,56.02851],[-77.32567,57.55654],[-79.23729,58.64783],[-78.62206,62.65853],[-73.67821,62.57769],[-69.3496,61.12682],[-68.84423,60.00396],[-64.79302,60.27404],[-53.68108,62.9266],[-74.12379,75.70014],[-73.91222,78.42484],[-67.48417,80.75493],[-63.1988,81.66522],[-59.93819,82.31398],[-62.36036,83.40597],[-85.36473,83.41631],[-110.08928,79.74266],[-110.07969,70.00345],[-120.71446,69.63965],[-120.71446,68.0217]]]}},{"type":"Feature","properties":{"id":"US-DC"},"geometry":{"type":"Polygon","coordinates":[[[-77.11962,38.93441],[-77.11536,38.92787],[-77.10592,38.91912],[-77.10201,38.91264],[-77.08897,38.90436],[-77.07047,38.90106],[-77.06759,38.89895],[-77.05957,38.88152],[-77.05493,38.87964],[-77.0491,38.87343],[-77.04592,38.87557],[-77.03021,38.86133],[-77.03906,38.79153],[-76.90939,38.89301],[-77.04117,38.99552],[-77.11962,38.93441]]]}},{"type":"Feature","properties":{"id":"US-AL"},"geometry":{"type":"Polygon","coordinates":[[[-88.47496,31.89065],[-88.37952,30.00457],[-87.51915,30.07055],[-87.51915,30.28187],[-87.44774,30.30677],[-87.50679,30.31863],[-87.3777,30.44658],[-87.42989,30.47144],[-87.44774,30.52113],[-87.39418,30.62518],[-87.40654,30.67362],[-87.52739,30.74093],[-87.63588,30.86596],[-87.59056,30.96375],[-87.5988,30.99671],[-85.00191,31.0026],[-85.00191,31.02143],[-85.09804,31.16255],[-85.10902,31.27295],[-85.04311,31.52965],[-85.07332,31.62676],[-85.14336,31.844],[-85.05547,32.01766],[-85.06233,32.13519],[-84.88518,32.26185],[-85.0074,32.33034],[-84.9662,32.42661],[-84.99916,32.51697],[-85.07057,32.5818],[-85.18318,32.85909],[-85.60478,34.98639],[-88.20305,35.00664],[-88.20029,34.99532],[-88.14949,34.9211],[-88.09868,34.89407],[-88.47496,31.89065]]]}},{"type":"Feature","properties":{"id":"FM-TRK"},"geometry":{"type":"Polygon","coordinates":[[[148.42679,11.45488],[148.49862,1.920402],[154.49668,-0.44964],[153.83475,11.01511],[148.42679,11.45488]]]}},{"type":"Feature","properties":{"id":"US-AZ"},"geometry":{"type":"Polygon","coordinates":[[[-114.82011,32.49609],[-112.34553,31.7357],[-111.07523,31.33232],[-109.05235,31.3333],[-109.04686,37.00061],[-114.05113,37.00171],[-114.04564,36.1991],[-114.12254,36.11372],[-114.14864,36.02936],[-114.2379,36.01715],[-114.30931,36.06379],[-114.37523,36.147],[-114.57298,36.15254],[-114.61281,36.13036],[-114.63066,36.14367],[-114.75014,36.09486],[-114.72404,36.03159],[-114.74052,36.0127],[-114.74602,35.98271],[-114.66774,35.87039],[-114.70482,35.85592],[-114.68971,35.65197],[-114.65263,35.60956],[-114.68147,35.49783],[-114.59633,35.33331],[-114.57024,35.15498],[-114.5826,35.12803],[-114.62791,35.12129],[-114.64577,35.10444],[-114.60732,35.07635],[-114.64165,35.01451],[-114.63203,34.86704],[-114.58672,34.83773],[-114.5565,34.77233],[-114.47411,34.71478],[-114.42879,34.58939],[-114.37798,34.52153],[-114.38484,34.45361],[-114.3409,34.44682],[-114.1404,34.3074],[-114.13628,34.26315],[-114.3821,34.11434],[-114.53728,33.93223],[-114.50432,33.87182],[-114.52766,33.85015],[-114.49745,33.69718],[-114.52904,33.68461],[-114.53316,33.55196],[-114.65263,33.41336],[-114.72954,33.40763],[-114.69795,33.35947],[-114.72816,33.3044],[-114.67186,33.22517],[-114.70894,33.0918],[-114.61693,33.0262],[-114.5208,33.03196],[-114.46724,32.91098],[-114.47136,32.84294],[-114.5359,32.79216],[-114.53178,32.75405],[-114.59633,32.73442],[-114.70482,32.7425],[-114.71871,32.71894],[-114.76736,32.64094],[-114.80584,32.62028],[-114.81141,32.55543],[-114.79524,32.55731],[-114.82011,32.49609]]]}},{"type":"Feature","properties":{"id":"US-AR"},"geometry":{"type":"Polygon","coordinates":[[[-94.61906,36.49995],[-94.43092,35.38371],[-94.48448,33.64004],[-94.38423,33.56455],[-94.07661,33.57828],[-94.0464,33.55539],[-94.04366,33.01929],[-91.16798,33.00432],[-91.14052,33.29866],[-91.08696,33.96299],[-90.58846,34.49776],[-90.56236,34.72946],[-90.24925,34.93349],[-90.31242,34.99989],[-90.16136,35.13252],[-90.07072,35.1314],[-90.11604,35.38483],[-89.89357,35.64528],[-89.94301,35.66871],[-89.95399,35.73228],[-89.72878,35.8047],[-89.70131,35.83366],[-89.77409,35.87261],[-89.73564,35.91044],[-89.67384,35.8804],[-89.64638,35.91489],[-89.73701,36.00048],[-90.37834,35.99826],[-90.11467,36.26446],[-90.07759,36.27442],[-90.07347,36.39833],[-90.13939,36.41711],[-90.1545,36.49885],[-94.61906,36.49995]]]}},{"type":"Feature","properties":{"id":"US-CA"},"geometry":{"type":"Polygon","coordinates":[[[-125.69978,42.00605],[-122.18305,33.57011],[-118.48109,32.5991],[-117.1243,32.53427],[-115.88053,32.63624],[-114.71871,32.71894],[-114.70482,32.7425],[-114.59633,32.73442],[-114.53178,32.75405],[-114.5359,32.79216],[-114.47136,32.84294],[-114.46724,32.91098],[-114.5208,33.03196],[-114.61693,33.0262],[-114.70894,33.0918],[-114.67186,33.22517],[-114.72816,33.3044],[-114.69795,33.35947],[-114.72954,33.40763],[-114.65263,33.41336],[-114.53316,33.55196],[-114.52904,33.68461],[-114.49745,33.69718],[-114.52766,33.85015],[-114.50432,33.87182],[-114.53728,33.93223],[-114.3821,34.11434],[-114.13628,34.26315],[-114.1404,34.3074],[-114.3409,34.44682],[-114.38484,34.45361],[-114.37798,34.52153],[-114.42879,34.58939],[-114.47411,34.71478],[-114.5565,34.77233],[-114.58672,34.83773],[-114.63203,34.86704],[-114.64165,35.01451],[-120.00023,38.99862],[-120.0016,41.99495],[-125.69978,42.00605]]]}},{"type":"Feature","properties":{"id":"US-CO"},"geometry":{"type":"Polygon","coordinates":[[[-109.05003,41.00069],[-109.04686,37.00061],[-103.00462,36.99417],[-102.04214,36.99314],[-102.04923,40.00324],[-102.05165,41.00235],[-104.0521,41.00188],[-109.05003,41.00069]]]}},{"type":"Feature","properties":{"id":"US-CT"},"geometry":{"type":"Polygon","coordinates":[[[-73.72761,41.10079],[-73.65535,41.01225],[-73.66067,41.00098],[-73.65947,40.99373],[-73.65723,40.99062],[-73.65981,40.98854],[-73.62136,40.9494],[-71.85736,41.32188],[-71.82955,41.34199],[-71.83917,41.3626],[-71.83333,41.37033],[-71.83367,41.38837],[-71.84191,41.39455],[-71.84363,41.40948],[-71.81926,41.41952],[-71.79866,41.41592],[-71.78733,41.65621],[-71.8001,42.00779],[-71.80067,42.0236],[-72.75464,42.03756],[-72.76837,42.00491],[-72.81712,41.9993],[-72.81369,42.03654],[-73.48746,42.0497],[-73.5508,41.2957],[-73.48283,41.21298],[-73.72761,41.10079]]]}},{"type":"Feature","properties":{"id":"FM-YAP"},"geometry":{"type":"Polygon","coordinates":[[[136.04605,12.45908],[136.27107,6.73747],[148.49862,1.920402],[148.42679,11.45488],[136.04605,12.45908]]]}},{"type":"Feature","properties":{"id":"US-DE"},"geometry":{"type":"Polygon","coordinates":[[[-75.78987,39.72204],[-75.78918,39.65388],[-75.69382,38.46017],[-74.98718,38.4507],[-75.01808,38.79724],[-75.38063,39.30382],[-75.56053,39.45773],[-75.55641,39.63352],[-75.46131,39.77457],[-75.40672,39.7962],[-75.41621,39.80165],[-75.43796,39.81414],[-75.46783,39.82548],[-75.49942,39.83365],[-75.53959,39.83945],[-75.57632,39.83945],[-75.59898,39.83734],[-75.62713,39.83259],[-75.65975,39.82337],[-75.68172,39.81387],[-75.71434,39.79515],[-75.72532,39.78644],[-75.74043,39.77378],[-75.76,39.75002],[-75.77476,39.7223],[-75.78987,39.72204]]]}},{"type":"Feature","properties":{"id":"US-FL"},"geometry":{"type":"Polygon","coordinates":[[[-87.63588,30.86596],[-87.52739,30.74093],[-87.40654,30.67362],[-87.39418,30.62518],[-87.44774,30.52113],[-87.42989,30.47144],[-87.3777,30.44658],[-87.50679,30.31863],[-87.44774,30.30677],[-87.51915,30.28187],[-87.51915,30.07055],[-83.18732,24.36791],[-82.02215,24.23074],[-80.16442,23.44484],[-79.36558,27.02964],[-81.34929,30.71298],[-81.56078,30.71535],[-81.95354,30.83098],[-81.97826,30.77554],[-82.01946,30.7897],[-82.05105,30.66575],[-82.00847,30.56765],[-82.04555,30.36287],[-82.17052,30.3605],[-82.20897,30.40908],[-82.2282,30.56765],[-84.86355,30.7118],[-84.92122,30.76256],[-84.9377,30.88285],[-85.00637,30.97591],[-85.00191,31.0026],[-87.5988,30.99671],[-87.59056,30.96375],[-87.63588,30.86596]]]}},{"type":"Feature","properties":{"id":"US-GA"},"geometry":{"type":"Polygon","coordinates":[[[-85.60478,34.98639],[-85.18318,32.85909],[-85.07057,32.5818],[-84.99916,32.51697],[-84.9662,32.42661],[-85.0074,32.33034],[-84.88518,32.26185],[-85.06233,32.13519],[-85.05547,32.01766],[-85.14336,31.844],[-85.07332,31.62676],[-85.04311,31.52965],[-85.10902,31.27295],[-85.09804,31.16255],[-85.00191,31.0026],[-85.00637,30.97591],[-84.9377,30.88285],[-84.92122,30.76256],[-84.86355,30.7118],[-82.2282,30.56765],[-82.20897,30.40908],[-82.17052,30.3605],[-82.04555,30.36287],[-82.00847,30.56765],[-82.05105,30.66575],[-82.01946,30.7897],[-81.97826,30.77554],[-81.95354,30.83098],[-81.56078,30.71535],[-81.34929,30.71298],[-80.49837,32.0326],[-80.92066,32.03667],[-81.00511,32.1001],[-81.05386,32.08497],[-81.11635,32.11638],[-81.11978,32.1937],[-81.15823,32.24017],[-81.11772,32.29127],[-81.20767,32.42409],[-81.19257,32.46292],[-81.27771,32.53531],[-81.28595,32.55904],[-81.3244,32.55788],[-81.38277,32.59086],[-81.41847,32.63193],[-81.39581,32.65101],[-81.42809,32.84101],[-81.45555,32.84851],[-81.50087,32.93557],[-81.49538,33.00988],[-81.62103,33.09564],[-81.64163,33.09276],[-81.70824,33.11807],[-81.76248,33.15947],[-81.7721,33.18303],[-81.75974,33.19912],[-81.77553,33.2198],[-81.78858,33.20716],[-81.85037,33.24622],[-81.85587,33.30248],[-81.93964,33.34609],[-81.91354,33.43953],[-81.92968,33.46674],[-81.9877,33.48794],[-81.99766,33.51342],[-82.05019,33.56464],[-82.10752,33.59782],[-82.1343,33.59095],[-82.19747,33.63013],[-82.20022,33.66214],[-82.3015,33.80062],[-82.39008,33.85651],[-82.56414,33.95567],[-82.59538,34.02855],[-82.64105,34.06809],[-82.64311,34.0951],[-82.71658,34.14882],[-82.74095,34.20789],[-82.75057,34.2709],[-82.78043,34.29672],[-82.79657,34.34039],[-82.83365,34.36419],[-82.87519,34.47408],[-82.90231,34.48625],[-83.00085,34.47238],[-83.03655,34.48625],[-83.16873,34.59259],[-83.17148,34.60728],[-83.22916,34.61096],[-83.34829,34.69455],[-83.35447,34.72814],[-83.32048,34.75861],[-83.32357,34.78878],[-83.2374,34.87417],[-83.11689,34.94033],[-83.12616,34.9544],[-83.09869,34.99098],[-83.10796,35.0011],[-84.32551,34.99393],[-85.60478,34.98639]]]}},{"type":"Feature","properties":{"id":"US-ID"},"geometry":{"type":"Polygon","coordinates":[[[-117.24003,44.37408],[-117.18921,44.34266],[-117.22149,44.30238],[-117.18304,44.26453],[-116.97841,44.24387],[-116.97155,44.19565],[-116.90151,44.17792],[-116.89602,44.15526],[-116.94271,44.09513],[-116.97429,44.08921],[-116.97704,44.05172],[-116.93722,44.03198],[-116.93859,43.98654],[-116.97704,43.96282],[-116.96194,43.91734],[-116.99215,43.8629],[-117.02923,43.83121],[-117.02619,42.00013],[-114.04073,42.00031],[-111.04628,42.00049],[-111.04897,44.47412],[-111.38328,44.75395],[-111.51237,44.64267],[-111.48765,44.539],[-112.31163,44.55662],[-112.39403,44.44888],[-112.71812,44.50571],[-112.83897,44.43712],[-112.82249,44.36255],[-113.00926,44.45476],[-113.13286,44.7754],[-113.25645,44.82802],[-113.3361,44.7871],[-113.5009,44.93506],[-113.44047,44.97005],[-113.45421,45.05742],[-113.75084,45.34385],[-113.82225,45.59809],[-113.98155,45.70752],[-114.33861,45.46148],[-114.54735,45.5731],[-114.55559,45.77653],[-114.39628,45.88752],[-114.49516,46.04407],[-114.31938,46.64887],[-114.61052,46.64322],[-115.33562,47.25631],[-115.75859,47.43311],[-115.63225,47.47953],[-115.75859,47.55002],[-115.68992,47.60746],[-116.04835,47.97742],[-116.04938,48.99999],[-117.03266,49.00056],[-117.04021,46.4257],[-117.03644,46.42309],[-117.03575,46.40794],[-117.04914,46.37787],[-117.06287,46.3665],[-117.06184,46.34873],[-117.04811,46.34281],[-117.03472,46.34138],[-116.92348,46.16048],[-116.98116,46.08242],[-116.94958,46.07004],[-116.91799,45.99567],[-116.77585,45.82129],[-116.54789,45.7533],[-116.46274,45.60746],[-116.67972,45.3185],[-116.75388,45.11342],[-116.84864,45.02321],[-116.85825,44.97563],[-116.83353,44.92995],[-116.9379,44.78588],[-117.05051,44.74298],[-117.15626,44.53093],[-117.22492,44.48392],[-117.24003,44.37408]]]}},{"type":"Feature","properties":{"id":"US-IL"},"geometry":{"type":"Polygon","coordinates":[[[-91.52996,40.18533],[-91.44756,39.8551],[-91.36242,39.80026],[-91.37615,39.73481],[-91.04656,39.45756],[-90.72933,39.25847],[-90.67989,39.09612],[-90.71491,39.05828],[-90.66822,38.94035],[-90.59475,38.87302],[-90.55767,38.87088],[-90.50342,38.90616],[-90.46429,38.96705],[-90.41141,38.96438],[-90.25554,38.91951],[-90.1086,38.84789],[-90.12234,38.79761],[-90.16079,38.7778],[-90.2116,38.73015],[-90.21366,38.70604],[-90.18894,38.68138],[-90.17795,38.64385],[-90.19031,38.60094],[-90.26104,38.54027],[-90.29674,38.43278],[-90.37914,38.32728],[-90.35168,38.20436],[-90.18139,38.07258],[-89.83806,37.90374],[-89.52221,37.69322],[-89.52221,37.52786],[-89.43706,37.4385],[-89.42882,37.35559],[-89.49199,37.3403],[-89.52495,37.28788],[-89.46728,37.20698],[-89.38488,37.04274],[-89.28325,36.99011],[-89.286,37.09314],[-89.19536,37.01862],[-89.09649,37.1676],[-88.95916,37.23323],[-88.62682,37.11505],[-88.55266,37.06247],[-88.46477,37.06247],[-88.42357,37.15884],[-88.51696,37.27476],[-88.47301,37.39487],[-88.40984,37.4276],[-88.35491,37.40142],[-88.30822,37.44286],[-88.06652,37.47121],[-88.15716,37.66279],[-88.03356,37.80181],[-88.09124,37.90807],[-88.02257,37.8864],[-88.03356,38.04231],[-87.93468,38.14606],[-87.97863,38.24535],[-87.74792,38.4005],[-87.75341,38.47364],[-87.66552,38.51234],[-87.61059,38.6712],[-87.52819,38.68406],[-87.49523,38.76121],[-87.55565,38.86821],[-87.5172,38.9537],[-87.57213,38.98786],[-87.57213,39.05188],[-87.65453,39.15845],[-87.59411,39.20103],[-87.59411,39.33286],[-87.52819,39.35835],[-87.51995,41.76174],[-87.20959,41.75764],[-87.02282,42.49706],[-90.64245,42.50785],[-90.65258,42.48406],[-90.43079,42.34617],[-90.40333,42.22019],[-90.19184,42.14692],[-90.14515,42.0287],[-90.18566,41.809],[-90.30651,41.75012],[-90.34325,41.65143],[-90.34187,41.58803],[-90.3956,41.57481],[-90.46581,41.52162],[-90.50667,41.51828],[-90.54581,41.5274],[-90.59267,41.51468],[-90.60572,41.49565],[-90.67303,41.46141],[-91.04107,41.43259],[-91.10149,41.28415],[-90.96416,41.01115],[-91.15093,40.66621],[-91.3377,40.60368],[-91.44307,40.37471],[-91.52996,40.18533]]]}},{"type":"Feature","properties":{"id":"US-IN"},"geometry":{"type":"Polygon","coordinates":[[[-88.09124,37.90807],[-88.03356,37.80181],[-87.95578,37.76983],[-87.90222,37.82273],[-87.94102,37.88427],[-87.89089,37.92842],[-87.83527,37.87478],[-87.67666,37.90079],[-87.67735,37.82273],[-87.60662,37.82761],[-87.58328,37.87912],[-87.62447,37.92463],[-87.59014,37.97336],[-87.5771,37.94629],[-87.51049,37.90567],[-87.41093,37.94683],[-87.23309,37.84768],[-87.16442,37.84334],[-87.13078,37.7853],[-87.09301,37.78313],[-87.05387,37.82002],[-87.04357,37.89971],[-86.81217,37.99717],[-86.74282,37.90133],[-86.65645,37.90794],[-86.63556,37.83675],[-86.58981,37.91303],[-86.52309,37.91813],[-86.52378,38.03288],[-86.32465,38.17552],[-86.27384,38.13989],[-86.27728,38.05937],[-86.20106,38.01611],[-86.10973,38.01557],[-86.04244,37.9582],[-86.01772,37.99771],[-85.94219,38.01016],[-85.91267,38.0664],[-85.90511,38.17552],[-85.83576,38.23379],[-85.84057,38.27316],[-85.79182,38.28717],[-85.74925,38.26345],[-85.65723,38.3125],[-85.6387,38.3825],[-85.41828,38.53574],[-85.443,38.72402],[-85.2734,38.73848],[-85.16628,38.68597],[-84.99188,38.77543],[-84.80991,38.78881],[-84.82845,38.83054],[-84.7852,38.88294],[-84.88064,38.90486],[-84.82433,38.97696],[-84.89643,39.05911],[-84.81884,39.10708],[-84.80645,41.69747],[-84.80614,41.761],[-87.20959,41.75764],[-87.51995,41.76174],[-87.52819,39.35835],[-87.59411,39.33286],[-87.59411,39.20103],[-87.65453,39.15845],[-87.57213,39.05188],[-87.57213,38.98786],[-87.5172,38.9537],[-87.55565,38.86821],[-87.49523,38.76121],[-87.52819,38.68406],[-87.61059,38.6712],[-87.66552,38.51234],[-87.75341,38.47364],[-87.74792,38.4005],[-87.97863,38.24535],[-87.93468,38.14606],[-88.03356,38.04231],[-88.02257,37.8864],[-88.09124,37.90807]]]}},{"type":"Feature","properties":{"id":"US-IA"},"geometry":{"type":"Polygon","coordinates":[[[-96.63614,42.74513],[-96.49057,42.57547],[-96.47134,42.49249],[-96.40268,42.49249],[-96.38071,42.46616],[-96.3862,42.4317],[-96.41641,42.40737],[-96.25162,42.02699],[-96.14999,41.9678],[-96.08682,41.53956],[-95.91928,41.45728],[-95.95224,41.34603],[-95.93507,41.32489],[-95.88701,41.31922],[-95.8719,41.30426],[-95.87602,41.28569],[-95.92203,41.26917],[-95.91173,41.23046],[-95.93095,41.20566],[-95.92203,41.18655],[-95.86092,41.18861],[-95.84512,41.18035],[-95.84581,41.16484],[-95.87534,41.16587],[-95.88426,41.14985],[-95.86298,41.08829],[-95.88495,41.05568],[-95.84238,40.67557],[-95.77211,40.58647],[-91.73074,40.61201],[-91.44307,40.37471],[-91.3377,40.60368],[-91.15093,40.66621],[-90.96416,41.01115],[-91.10149,41.28415],[-91.04107,41.43259],[-90.67303,41.46141],[-90.60572,41.49565],[-90.59267,41.51468],[-90.54581,41.5274],[-90.50667,41.51828],[-90.46581,41.52162],[-90.3956,41.57481],[-90.34187,41.58803],[-90.34325,41.65143],[-90.30651,41.75012],[-90.18566,41.809],[-90.14515,42.0287],[-90.19184,42.14692],[-90.40333,42.22019],[-90.43079,42.34617],[-90.65258,42.48406],[-90.64245,42.50785],[-90.70901,42.65026],[-91.02487,42.7068],[-91.08804,42.7774],[-91.09354,42.8761],[-91.1622,42.93646],[-91.15121,43.02085],[-91.16769,43.17528],[-91.07706,43.27334],[-91.09628,43.31932],[-91.21439,43.38123],[-91.22537,43.4989],[-96.45334,43.50083],[-96.60043,43.50089],[-96.52902,43.31333],[-96.58121,43.28334],[-96.54275,43.22733],[-96.48233,43.22133],[-96.43838,43.11516],[-96.63614,42.74513]]]}},{"type":"Feature","properties":{"id":"US-KS"},"geometry":{"type":"Polygon","coordinates":[[[-102.04923,40.00324],[-102.04214,36.99314],[-94.61795,36.9986],[-94.60772,39.11895],[-94.5909,39.13759],[-94.58953,39.15384],[-94.60601,39.16129],[-94.64686,39.1533],[-94.66128,39.15863],[-94.66094,39.17673],[-94.67845,39.18498],[-94.72239,39.16874],[-94.75157,39.17194],[-94.77011,39.18684],[-94.77664,39.20174],[-94.90298,39.30383],[-94.91122,39.35216],[-94.87895,39.37446],[-94.89405,39.39622],[-94.92839,39.38561],[-95.10348,39.53404],[-95.10279,39.57851],[-95.02177,39.67371],[-94.86247,39.74133],[-94.85903,39.75769],[-94.86865,39.773],[-94.90779,39.75875],[-94.93491,39.77221],[-94.93251,39.78804],[-94.88272,39.79542],[-94.87654,39.82391],[-95.31497,40.00166],[-102.04923,40.00324]]]}},{"type":"Feature","properties":{"id":"US-KY"},"geometry":{"type":"Polygon","coordinates":[[[-89.41668,36.50778],[-88.05987,36.49674],[-88.06536,36.67979],[-87.80993,36.63792],[-86.5932,36.65555],[-86.56848,36.63572],[-86.51355,36.65555],[-83.69109,36.58281],[-83.67455,36.60056],[-83.5239,36.66716],[-83.42125,36.66798],[-83.14074,36.75244],[-83.08032,36.84701],[-82.89904,36.88437],[-82.86059,36.98316],[-82.73425,37.04018],[-82.72326,37.12564],[-82.34698,37.2744],[-81.9707,37.53839],[-82.13961,37.56235],[-82.17669,37.63416],[-82.29754,37.67656],[-82.33462,37.77758],[-82.41702,37.84593],[-82.49392,37.93372],[-82.46646,37.98569],[-82.63263,38.13922],[-82.64361,38.1673],[-82.60791,38.17378],[-82.59692,38.21156],[-82.61203,38.24176],[-82.58319,38.25039],[-82.57495,38.32261],[-82.59692,38.342],[-82.59692,38.42382],[-82.61203,38.47329],[-82.72601,38.55603],[-82.79879,38.56355],[-82.84549,38.58502],[-82.87844,38.69121],[-82.8702,38.73943],[-82.89492,38.7555],[-82.97869,38.72765],[-83.02951,38.72872],[-83.0556,38.69336],[-83.10778,38.67621],[-83.14898,38.62258],[-83.20117,38.61614],[-83.24511,38.63116],[-83.27533,38.59897],[-83.31652,38.60112],[-83.33575,38.64618],[-83.52801,38.70408],[-83.62414,38.67621],[-83.66534,38.62902],[-83.7752,38.65047],[-83.79443,38.69765],[-83.95648,38.78762],[-84.07183,38.77263],[-84.23388,38.81331],[-84.23388,38.88176],[-84.29293,38.95334],[-84.30117,38.99445],[-84.34924,39.0382],[-84.41035,39.0446],[-84.43026,39.05739],[-84.4337,39.09577],[-84.4488,39.11922],[-84.47695,39.12188],[-84.49893,39.0995],[-84.51747,39.09151],[-84.54631,39.10163],[-84.56622,39.08671],[-84.62252,39.07392],[-84.68432,39.09844],[-84.74887,39.14851],[-84.81884,39.10708],[-84.89643,39.05911],[-84.82433,38.97696],[-84.88064,38.90486],[-84.7852,38.88294],[-84.82845,38.83054],[-84.80991,38.78881],[-84.99188,38.77543],[-85.16628,38.68597],[-85.2734,38.73848],[-85.443,38.72402],[-85.41828,38.53574],[-85.6387,38.3825],[-85.65723,38.3125],[-85.74925,38.26345],[-85.79182,38.28717],[-85.84057,38.27316],[-85.83576,38.23379],[-85.90511,38.17552],[-85.91267,38.0664],[-85.94219,38.01016],[-86.01772,37.99771],[-86.04244,37.9582],[-86.10973,38.01557],[-86.20106,38.01611],[-86.27728,38.05937],[-86.27384,38.13989],[-86.32465,38.17552],[-86.52378,38.03288],[-86.52309,37.91813],[-86.58981,37.91303],[-86.63556,37.83675],[-86.65645,37.90794],[-86.74282,37.90133],[-86.81217,37.99717],[-87.04357,37.89971],[-87.05387,37.82002],[-87.09301,37.78313],[-87.13078,37.7853],[-87.16442,37.84334],[-87.23309,37.84768],[-87.41093,37.94683],[-87.51049,37.90567],[-87.5771,37.94629],[-87.59014,37.97336],[-87.62447,37.92463],[-87.58328,37.87912],[-87.60662,37.82761],[-87.67735,37.82273],[-87.67666,37.90079],[-87.83527,37.87478],[-87.89089,37.92842],[-87.94102,37.88427],[-87.90222,37.82273],[-87.95578,37.76983],[-88.03356,37.80181],[-88.15716,37.66279],[-88.06652,37.47121],[-88.30822,37.44286],[-88.35491,37.40142],[-88.40984,37.4276],[-88.47301,37.39487],[-88.51696,37.27476],[-88.42357,37.15884],[-88.46477,37.06247],[-88.55266,37.06247],[-88.62682,37.11505],[-88.95916,37.23323],[-89.09649,37.1676],[-89.19536,37.01862],[-89.19146,36.9678],[-89.10907,36.98097],[-89.22168,36.56737],[-89.36175,36.63352],[-89.41668,36.50778]]]}},{"type":"Feature","properties":{"id":"US-LA"},"geometry":{"type":"Polygon","coordinates":[[[-94.04467,32.00379],[-93.92932,31.91524],[-93.52008,31.03684],[-93.76452,30.33771],[-93.70959,30.28791],[-93.71508,30.05521],[-93.92382,29.81005],[-93.77551,29.43998],[-88.93054,28.25639],[-88.37952,30.00457],[-89.58972,30.1835],[-89.69134,30.46089],[-89.77374,30.5177],[-89.85339,30.67373],[-89.73529,31.00389],[-91.63317,31.00153],[-91.44641,31.54147],[-90.90533,32.31769],[-91.16798,33.00432],[-94.04366,33.01929],[-94.04467,32.00379]]]}},{"type":"Feature","properties":{"id":"US-ME"},"geometry":{"type":"Polygon","coordinates":[[[-71.08364,45.30623],[-70.97613,43.56977],[-70.95278,43.55783],[-70.98849,43.3884],[-70.96377,43.33749],[-70.93493,43.33549],[-70.89648,43.29052],[-70.81271,43.23052],[-70.83331,43.13238],[-70.73786,43.07272],[-70.70422,43.07623],[-70.70216,43.04463],[-70.04999,42.81005],[-67.16117,44.20069],[-66.93432,44.82597],[-66.96824,44.83078],[-66.98249,44.87071],[-66.96824,44.90965],[-67.0216,44.95333],[-67.11316,45.11176],[-67.15965,45.16179],[-67.19603,45.16771],[-67.20349,45.1722],[-67.22751,45.16344],[-67.27039,45.1934],[-67.29748,45.18173],[-67.29754,45.14865],[-67.34927,45.122],[-67.48201,45.27351],[-67.42394,45.37969],[-67.50578,45.48971],[-67.42144,45.50584],[-67.43815,45.59162],[-67.6049,45.60725],[-67.80705,45.69528],[-67.80653,45.80022],[-67.75654,45.82324],[-67.80961,45.87531],[-67.75196,45.91814],[-67.78111,45.9392],[-67.78578,47.06473],[-67.87993,47.10377],[-67.94843,47.1925],[-68.23244,47.35712],[-68.37458,47.35851],[-68.38332,47.28723],[-68.57914,47.28431],[-68.60575,47.24659],[-68.70125,47.24399],[-68.89222,47.1807],[-69.05039,47.2456],[-69.05073,47.30076],[-69.05148,47.42012],[-69.22119,47.46461],[-69.99966,46.69543],[-70.05812,46.41768],[-70.18547,46.35357],[-70.29078,46.18832],[-70.23855,46.1453],[-70.31025,45.96424],[-70.24694,45.95138],[-70.25976,45.89675],[-70.41523,45.79497],[-70.38934,45.73215],[-70.54019,45.67291],[-70.68516,45.56964],[-70.72651,45.49771],[-70.62518,45.42286],[-70.65383,45.37592],[-70.78372,45.43269],[-70.82638,45.39828],[-70.80236,45.37444],[-70.84816,45.22698],[-70.89864,45.2398],[-70.91169,45.29849],[-70.95193,45.33895],[-71.0107,45.34819],[-71.01866,45.31573],[-71.08364,45.30623]]]}},{"type":"Feature","properties":{"id":"US-MD"},"geometry":{"type":"Polygon","coordinates":[[[-79.48702,39.20187],[-79.42831,39.22448],[-79.37853,39.27261],[-79.35999,39.27526],[-79.33801,39.29652],[-79.31158,39.30502],[-79.29235,39.29865],[-79.25321,39.35575],[-79.21579,39.36424],[-79.19657,39.38733],[-79.06885,39.47643],[-79.0455,39.4804],[-78.96688,39.43958],[-78.83951,39.5678],[-78.81925,39.56065],[-78.79659,39.63472],[-78.76501,39.64715],[-78.77702,39.62177],[-78.73789,39.62388],[-78.73892,39.60775],[-78.77771,39.60457],[-78.76364,39.58262],[-78.73789,39.58632],[-78.72621,39.56356],[-78.66167,39.53576],[-78.46872,39.5167],[-78.40658,39.617],[-78.31732,39.59479],[-78.18411,39.69524],[-78.10789,39.68203],[-78.05021,39.64768],[-78.01107,39.60113],[-77.87512,39.617],[-77.83392,39.60431],[-77.83392,39.56621],[-77.88885,39.55774],[-77.84216,39.49842],[-77.78036,39.49312],[-77.79821,39.43587],[-77.75289,39.42632],[-77.73135,39.32349],[-77.68054,39.32667],[-77.61462,39.3033],[-77.56655,39.30861],[-77.54596,39.27247],[-77.49102,39.25227],[-77.45532,39.22462],[-77.47729,39.18844],[-77.51162,39.18311],[-77.5281,39.14691],[-77.51849,39.12135],[-77.48416,39.11282],[-77.46081,39.07872],[-77.30975,39.05846],[-77.23971,39.02006],[-77.25619,39.00192],[-77.22186,38.97417],[-77.1477,38.9699],[-77.11962,38.93441],[-77.04117,38.99552],[-76.90939,38.89301],[-77.03906,38.79153],[-77.04196,38.70568],[-77.08041,38.70568],[-77.12023,38.68103],[-77.12161,38.63385],[-77.21225,38.6038],[-77.28915,38.50178],[-77.28778,38.38454],[-77.20813,38.35223],[-77.03784,38.42973],[-76.92248,38.23475],[-76.61074,38.15704],[-76.23034,37.8985],[-75.88153,37.90934],[-75.65768,37.94509],[-75.62472,37.99597],[-75.16879,38.02735],[-74.98718,38.4507],[-75.69382,38.46017],[-75.78918,39.65388],[-75.78987,39.72204],[-79.47663,39.72086],[-79.48702,39.20187]]]}},{"type":"Feature","properties":{"id":"IN-DD"},"geometry":{"type":"Polygon","coordinates":[[[70.8467,20.44438],[72.47526,20.38318],[72.89291,20.36748],[72.90801,20.43087],[72.83901,20.48555],[71.00154,20.74648],[70.87331,20.73203],[70.8467,20.44438]]]}},{"type":"Feature","properties":{"id":"US-MA"},"geometry":{"type":"Polygon","coordinates":[[[-73.50942,42.0867],[-73.48746,42.0497],[-72.81369,42.03654],[-72.81712,41.9993],[-72.76837,42.00491],[-72.75464,42.03756],[-71.80067,42.0236],[-71.8001,42.00779],[-71.38117,42.0194],[-71.3822,41.89277],[-71.33894,41.89916],[-71.341,41.79814],[-71.32898,41.7815],[-71.26135,41.75231],[-71.19577,41.67465],[-71.13432,41.65952],[-71.14153,41.60717],[-71.13123,41.591],[-71.10101,41.43444],[-69.97282,40.56828],[-69.42513,41.52748],[-70.04999,42.81005],[-70.81606,42.8739],[-70.84627,42.86182],[-70.88541,42.88446],[-70.92592,42.88698],[-70.96781,42.86887],[-71.04196,42.85981],[-71.06531,42.80744],[-71.13878,42.82305],[-71.18616,42.79484],[-71.18273,42.7399],[-71.25139,42.74343],[-71.29465,42.69651],[-72.45969,42.72515],[-73.26498,42.74494],[-73.50942,42.0867]]]}},{"type":"Feature","properties":{"id":"CN-QH"},"geometry":{"type":"Polygon","coordinates":[[[89.40811,36.01522],[89.42802,35.91602],[89.80224,35.859],[89.68482,35.42207],[89.45274,35.22991],[89.57908,34.90113],[89.81941,34.90395],[89.73358,34.65862],[89.87708,34.2277],[89.63882,34.04583],[89.93751,33.80083],[89.99656,33.55913],[90.24993,33.42857],[90.38177,33.2605],[90.51567,33.2651],[90.70175,33.13985],[91.51405,33.11339],[91.97891,32.86113],[92.20275,32.88766],[92.22335,32.72144],[93.02398,32.73646],[93.48953,32.4947],[93.72161,32.57343],[94.14733,32.43561],[94.61254,32.67001],[94.91981,32.413],[95.22571,32.3872],[95.51032,31.74685],[95.61881,31.77896],[95.7891,31.75327],[96.14822,31.69078],[96.20109,31.53816],[96.25053,31.55396],[96.21963,31.76145],[96.13929,31.82623],[96.24435,31.9341],[96.33636,31.95682],[96.37069,31.85539],[96.5657,31.7194],[96.8431,31.7083],[96.725,32.02612],[96.9564,31.99351],[97.00309,32.06628],[97.16583,32.03049],[97.22557,32.10962],[97.30865,32.07501],[97.37663,32.5306],[97.66399,32.47704],[97.72544,32.52886],[97.66296,32.55781],[97.54417,32.62332],[97.5325,32.64241],[97.48168,32.65556],[97.4271,32.7122],[97.37766,32.80718],[97.37834,32.86978],[97.33989,32.9038],[97.4319,32.9813],[97.53078,32.99167],[97.49061,33.11512],[97.4858,33.166],[97.59841,33.25964],[97.62348,33.33769],[97.7584,33.40536],[97.40409,33.63062],[97.39517,33.89492],[97.65266,33.93538],[97.66158,34.12431],[98.41689,34.09816],[98.42651,33.85217],[98.73962,33.43717],[98.85497,33.14675],[99.36035,32.90092],[99.73388,32.72375],[99.8822,33.04781],[100.49554,32.65671],[100.54687,32.5714],[100.66463,32.52481],[100.71819,32.67492],[100.94032,32.60756],[101.1185,32.63619],[101.22665,32.76071],[101.1621,33.22835],[101.64001,33.09844],[101.76223,33.46925],[101.61254,33.51506],[101.58782,33.67349],[101.17034,33.65463],[101.19232,33.79455],[100.7611,34.17545],[100.94581,34.37404],[101.76841,34.06233],[102.25318,34.36441],[102.15499,34.51221],[101.72996,34.70436],[102.40218,35.18503],[102.3191,35.34369],[102.50381,35.58808],[102.75169,35.49533],[102.80456,35.5758],[102.70568,35.86011],[102.9467,35.83507],[102.97004,36.03299],[103.02394,36.25562],[102.92026,36.30073],[102.88936,36.33338],[102.83168,36.33531],[102.82859,36.37098],[102.71598,36.60009],[102.59651,36.71081],[102.72319,36.76886],[102.47325,36.97238],[102.64594,37.10447],[101.9878,37.73108],[101.3578,37.7916],[100.97019,38.01293],[100.93105,38.16749],[100.09815,38.45735],[100.17677,38.2112],[98.74923,39.08743],[98.28643,39.03358],[98.24867,38.88515],[98.08937,38.78513],[97.3368,39.16733],[96.97769,39.20884],[96.9358,38.9108],[97.05459,38.6284],[96.66595,38.48665],[96.65702,38.22901],[96.29859,38.15669],[95.65658,38.36857],[95.24459,38.30502],[94.99053,38.43638],[94.5346,38.35781],[94.35607,38.76265],[93.42086,38.9092],[93.11599,39.17372],[92.40874,39.03625],[90.44975,38.49928],[90.09406,38.49014],[90.18127,38.39764],[90.14076,38.33734],[90.31585,38.22955],[90.52322,38.31903],[90.5136,37.74465],[91.06567,37.48575],[91.31149,37.02887],[90.84869,36.93342],[90.7196,36.59347],[91.02035,36.54053],[91.10961,36.10792],[90.86379,36.02577],[90.01922,36.2631],[89.95056,36.08018],[89.691,36.0935],[89.40811,36.01522]]]}},{"type":"Feature","properties":{"id":"US-MI"},"geometry":{"type":"Polygon","coordinates":[[[-90.42047,46.56636],[-90.39026,46.53331],[-90.33396,46.5522],[-90.2186,46.50212],[-90.11148,46.3355],[-89.09387,46.14078],[-88.8096,46.02457],[-88.64618,45.98832],[-88.51846,46.02362],[-88.30835,45.95587],[-88.10098,45.9234],[-88.07901,45.87371],[-88.13669,45.82205],[-88.0845,45.78088],[-88.01172,45.79333],[-87.86752,45.75023],[-87.77826,45.67639],[-87.83182,45.65912],[-87.77414,45.6063],[-87.79474,45.56209],[-87.84143,45.57074],[-87.80435,45.53805],[-87.8071,45.47067],[-87.85791,45.43888],[-87.87027,45.35499],[-87.75903,45.35209],[-87.69724,45.38972],[-87.64368,45.34534],[-87.74118,45.19554],[-87.65741,45.10643],[-87.44318,45.07735],[-87.41022,45.20134],[-87.09574,45.44563],[-86.7428,45.44467],[-86.25116,45.23713],[-87.02282,42.49706],[-87.20959,41.75764],[-84.80614,41.761],[-84.80645,41.69747],[-83.42355,41.73233],[-83.11184,41.95671],[-83.14962,42.04089],[-83.12724,42.2376],[-83.09837,42.28877],[-83.07837,42.30978],[-83.02253,42.33045],[-82.82964,42.37355],[-82.64242,42.55594],[-82.58873,42.54984],[-82.57583,42.5718],[-82.51858,42.611],[-82.51063,42.66025],[-82.46613,42.76615],[-82.4826,42.8068],[-82.45331,42.93139],[-82.4253,42.95423],[-82.4146,42.97626],[-82.42469,42.992],[-82.48419,45.30225],[-83.59589,45.82131],[-83.43746,45.99749],[-83.57017,46.105],[-83.83329,46.12169],[-83.90453,46.05922],[-83.95399,46.05634],[-84.1096,46.23987],[-84.09756,46.25512],[-84.11615,46.2681],[-84.11254,46.32329],[-84.13451,46.39218],[-84.11196,46.50248],[-84.12885,46.53068],[-84.17723,46.52753],[-84.1945,46.54061],[-84.2264,46.53337],[-84.26351,46.49508],[-84.29893,46.49127],[-84.34174,46.50683],[-84.42101,46.49853],[-84.4481,46.48972],[-84.47607,46.45225],[-84.55635,46.45974],[-84.85871,46.88881],[-88.37033,48.30586],[-89.48837,48.01412],[-90.42047,46.56636]]]}},{"type":"Feature","properties":{"id":"US-MN"},"geometry":{"type":"Polygon","coordinates":[[[-97.24024,48.99952],[-97.09604,48.68275],[-97.15372,48.59745],[-97.14685,48.17245],[-97.05484,47.9485],[-97.01639,47.91905],[-97.02738,47.89144],[-96.85983,47.60148],[-96.83511,47.0083],[-96.76233,46.92114],[-96.79666,46.81035],[-96.7898,46.63902],[-96.72525,46.45011],[-96.60303,46.32981],[-96.56732,45.93486],[-96.58106,45.82588],[-96.65109,45.74735],[-96.86258,45.62263],[-96.68405,45.41382],[-96.49591,45.36367],[-96.45334,45.29706],[-96.45334,43.50083],[-91.22537,43.4989],[-91.24582,43.77649],[-91.42572,43.99227],[-91.65094,44.06533],[-91.9874,44.3832],[-92.82511,44.74132],[-92.77017,44.82953],[-92.76056,45.28643],[-92.65207,45.40804],[-92.76056,45.56786],[-92.88416,45.56978],[-92.8718,45.72051],[-92.78803,45.76364],[-92.72348,45.889],[-92.29639,46.07794],[-92.29227,46.66258],[-92.2085,46.65221],[-92.203,46.69744],[-92.1055,46.75016],[-92.02448,46.70498],[-89.48837,48.01412],[-89.57972,48.00023],[-89.77248,48.02607],[-89.89974,47.98109],[-90.07418,48.11043],[-90.56312,48.09488],[-90.56444,48.12184],[-90.75045,48.09143],[-90.87588,48.2484],[-91.08016,48.18096],[-91.25025,48.08522],[-91.43248,48.04912],[-91.45829,48.07454],[-91.58025,48.04339],[-91.55649,48.10611],[-91.70451,48.11805],[-91.71231,48.19875],[-91.86125,48.21278],[-91.98929,48.25409],[-92.05339,48.35958],[-92.14732,48.36578],[-92.202,48.35252],[-92.26662,48.35651],[-92.30939,48.31251],[-92.27167,48.25046],[-92.37185,48.22259],[-92.48147,48.36609],[-92.45588,48.40624],[-92.50712,48.44921],[-92.65606,48.43471],[-92.71323,48.46081],[-92.69927,48.49573],[-92.62747,48.50278],[-92.6342,48.54133],[-92.7287,48.54005],[-92.94973,48.60866],[-93.25391,48.64266],[-93.33946,48.62787],[-93.3712,48.60599],[-93.39758,48.60364],[-93.40693,48.60948],[-93.44472,48.59147],[-93.47022,48.54357],[-93.66382,48.51845],[-93.79267,48.51631],[-93.80939,48.52439],[-93.80676,48.58232],[-93.83288,48.62745],[-93.85769,48.63284],[-94.23215,48.65202],[-94.25104,48.65729],[-94.25172,48.68404],[-94.27153,48.70232],[-94.4174,48.71049],[-94.44258,48.69223],[-94.53826,48.70216],[-94.54885,48.71543],[-94.58903,48.71803],[-94.69335,48.77883],[-94.69669,48.80918],[-94.70486,48.82365],[-94.70087,48.8339],[-94.687,48.84077],[-94.75017,49.09931],[-94.77355,49.11998],[-94.82487,49.29483],[-94.8159,49.32299],[-94.85381,49.32492],[-94.95681,49.37035],[-94.99532,49.36579],[-95.01419,49.35647],[-95.05825,49.35311],[-95.12903,49.37056],[-95.15357,49.384],[-95.15355,48.9996],[-97.24024,48.99952]]]}},{"type":"Feature","properties":{"id":"IN-AR"},"geometry":{"type":"Polygon","coordinates":[[[91.55819,27.6144],[91.65007,27.48287],[92.01132,27.47352],[92.12019,27.27829],[92.04702,27.26861],[92.03457,27.07334],[92.11863,26.893],[92.6441,26.98495],[92.64289,27.03894],[92.90313,27.00591],[93.03291,26.919],[93.38035,26.96308],[93.50257,26.93859],[93.68385,26.97838],[93.83354,27.07746],[93.80882,27.15142],[94.15489,27.4839],[94.26269,27.52471],[94.216,27.62331],[94.46937,27.5728],[94.86968,27.74036],[95.316,27.87125],[95.51994,27.88157],[95.63117,27.96105],[95.97518,27.9665],[95.76335,27.73519],[95.88523,27.52836],[95.86189,27.43333],[95.99647,27.37481],[95.89279,27.27294],[95.49041,27.2473],[95.46295,27.12209],[95.19515,27.02915],[95.23513,26.68499],[95.30339,26.65372],[95.437,26.7083],[95.81603,27.01335],[95.93002,27.04149],[96.04949,27.19428],[96.15591,27.24572],[96.40779,27.29818],[96.55761,27.29928],[96.73888,27.36638],[96.88445,27.25046],[96.85287,27.2065],[96.89132,27.17474],[97.14675,27.09041],[97.17422,27.14052],[96.91431,27.45752],[96.90112,27.62149],[97.29919,27.92233],[97.35824,27.87256],[97.38845,28.01329],[97.35412,28.06663],[97.31292,28.06784],[97.34547,28.21385],[97.1289,28.3619],[96.98882,28.32564],[96.88445,28.39452],[96.85561,28.4875],[96.6455,28.61657],[96.48895,28.42955],[96.40929,28.51526],[96.61391,28.72742],[96.3626,29.10607],[96.20467,29.02325],[96.18682,29.11087],[96.31316,29.18643],[96.05361,29.38167],[95.84899,29.31464],[95.75149,29.32063],[95.72086,29.20797],[95.50842,29.13487],[95.41091,29.13007],[95.3038,29.13847],[95.26122,29.07727],[95.2214,29.10727],[95.11291,29.09527],[95.0978,29.14446],[94.81353,29.17804],[94.69318,29.31739],[94.2752,29.11687],[94.35897,29.01965],[93.72797,28.68821],[93.44621,28.67189],[93.18069,28.50319],[93.14635,28.37035],[92.93075,28.25671],[92.67486,28.15018],[92.65472,28.07632],[92.73025,28.05814],[92.7275,27.98662],[92.42538,27.80092],[92.32101,27.79363],[92.27432,27.89077],[91.87057,27.7195],[91.84722,27.76325],[91.6469,27.76358],[91.55819,27.6144]]]}},{"type":"Feature","properties":{"id":"US-WI"},"geometry":{"type":"Polygon","coordinates":[[[-92.88416,45.56978],[-92.76056,45.56786],[-92.65207,45.40804],[-92.76056,45.28643],[-92.77017,44.82953],[-92.82511,44.74132],[-91.9874,44.3832],[-91.65094,44.06533],[-91.42572,43.99227],[-91.24582,43.77649],[-91.22537,43.4989],[-91.21439,43.38123],[-91.09628,43.31932],[-91.07706,43.27334],[-91.16769,43.17528],[-91.15121,43.02085],[-91.1622,42.93646],[-91.09354,42.8761],[-91.08804,42.7774],[-91.02487,42.7068],[-90.70901,42.65026],[-90.64245,42.50785],[-87.02282,42.49706],[-86.25116,45.23713],[-86.7428,45.44467],[-87.09574,45.44563],[-87.41022,45.20134],[-87.44318,45.07735],[-87.65741,45.10643],[-87.74118,45.19554],[-87.64368,45.34534],[-87.69724,45.38972],[-87.75903,45.35209],[-87.87027,45.35499],[-87.85791,45.43888],[-87.8071,45.47067],[-87.80435,45.53805],[-87.84143,45.57074],[-87.79474,45.56209],[-87.77414,45.6063],[-87.83182,45.65912],[-87.77826,45.67639],[-87.86752,45.75023],[-88.01172,45.79333],[-88.0845,45.78088],[-88.13669,45.82205],[-88.07901,45.87371],[-88.10098,45.9234],[-88.30835,45.95587],[-88.51846,46.02362],[-88.64618,45.98832],[-88.8096,46.02457],[-89.09387,46.14078],[-90.11148,46.3355],[-90.2186,46.50212],[-90.33396,46.5522],[-90.39026,46.53331],[-90.42047,46.56636],[-89.48837,48.01412],[-92.02448,46.70498],[-92.1055,46.75016],[-92.203,46.69744],[-92.2085,46.65221],[-92.29227,46.66258],[-92.29639,46.07794],[-92.72348,45.889],[-92.78803,45.76364],[-92.8718,45.72051],[-92.88416,45.56978]]]}},{"type":"Feature","properties":{"id":"US-WY"},"geometry":{"type":"Polygon","coordinates":[[[-111.05503,44.99947],[-111.04897,44.47412],[-111.04628,42.00049],[-111.04675,40.99766],[-109.05003,41.00069],[-104.0521,41.00188],[-104.05548,43.00258],[-104.05897,44.99972],[-111.05503,44.99947]]]}},{"type":"Feature","properties":{"id":"US-MS"},"geometry":{"type":"Polygon","coordinates":[[[-91.63317,31.00153],[-89.73529,31.00389],[-89.85339,30.67373],[-89.77374,30.5177],[-89.69134,30.46089],[-89.58972,30.1835],[-88.37952,30.00457],[-88.47496,31.89065],[-88.09868,34.89407],[-88.14949,34.9211],[-88.20029,34.99532],[-90.31242,34.99989],[-90.24925,34.93349],[-90.56236,34.72946],[-90.58846,34.49776],[-91.08696,33.96299],[-91.14052,33.29866],[-91.16798,33.00432],[-90.90533,32.31769],[-91.44641,31.54147],[-91.63317,31.00153]]]}},{"type":"Feature","properties":{"id":"US-MO"},"geometry":{"type":"Polygon","coordinates":[[[-95.77211,40.58647],[-95.65168,40.39775],[-95.31497,40.00166],[-94.87654,39.82391],[-94.88272,39.79542],[-94.93251,39.78804],[-94.93491,39.77221],[-94.90779,39.75875],[-94.86865,39.773],[-94.85903,39.75769],[-94.86247,39.74133],[-95.02177,39.67371],[-95.10279,39.57851],[-95.10348,39.53404],[-94.92839,39.38561],[-94.89405,39.39622],[-94.87895,39.37446],[-94.91122,39.35216],[-94.90298,39.30383],[-94.77664,39.20174],[-94.77011,39.18684],[-94.75157,39.17194],[-94.72239,39.16874],[-94.67845,39.18498],[-94.66094,39.17673],[-94.66128,39.15863],[-94.64686,39.1533],[-94.60601,39.16129],[-94.58953,39.15384],[-94.5909,39.13759],[-94.60772,39.11895],[-94.61795,36.9986],[-94.61906,36.49995],[-90.1545,36.49885],[-90.13939,36.41711],[-90.07347,36.39833],[-90.07759,36.27442],[-90.11467,36.26446],[-90.37834,35.99826],[-89.73701,36.00048],[-89.61332,36.10897],[-89.58997,36.1478],[-89.6916,36.24089],[-89.57212,36.24754],[-89.5474,36.43117],[-89.52268,36.46762],[-89.57212,36.56144],[-89.52268,36.5846],[-89.47187,36.55813],[-89.48972,36.46541],[-89.45127,36.46431],[-89.41668,36.50778],[-89.36175,36.63352],[-89.22168,36.56737],[-89.10907,36.98097],[-89.19146,36.9678],[-89.19536,37.01862],[-89.286,37.09314],[-89.28325,36.99011],[-89.38488,37.04274],[-89.46728,37.20698],[-89.52495,37.28788],[-89.49199,37.3403],[-89.42882,37.35559],[-89.43706,37.4385],[-89.52221,37.52786],[-89.52221,37.69322],[-89.83806,37.90374],[-90.18139,38.07258],[-90.35168,38.20436],[-90.37914,38.32728],[-90.29674,38.43278],[-90.26104,38.54027],[-90.19031,38.60094],[-90.17795,38.64385],[-90.18894,38.68138],[-90.21366,38.70604],[-90.2116,38.73015],[-90.16079,38.7778],[-90.12234,38.79761],[-90.1086,38.84789],[-90.25554,38.91951],[-90.41141,38.96438],[-90.46429,38.96705],[-90.50342,38.90616],[-90.55767,38.87088],[-90.59475,38.87302],[-90.66822,38.94035],[-90.71491,39.05828],[-90.67989,39.09612],[-90.72933,39.25847],[-91.04656,39.45756],[-91.37615,39.73481],[-91.36242,39.80026],[-91.44756,39.8551],[-91.52996,40.18533],[-91.44307,40.37471],[-91.73074,40.61201],[-95.77211,40.58647]]]}},{"type":"Feature","properties":{"id":"US-MT"},"geometry":{"type":"Polygon","coordinates":[[[-116.04938,48.99999],[-116.04835,47.97742],[-115.68992,47.60746],[-115.75859,47.55002],[-115.63225,47.47953],[-115.75859,47.43311],[-115.33562,47.25631],[-114.61052,46.64322],[-114.31938,46.64887],[-114.49516,46.04407],[-114.39628,45.88752],[-114.55559,45.77653],[-114.54735,45.5731],[-114.33861,45.46148],[-113.98155,45.70752],[-113.82225,45.59809],[-113.75084,45.34385],[-113.45421,45.05742],[-113.44047,44.97005],[-113.5009,44.93506],[-113.3361,44.7871],[-113.25645,44.82802],[-113.13286,44.7754],[-113.00926,44.45476],[-112.82249,44.36255],[-112.83897,44.43712],[-112.71812,44.50571],[-112.39403,44.44888],[-112.31163,44.55662],[-111.48765,44.539],[-111.51237,44.64267],[-111.38328,44.75395],[-111.04897,44.47412],[-111.05503,44.99947],[-104.05897,44.99972],[-104.05691,45.94728],[-104.05004,48.99925],[-110.0051,48.99901],[-114.0683,48.99885],[-116.04938,48.99999]]]}},{"type":"Feature","properties":{"id":"US-SD"},"geometry":{"type":"Polygon","coordinates":[[[-104.05897,44.99972],[-104.05548,43.00258],[-98.50401,43.00058],[-98.44908,42.93275],[-97.99383,42.76612],[-97.95057,42.77065],[-97.84826,42.86685],[-97.44314,42.84773],[-97.40125,42.86736],[-97.25019,42.85729],[-97.11836,42.76813],[-97.00575,42.76561],[-96.94875,42.71922],[-96.88627,42.73536],[-96.69264,42.64805],[-96.71461,42.60561],[-96.60475,42.50344],[-96.55187,42.51963],[-96.47134,42.49249],[-96.49057,42.57547],[-96.63614,42.74513],[-96.43838,43.11516],[-96.48233,43.22133],[-96.54275,43.22733],[-96.58121,43.28334],[-96.52902,43.31333],[-96.60043,43.50089],[-96.45334,43.50083],[-96.45334,45.29706],[-96.49591,45.36367],[-96.68405,45.41382],[-96.86258,45.62263],[-96.65109,45.74735],[-96.58106,45.82588],[-96.56732,45.93486],[-104.05691,45.94728],[-104.05897,44.99972]]]}},{"type":"Feature","properties":{"id":"US-NE"},"geometry":{"type":"Polygon","coordinates":[[[-104.05548,43.00258],[-104.0521,41.00188],[-102.05165,41.00235],[-102.04923,40.00324],[-95.31497,40.00166],[-95.65168,40.39775],[-95.77211,40.58647],[-95.84238,40.67557],[-95.88495,41.05568],[-95.86298,41.08829],[-95.88426,41.14985],[-95.87534,41.16587],[-95.84581,41.16484],[-95.84512,41.18035],[-95.86092,41.18861],[-95.92203,41.18655],[-95.93095,41.20566],[-95.91173,41.23046],[-95.92203,41.26917],[-95.87602,41.28569],[-95.8719,41.30426],[-95.88701,41.31922],[-95.93507,41.32489],[-95.95224,41.34603],[-95.91928,41.45728],[-96.08682,41.53956],[-96.14999,41.9678],[-96.25162,42.02699],[-96.41641,42.40737],[-96.3862,42.4317],[-96.38071,42.46616],[-96.40268,42.49249],[-96.47134,42.49249],[-96.55187,42.51963],[-96.60475,42.50344],[-96.71461,42.60561],[-96.69264,42.64805],[-96.88627,42.73536],[-96.94875,42.71922],[-97.00575,42.76561],[-97.11836,42.76813],[-97.25019,42.85729],[-97.40125,42.86736],[-97.44314,42.84773],[-97.84826,42.86685],[-97.95057,42.77065],[-97.99383,42.76612],[-98.44908,42.93275],[-98.50401,43.00058],[-104.05548,43.00258]]]}},{"type":"Feature","properties":{"id":"US-NV"},"geometry":{"type":"Polygon","coordinates":[[[-120.0016,41.99495],[-120.00023,38.99862],[-114.64165,35.01451],[-114.60732,35.07635],[-114.64577,35.10444],[-114.62791,35.12129],[-114.5826,35.12803],[-114.57024,35.15498],[-114.59633,35.33331],[-114.68147,35.49783],[-114.65263,35.60956],[-114.68971,35.65197],[-114.70482,35.85592],[-114.66774,35.87039],[-114.74602,35.98271],[-114.74052,36.0127],[-114.72404,36.03159],[-114.75014,36.09486],[-114.63066,36.14367],[-114.61281,36.13036],[-114.57298,36.15254],[-114.37523,36.147],[-114.30931,36.06379],[-114.2379,36.01715],[-114.14864,36.02936],[-114.12254,36.11372],[-114.04564,36.1991],[-114.05113,37.00171],[-114.04073,42.00031],[-117.02619,42.00013],[-120.0016,41.99495]]]}},{"type":"Feature","properties":{"id":"US-UT"},"geometry":{"type":"Polygon","coordinates":[[[-114.05113,37.00171],[-109.04686,37.00061],[-109.05003,41.00069],[-111.04675,40.99766],[-111.04628,42.00049],[-114.04073,42.00031],[-114.05113,37.00171]]]}},{"type":"Feature","properties":{"id":"US-ND"},"geometry":{"type":"Polygon","coordinates":[[[-104.05691,45.94728],[-96.56732,45.93486],[-96.60303,46.32981],[-96.72525,46.45011],[-96.7898,46.63902],[-96.79666,46.81035],[-96.76233,46.92114],[-96.83511,47.0083],[-96.85983,47.60148],[-97.02738,47.89144],[-97.01639,47.91905],[-97.05484,47.9485],[-97.14685,48.17245],[-97.15372,48.59745],[-97.09604,48.68275],[-97.24024,48.99952],[-101.36198,48.99935],[-104.05004,48.99925],[-104.05691,45.94728]]]}},{"type":"Feature","properties":{"id":"US-NH"},"geometry":{"type":"Polygon","coordinates":[[[-72.55705,42.85427],[-72.54331,42.81147],[-72.51173,42.78174],[-72.51722,42.7636],[-72.49456,42.77368],[-72.45969,42.72515],[-71.29465,42.69651],[-71.25139,42.74343],[-71.18273,42.7399],[-71.18616,42.79484],[-71.13878,42.82305],[-71.06531,42.80744],[-71.04196,42.85981],[-70.96781,42.86887],[-70.92592,42.88698],[-70.88541,42.88446],[-70.84627,42.86182],[-70.81606,42.8739],[-70.04999,42.81005],[-70.70216,43.04463],[-70.70422,43.07623],[-70.73786,43.07272],[-70.83331,43.13238],[-70.81271,43.23052],[-70.89648,43.29052],[-70.93493,43.33549],[-70.96377,43.33749],[-70.98849,43.3884],[-70.95278,43.55783],[-70.97613,43.56977],[-71.08364,45.30623],[-71.14568,45.24128],[-71.19723,45.25438],[-71.22338,45.25184],[-71.29371,45.29996],[-71.37133,45.24624],[-71.44252,45.2361],[-71.40364,45.21382],[-71.42778,45.12624],[-71.48735,45.07784],[-71.50067,45.01357],[-71.54287,44.98758],[-71.49618,44.90788],[-71.55386,44.86214],[-71.57583,44.79592],[-71.63351,44.74912],[-71.54287,44.58601],[-71.56347,44.56351],[-71.59231,44.56351],[-71.59368,44.49302],[-71.639,44.47343],[-71.7008,44.41558],[-71.79693,44.39891],[-71.81753,44.35866],[-71.86834,44.33706],[-71.90679,44.34688],[-71.9782,44.33411],[-72.0249,44.31741],[-72.06335,44.27514],[-72.04,44.08408],[-72.11141,43.9977],[-72.12309,43.9201],[-72.16291,43.8919],[-72.20617,43.77302],[-72.30093,43.70754],[-72.32702,43.63553],[-72.40049,43.51265],[-72.38127,43.49472],[-72.41491,43.36557],[-72.39362,43.35659],[-72.45611,43.14654],[-72.441,43.13702],[-72.43688,43.08388],[-72.46092,43.05479],[-72.46504,42.98099],[-72.53233,42.95134],[-72.52546,42.93626],[-72.53301,42.89553],[-72.55361,42.88497],[-72.55705,42.85427]]]}},{"type":"Feature","properties":{"id":"US-NJ"},"geometry":{"type":"Polygon","coordinates":[[[-75.56053,39.45773],[-75.38063,39.30382],[-75.01808,38.79724],[-74.98718,38.4507],[-73.81773,39.66512],[-73.9166,40.514],[-74.23109,40.47954],[-74.2613,40.49625],[-74.25615,40.51427],[-74.24722,40.52236],[-74.25134,40.54532],[-74.23418,40.55914],[-74.21839,40.55836],[-74.19985,40.59956],[-74.20397,40.63162],[-74.18577,40.64647],[-74.12775,40.6436],[-74.05668,40.65402],[-74.02612,40.69959],[-74.01445,40.7589],[-73.95197,40.8509],[-73.92072,40.91268],[-73.89463,40.99461],[-74.32825,41.18583],[-74.6956,41.35872],[-74.76152,41.33913],[-74.83431,41.28136],[-74.88237,41.1848],[-75.02931,41.03995],[-75.13849,40.98347],[-75.05266,40.8657],[-75.06777,40.84804],[-75.09935,40.84804],[-75.08219,40.82726],[-75.133,40.77373],[-75.17076,40.77893],[-75.19617,40.75084],[-75.18175,40.73107],[-75.20372,40.691],[-75.18003,40.66939],[-75.20029,40.64881],[-75.19068,40.63865],[-75.19102,40.62093],[-75.19994,40.61259],[-75.19068,40.59435],[-75.1948,40.57844],[-75.18106,40.56619],[-75.16458,40.56332],[-75.14776,40.57453],[-75.12682,40.57453],[-75.10347,40.56984],[-75.06708,40.5388],[-75.06365,40.48581],[-75.07017,40.45525],[-75.06124,40.4218],[-75.03137,40.40534],[-74.98743,40.40664],[-74.96477,40.39592],[-74.94966,40.36611],[-74.94451,40.34152],[-74.91396,40.3177],[-74.89198,40.31246],[-74.86315,40.28837],[-74.84426,40.25013],[-74.82504,40.24069],[-74.78349,40.2234],[-74.76701,40.2095],[-74.75843,40.18774],[-74.73852,40.17855],[-74.72273,40.16045],[-74.72341,40.14864],[-74.74127,40.13473],[-74.75843,40.13447],[-74.7907,40.12187],[-74.81542,40.1287],[-74.82641,40.12633],[-74.83774,40.10244],[-74.8628,40.08432],[-74.91224,40.06987],[-74.9246,40.07224],[-74.97164,40.05279],[-75.01352,40.0202],[-75.04682,40.01099],[-75.0712,39.97969],[-75.09901,39.97443],[-75.13334,39.95786],[-75.13815,39.93627],[-75.12785,39.91073],[-75.13986,39.88716],[-75.16905,39.88294],[-75.26809,39.85014],[-75.33745,39.84842],[-75.41621,39.80165],[-75.40672,39.7962],[-75.46131,39.77457],[-75.55641,39.63352],[-75.56053,39.45773]]]}},{"type":"Feature","properties":{"id":"US-NM"},"geometry":{"type":"Polygon","coordinates":[[[-109.05235,31.3333],[-108.20979,31.33316],[-108.20899,31.78534],[-106.529,31.784],[-106.5436,31.80546],[-106.60333,31.82938],[-106.64522,31.89178],[-106.62187,31.92151],[-106.61981,32.00074],[-103.06402,31.99986],[-103.04239,36.49992],[-103.00325,36.50047],[-103.00462,36.99417],[-109.04686,37.00061],[-109.05235,31.3333]]]}},{"type":"Feature","properties":{"id":"CN-HA"},"geometry":{"type":"Polygon","coordinates":[[[110.35388,34.519],[110.6385,34.18056],[110.587,33.89093],[110.84106,33.67978],[111.0189,33.56771],[110.9801,33.2605],[111.18163,33.10534],[111.29665,32.85622],[111.46488,32.73732],[111.57474,32.59455],[111.67293,32.62636],[112.0008,32.45864],[112.31323,32.32862],[112.55149,32.39851],[113.2505,32.39213],[113.41804,32.27784],[113.71467,32.43329],[113.75038,32.26855],[113.7284,32.08432],[113.84582,31.84314],[113.9859,31.75736],[114.18708,31.85452],[114.58465,31.71648],[114.83184,31.45092],[115.21018,31.58204],[115.36165,31.40228],[115.4718,31.65045],[115.64002,31.76145],[115.87005,31.77195],[115.93872,32.06861],[115.86181,32.45705],[115.91812,32.57922],[115.56312,32.38634],[115.44982,32.53813],[115.1889,32.59946],[115.20675,32.84844],[114.88128,32.97295],[114.91287,33.14387],[115.13465,33.08348],[115.30769,33.20709],[115.34957,33.5185],[115.62286,33.57115],[115.54801,33.8821],[115.59059,34.02563],[115.76431,34.07598],[115.99845,33.97439],[115.96034,33.90376],[116.05545,33.85787],[116.03176,33.83563],[116.14986,33.71148],[116.22951,33.72148],[116.63806,33.8858],[116.64974,33.96585],[116.53266,34.09588],[116.57867,34.27537],[116.2429,34.37177],[116.19792,34.51759],[116.09596,34.60665],[115.67916,34.55577],[115.45394,34.63659],[115.42545,34.8014],[115.19439,34.90817],[115.21465,34.94814],[115.13122,35.00187],[114.82429,34.994],[114.91218,35.19962],[115.07903,35.40416],[115.32966,35.47744],[115.41206,35.64111],[115.67985,35.76211],[115.87005,35.91185],[116.03073,35.963],[116.0939,36.11069],[115.64414,35.91936],[115.49171,35.92297],[115.35644,35.77771],[115.35026,35.95021],[115.43197,36.01078],[115.48072,36.15506],[115.31936,36.07518],[115.19714,36.22544],[114.91493,36.04354],[114.91287,36.13066],[114.61418,36.12345],[114.02435,36.33172],[113.72909,36.36103],[113.65287,35.83507],[113.57597,35.81614],[113.60961,35.67626],[113.48945,35.52943],[113.02802,35.35937],[112.76916,35.20635],[112.05505,35.27981],[112.03994,35.04517],[111.82228,35.07159],[111.57508,34.84649],[111.22695,34.79294],[111.1576,34.81831],[110.8905,34.65354],[110.41122,34.58432],[110.35388,34.519]]]}},{"type":"Feature","properties":{"id":"US-NY"},"geometry":{"type":"Polygon","coordinates":[[[-79.77073,42.55308],[-79.76349,42.00107],[-75.3772,42.00515],[-75.08057,41.80278],[-75.0531,41.59155],[-74.98718,41.48258],[-74.74274,41.42288],[-74.6956,41.35872],[-73.89463,40.99461],[-73.92072,40.91268],[-73.95197,40.8509],[-74.01445,40.7589],[-74.02612,40.69959],[-74.05668,40.65402],[-74.12775,40.6436],[-74.18577,40.64647],[-74.20397,40.63162],[-74.19985,40.59956],[-74.21839,40.55836],[-74.23418,40.55914],[-74.25134,40.54532],[-74.24722,40.52236],[-74.25615,40.51427],[-74.2613,40.49625],[-74.23109,40.47954],[-73.9166,40.514],[-73.81773,39.66512],[-71.6391,40.94332],[-71.85736,41.32188],[-73.62136,40.9494],[-73.65981,40.98854],[-73.65723,40.99062],[-73.65947,40.99373],[-73.66067,41.00098],[-73.65535,41.01225],[-73.72761,41.10079],[-73.48283,41.21298],[-73.5508,41.2957],[-73.48746,42.0497],[-73.50942,42.0867],[-73.26498,42.74494],[-73.25409,43.57117],[-73.33649,43.62686],[-73.43811,43.57117],[-73.35571,43.77182],[-73.4436,44.07055],[-73.3255,44.25969],[-73.35025,45.00942],[-74.32699,44.99029],[-74.66689,45.00646],[-74.8447,45.00606],[-74.99101,44.98051],[-75.01363,44.95608],[-75.2193,44.87821],[-75.41441,44.76614],[-75.76813,44.51537],[-75.8217,44.43176],[-75.95947,44.34463],[-76.00018,44.34896],[-76.16285,44.28262],[-76.1664,44.23051],[-76.244,44.19643],[-76.31222,44.19894],[-76.35324,44.13493],[-76.43859,44.09393],[-76.79706,43.63099],[-79.25796,43.54052],[-79.06921,43.26183],[-79.05512,43.25375],[-79.05544,43.21224],[-79.05002,43.20133],[-79.05384,43.17418],[-79.04652,43.16396],[-79.0427,43.13934],[-79.06881,43.12029],[-79.05671,43.10937],[-79.07486,43.07845],[-79.01055,43.06659],[-78.99941,43.05612],[-79.02424,43.01983],[-79.02074,42.98444],[-78.98126,42.97],[-78.96312,42.95509],[-78.93224,42.95229],[-78.90905,42.93022],[-78.90712,42.89733],[-78.93684,42.82887],[-79.77073,42.55308]]]}},{"type":"Feature","properties":{"id":"US-NC"},"geometry":{"type":"Polygon","coordinates":[[[-84.32551,34.99393],[-83.10796,35.0011],[-82.39746,35.20044],[-81.04889,35.15105],[-81.04889,35.04993],[-80.9198,35.08815],[-80.79346,34.94193],[-80.78796,34.82252],[-79.68109,34.81124],[-77.99552,33.38485],[-74.86753,35.41538],[-75.79776,36.55091],[-80.30148,36.54837],[-81.66962,36.58987],[-81.72455,36.33806],[-81.82617,36.36682],[-82.06238,36.11872],[-82.22992,36.16086],[-82.57324,35.96103],[-82.64191,36.06989],[-82.97424,35.78744],[-83.13904,35.76961],[-83.50159,35.56433],[-83.74603,35.56209],[-84.0097,35.4324],[-84.03717,35.29129],[-84.1333,35.24419],[-84.22943,35.27335],[-84.28711,35.224],[-84.32551,34.99393]]]}},{"type":"Feature","properties":{"id":"US-PA"},"geometry":{"type":"Polygon","coordinates":[[[-80.53581,42.29896],[-80.51902,40.63803],[-80.52292,39.72222],[-79.47663,39.72086],[-75.78987,39.72204],[-75.77476,39.7223],[-75.76,39.75002],[-75.74043,39.77378],[-75.72532,39.78644],[-75.71434,39.79515],[-75.68172,39.81387],[-75.65975,39.82337],[-75.62713,39.83259],[-75.59898,39.83734],[-75.57632,39.83945],[-75.53959,39.83945],[-75.49942,39.83365],[-75.46783,39.82548],[-75.43796,39.81414],[-75.41621,39.80165],[-75.33745,39.84842],[-75.26809,39.85014],[-75.16905,39.88294],[-75.13986,39.88716],[-75.12785,39.91073],[-75.13815,39.93627],[-75.13334,39.95786],[-75.09901,39.97443],[-75.0712,39.97969],[-75.04682,40.01099],[-75.01352,40.0202],[-74.97164,40.05279],[-74.9246,40.07224],[-74.91224,40.06987],[-74.8628,40.08432],[-74.83774,40.10244],[-74.82641,40.12633],[-74.81542,40.1287],[-74.7907,40.12187],[-74.75843,40.13447],[-74.74127,40.13473],[-74.72341,40.14864],[-74.72273,40.16045],[-74.73852,40.17855],[-74.75843,40.18774],[-74.76701,40.2095],[-74.78349,40.2234],[-74.82504,40.24069],[-74.84426,40.25013],[-74.86315,40.28837],[-74.89198,40.31246],[-74.91396,40.3177],[-74.94451,40.34152],[-74.94966,40.36611],[-74.96477,40.39592],[-74.98743,40.40664],[-75.03137,40.40534],[-75.06124,40.4218],[-75.07017,40.45525],[-75.06365,40.48581],[-75.06708,40.5388],[-75.10347,40.56984],[-75.12682,40.57453],[-75.14776,40.57453],[-75.16458,40.56332],[-75.18106,40.56619],[-75.1948,40.57844],[-75.19068,40.59435],[-75.19994,40.61259],[-75.19102,40.62093],[-75.19068,40.63865],[-75.20029,40.64881],[-75.18003,40.66939],[-75.20372,40.691],[-75.18175,40.73107],[-75.19617,40.75084],[-75.17076,40.77893],[-75.133,40.77373],[-75.08219,40.82726],[-75.09935,40.84804],[-75.06777,40.84804],[-75.05266,40.8657],[-75.13849,40.98347],[-75.02931,41.03995],[-74.88237,41.1848],[-74.83431,41.28136],[-74.76152,41.33913],[-74.6956,41.35872],[-74.74274,41.42288],[-74.98718,41.48258],[-75.0531,41.59155],[-75.08057,41.80278],[-75.3772,42.00515],[-79.76349,42.00107],[-79.77073,42.55308],[-80.53581,42.29896]]]}},{"type":"Feature","properties":{"id":"US-OR"},"geometry":{"type":"Polygon","coordinates":[[[-125.69978,42.00605],[-120.0016,41.99495],[-117.02619,42.00013],[-117.02923,43.83121],[-116.99215,43.8629],[-116.96194,43.91734],[-116.97704,43.96282],[-116.93859,43.98654],[-116.93722,44.03198],[-116.97704,44.05172],[-116.97429,44.08921],[-116.94271,44.09513],[-116.89602,44.15526],[-116.90151,44.17792],[-116.97155,44.19565],[-116.97841,44.24387],[-117.18304,44.26453],[-117.22149,44.30238],[-117.18921,44.34266],[-117.24003,44.37408],[-117.22492,44.48392],[-117.15626,44.53093],[-117.05051,44.74298],[-116.9379,44.78588],[-116.83353,44.92995],[-116.85825,44.97563],[-116.84864,45.02321],[-116.75388,45.11342],[-116.67972,45.3185],[-116.46274,45.60746],[-116.54789,45.7533],[-116.77585,45.82129],[-116.91799,45.99567],[-118.98743,46.00052],[-119.1378,45.92797],[-119.26826,45.94086],[-119.4928,45.90647],[-119.6109,45.92654],[-119.67064,45.85772],[-119.96658,45.82375],[-120.16502,45.7663],[-120.21103,45.72701],[-120.50079,45.69537],[-120.56053,45.74043],[-120.63538,45.74714],[-120.89493,45.65219],[-121.08307,45.65075],[-121.14899,45.60609],[-121.19156,45.61282],[-121.21284,45.66802],[-121.34674,45.70592],[-121.41266,45.69489],[-121.53008,45.7227],[-121.71478,45.69489],[-121.81366,45.71215],[-121.90097,45.67502],[-121.9891,45.62074],[-122.14899,45.58514],[-122.23595,45.55275],[-122.32384,45.54697],[-122.38014,45.5739],[-122.43919,45.5638],[-122.47456,45.57942],[-122.67609,45.61786],[-122.76501,45.65747],[-122.77668,45.68745],[-122.76157,45.7378],[-122.76501,45.76654],[-122.79591,45.81203],[-122.78492,45.86704],[-122.81445,45.91627],[-122.81033,45.96068],[-122.87762,46.03414],[-122.8941,46.07797],[-122.96688,46.10654],[-123.00945,46.13605],[-123.13442,46.18647],[-123.17837,46.18694],[-123.28754,46.14367],[-123.38093,46.14842],[-123.43243,46.18219],[-123.42968,46.23351],[-123.47431,46.26864],[-123.58349,46.25583],[-123.87806,46.23588],[-123.92818,46.23968],[-124.02981,46.30281],[-124.03873,46.26295],[-124.14997,46.26295],[-125.2772,46.2631],[-125.69978,42.00605]]]}},{"type":"Feature","properties":{"id":"CN-SH"},"geometry":{"type":"Polygon","coordinates":[[[120.85475,31.10938],[120.89836,31.09028],[120.89424,31.01822],[120.98659,31.01939],[120.98865,30.89603],[121.03122,30.82265],[121.12529,30.86509],[121.12152,30.78018],[121.21456,30.78726],[121.26434,30.68516],[123.5458,31.01942],[122.29378,31.76513],[121.76147,31.63818],[121.44286,31.76086],[121.30554,31.88338],[121.09954,31.75853],[121.3821,31.54723],[121.25335,31.47933],[121.23722,31.49704],[121.14246,31.44492],[121.15447,31.41108],[121.11173,31.37459],[121.12581,31.30348],[121.14057,31.31038],[121.15722,31.28515],[121.1495,31.27752],[121.11808,31.28529],[121.06006,31.27356],[121.05903,31.23658],[121.07276,31.16345],[121.03671,31.14171],[120.88325,31.14083],[120.85475,31.10938]]]}},{"type":"Feature","properties":{"id":"US-WA"},"geometry":{"type":"Polygon","coordinates":[[[-125.2772,46.2631],[-124.14997,46.26295],[-124.03873,46.26295],[-124.02981,46.30281],[-123.92818,46.23968],[-123.87806,46.23588],[-123.58349,46.25583],[-123.47431,46.26864],[-123.42968,46.23351],[-123.43243,46.18219],[-123.38093,46.14842],[-123.28754,46.14367],[-123.17837,46.18694],[-123.13442,46.18647],[-123.00945,46.13605],[-122.96688,46.10654],[-122.8941,46.07797],[-122.87762,46.03414],[-122.81033,45.96068],[-122.81445,45.91627],[-122.78492,45.86704],[-122.79591,45.81203],[-122.76501,45.76654],[-122.76157,45.7378],[-122.77668,45.68745],[-122.76501,45.65747],[-122.67609,45.61786],[-122.47456,45.57942],[-122.43919,45.5638],[-122.38014,45.5739],[-122.32384,45.54697],[-122.23595,45.55275],[-122.14899,45.58514],[-121.9891,45.62074],[-121.90097,45.67502],[-121.81366,45.71215],[-121.71478,45.69489],[-121.53008,45.7227],[-121.41266,45.69489],[-121.34674,45.70592],[-121.21284,45.66802],[-121.19156,45.61282],[-121.14899,45.60609],[-121.08307,45.65075],[-120.89493,45.65219],[-120.63538,45.74714],[-120.56053,45.74043],[-120.50079,45.69537],[-120.21103,45.72701],[-120.16502,45.7663],[-119.96658,45.82375],[-119.67064,45.85772],[-119.6109,45.92654],[-119.4928,45.90647],[-119.26826,45.94086],[-119.1378,45.92797],[-118.98743,46.00052],[-116.91799,45.99567],[-116.94958,46.07004],[-116.98116,46.08242],[-116.92348,46.16048],[-117.03472,46.34138],[-117.04811,46.34281],[-117.06184,46.34873],[-117.06287,46.3665],[-117.04914,46.37787],[-117.03575,46.40794],[-117.03644,46.42309],[-117.04021,46.4257],[-117.03266,49.00056],[-123.32163,49.00419],[-123.0093,48.83186],[-123.0093,48.76586],[-123.26565,48.6959],[-123.15614,48.35395],[-123.50039,48.21223],[-125.03842,48.53282],[-125.2772,46.2631]]]}},{"type":"Feature","properties":{"id":"US-OK"},"geometry":{"type":"Polygon","coordinates":[[[-103.00462,36.99417],[-103.00325,36.50047],[-100.00134,36.50078],[-99.99928,34.56197],[-99.9725,34.56197],[-99.95464,34.5778],[-99.92443,34.57836],[-99.76444,34.42841],[-99.71638,34.40462],[-99.71226,34.38932],[-99.66557,34.37515],[-99.59965,34.37629],[-99.58523,34.38989],[-99.58111,34.41821],[-99.50489,34.41198],[-99.43554,34.37515],[-99.39915,34.37799],[-99.39434,34.44257],[-99.37717,34.45899],[-99.23916,34.36439],[-99.18354,34.22029],[-99.04415,34.20042],[-98.98579,34.22313],[-98.75164,34.1277],[-98.64452,34.1635],[-98.58616,34.15384],[-98.49277,34.06572],[-98.409,34.09074],[-98.37879,34.15384],[-98.16662,34.11462],[-98.12061,34.15725],[-98.08834,34.13111],[-98.12061,34.07653],[-98.08697,34.00483],[-97.95033,33.99459],[-97.98191,33.89547],[-97.87067,33.85043],[-97.81368,33.87552],[-97.68116,33.99117],[-97.59258,33.95701],[-97.59327,33.90687],[-97.55138,33.89832],[-97.49783,33.92169],[-97.45251,33.89718],[-97.45731,33.83218],[-97.40513,33.82135],[-97.3605,33.82648],[-97.30762,33.88521],[-97.26849,33.85899],[-97.23553,33.91713],[-97.1854,33.90402],[-97.16686,33.84473],[-97.20394,33.81735],[-97.18334,33.75115],[-97.15038,33.72317],[-97.09888,33.72603],[-97.08515,33.75857],[-97.09614,33.80252],[-97.05221,33.82252],[-97.08515,33.85614],[-97.02691,33.84657],[-96.98215,33.89376],[-96.99245,33.93365],[-96.93958,33.95871],[-96.90113,33.94163],[-96.86954,33.85386],[-96.81118,33.87267],[-96.75762,33.82648],[-96.70887,33.8396],[-96.6663,33.91599],[-96.58733,33.89262],[-96.62441,33.85157],[-96.57497,33.82021],[-96.52622,33.82591],[-96.507,33.77342],[-96.42735,33.78026],[-96.35044,33.68661],[-96.32023,33.7049],[-96.29002,33.7757],[-96.22959,33.75743],[-96.18565,33.75743],[-96.16367,33.82135],[-95.93296,33.88749],[-95.84232,33.84416],[-95.57041,33.93992],[-95.54295,33.89661],[-95.43034,33.87381],[-95.28477,33.88521],[-95.2216,33.96726],[-95.10349,33.92397],[-94.88651,33.76657],[-94.48448,33.64004],[-94.43092,35.38371],[-94.61906,36.49995],[-94.61795,36.9986],[-102.04214,36.99314],[-103.00462,36.99417]]]}},{"type":"Feature","properties":{"id":"US-TX"},"geometry":{"type":"Polygon","coordinates":[[[-106.64522,31.89178],[-106.60333,31.82938],[-106.5436,31.80546],[-106.529,31.784],[-106.52266,31.77509],[-106.51251,31.76922],[-106.50962,31.76155],[-106.50111,31.75714],[-106.48815,31.74769],[-106.47298,31.75054],[-106.46726,31.75998],[-106.45244,31.76523],[-106.43419,31.75478],[-106.41773,31.75196],[-106.38003,31.73151],[-106.3718,31.71165],[-106.34864,31.69663],[-106.33419,31.66303],[-106.30305,31.62154],[-106.28084,31.56173],[-106.24612,31.54193],[-106.23711,31.51262],[-106.20346,31.46305],[-106.09025,31.40569],[-106.00363,31.39181],[-104.77674,30.4236],[-104.5171,29.64671],[-104.3969,29.57105],[-104.39363,29.55396],[-104.37752,29.54255],[-103.15787,28.93865],[-102.60596,29.8192],[-101.47277,29.7744],[-101.05686,29.44738],[-101.01128,29.36947],[-100.96725,29.3477],[-100.94579,29.34523],[-100.94056,29.33371],[-100.87982,29.296],[-100.79696,29.24688],[-100.67294,29.09744],[-100.63689,28.90812],[-100.59809,28.88197],[-100.52313,28.75598],[-100.5075,28.74066],[-100.51222,28.70679],[-100.50029,28.66117],[-99.55409,27.61314],[-99.51478,27.55836],[-99.52955,27.49747],[-99.50208,27.50021],[-99.48045,27.49016],[-99.482,27.47128],[-99.49744,27.43746],[-99.53573,27.30926],[-99.08477,26.39849],[-99.03053,26.41249],[-99.00546,26.3925],[-98.35126,26.15129],[-98.30491,26.10475],[-98.27075,26.09457],[-98.24603,26.07191],[-97.97017,26.05232],[-97.95155,26.0625],[-97.66511,26.01708],[-97.52025,25.88518],[-97.49828,25.89877],[-97.45669,25.86874],[-97.42511,25.83969],[-97.37332,25.83854],[-97.35946,25.92189],[-97.13927,25.96583],[-96.92418,25.97377],[-93.77551,29.43998],[-93.92382,29.81005],[-93.71508,30.05521],[-93.70959,30.28791],[-93.76452,30.33771],[-93.52008,31.03684],[-93.92932,31.91524],[-94.04467,32.00379],[-94.04366,33.01929],[-94.0464,33.55539],[-94.07661,33.57828],[-94.38423,33.56455],[-94.48448,33.64004],[-94.88651,33.76657],[-95.10349,33.92397],[-95.2216,33.96726],[-95.28477,33.88521],[-95.43034,33.87381],[-95.54295,33.89661],[-95.57041,33.93992],[-95.84232,33.84416],[-95.93296,33.88749],[-96.16367,33.82135],[-96.18565,33.75743],[-96.22959,33.75743],[-96.29002,33.7757],[-96.32023,33.7049],[-96.35044,33.68661],[-96.42735,33.78026],[-96.507,33.77342],[-96.52622,33.82591],[-96.57497,33.82021],[-96.62441,33.85157],[-96.58733,33.89262],[-96.6663,33.91599],[-96.70887,33.8396],[-96.75762,33.82648],[-96.81118,33.87267],[-96.86954,33.85386],[-96.90113,33.94163],[-96.93958,33.95871],[-96.99245,33.93365],[-96.98215,33.89376],[-97.02691,33.84657],[-97.08515,33.85614],[-97.05221,33.82252],[-97.09614,33.80252],[-97.08515,33.75857],[-97.09888,33.72603],[-97.15038,33.72317],[-97.18334,33.75115],[-97.20394,33.81735],[-97.16686,33.84473],[-97.1854,33.90402],[-97.23553,33.91713],[-97.26849,33.85899],[-97.30762,33.88521],[-97.3605,33.82648],[-97.40513,33.82135],[-97.45731,33.83218],[-97.45251,33.89718],[-97.49783,33.92169],[-97.55138,33.89832],[-97.59327,33.90687],[-97.59258,33.95701],[-97.68116,33.99117],[-97.81368,33.87552],[-97.87067,33.85043],[-97.98191,33.89547],[-97.95033,33.99459],[-98.08697,34.00483],[-98.12061,34.07653],[-98.08834,34.13111],[-98.12061,34.15725],[-98.16662,34.11462],[-98.37879,34.15384],[-98.409,34.09074],[-98.49277,34.06572],[-98.58616,34.15384],[-98.64452,34.1635],[-98.75164,34.1277],[-98.98579,34.22313],[-99.04415,34.20042],[-99.18354,34.22029],[-99.23916,34.36439],[-99.37717,34.45899],[-99.39434,34.44257],[-99.39915,34.37799],[-99.43554,34.37515],[-99.50489,34.41198],[-99.58111,34.41821],[-99.58523,34.38989],[-99.59965,34.37629],[-99.66557,34.37515],[-99.71226,34.38932],[-99.71638,34.40462],[-99.76444,34.42841],[-99.92443,34.57836],[-99.95464,34.5778],[-99.9725,34.56197],[-99.99928,34.56197],[-100.00134,36.50078],[-103.00325,36.50047],[-103.04239,36.49992],[-103.06402,31.99986],[-106.61981,32.00074],[-106.62187,31.92151],[-106.64522,31.89178]]]}},{"type":"Feature","properties":{"id":"US-OH"},"geometry":{"type":"Polygon","coordinates":[[[-84.81884,39.10708],[-84.74887,39.14851],[-84.68432,39.09844],[-84.62252,39.07392],[-84.56622,39.08671],[-84.54631,39.10163],[-84.51747,39.09151],[-84.49893,39.0995],[-84.47695,39.12188],[-84.4488,39.11922],[-84.4337,39.09577],[-84.43026,39.05739],[-84.41035,39.0446],[-84.34924,39.0382],[-84.30117,38.99445],[-84.29293,38.95334],[-84.23388,38.88176],[-84.23388,38.81331],[-84.07183,38.77263],[-83.95648,38.78762],[-83.79443,38.69765],[-83.7752,38.65047],[-83.66534,38.62902],[-83.62414,38.67621],[-83.52801,38.70408],[-83.33575,38.64618],[-83.31652,38.60112],[-83.27533,38.59897],[-83.24511,38.63116],[-83.20117,38.61614],[-83.14898,38.62258],[-83.10778,38.67621],[-83.0556,38.69336],[-83.02951,38.72872],[-82.97869,38.72765],[-82.89492,38.7555],[-82.8702,38.73943],[-82.87844,38.69121],[-82.84549,38.58502],[-82.79879,38.56355],[-82.72601,38.55603],[-82.61203,38.47329],[-82.59692,38.42382],[-82.54406,38.39937],[-82.31884,38.45101],[-82.28588,38.59283],[-82.17876,38.59713],[-82.21722,38.80933],[-82.14306,38.83714],[-82.1513,38.89488],[-82.08813,38.9782],[-82.02496,39.02943],[-81.93707,38.98888],[-81.90136,38.92694],[-81.93157,38.89702],[-81.89587,38.8735],[-81.84368,38.9013],[-81.83544,38.94831],[-81.76403,38.91839],[-81.74755,38.93762],[-81.786,38.96966],[-81.76128,39.01876],[-81.81622,39.05716],[-81.81622,39.08915],[-81.76952,39.07635],[-81.74206,39.1062],[-81.7503,39.1914],[-81.68987,39.2297],[-81.69537,39.26161],[-81.65417,39.28287],[-81.56902,39.27649],[-81.55529,39.35724],[-81.5086,39.36573],[-81.4674,39.41243],[-81.41796,39.39758],[-81.39874,39.35087],[-81.36303,39.34025],[-81.26141,39.39121],[-81.22021,39.38909],[-81.1845,39.43153],[-81.13232,39.4485],[-81.05541,39.53116],[-80.91534,39.61796],[-80.87139,39.62854],[-80.86384,39.68801],[-80.82813,39.71337],[-80.86555,39.77013],[-80.82092,39.80891],[-80.82436,39.84477],[-80.78899,39.87323],[-80.8065,39.90985],[-80.79655,39.91933],[-80.76702,39.90853],[-80.75329,39.91222],[-80.7605,39.95539],[-80.73818,39.97749],[-80.74265,40.00564],[-80.73097,40.03824],[-80.73681,40.07792],[-80.70591,40.10445],[-80.70763,40.14645],[-80.66917,40.19918],[-80.65475,40.24374],[-80.61871,40.26627],[-80.61733,40.28828],[-80.60017,40.31708],[-80.61253,40.3409],[-80.60944,40.37491],[-80.63313,40.39243],[-80.61356,40.40655],[-80.61424,40.43216],[-80.59776,40.46195],[-80.59639,40.47971],[-80.61287,40.49485],[-80.62935,40.53452],[-80.66608,40.58043],[-80.6345,40.61458],[-80.59948,40.6237],[-80.57837,40.6125],[-80.51902,40.63803],[-80.53581,42.29896],[-82.67862,41.67615],[-83.11184,41.95671],[-83.42355,41.73233],[-84.80645,41.69747],[-84.81884,39.10708]]]}},{"type":"Feature","properties":{"id":"US-RI"},"geometry":{"type":"Polygon","coordinates":[[[-71.85736,41.32188],[-71.6391,40.94332],[-71.10101,41.43444],[-71.13123,41.591],[-71.14153,41.60717],[-71.13432,41.65952],[-71.19577,41.67465],[-71.26135,41.75231],[-71.32898,41.7815],[-71.341,41.79814],[-71.33894,41.89916],[-71.3822,41.89277],[-71.38117,42.0194],[-71.8001,42.00779],[-71.79866,41.41592],[-71.81926,41.41952],[-71.84363,41.40948],[-71.84191,41.39455],[-71.83367,41.38837],[-71.83333,41.37033],[-71.83917,41.3626],[-71.82955,41.34199],[-71.85736,41.32188]]]}},{"type":"Feature","properties":{"id":"US-SC"},"geometry":{"type":"Polygon","coordinates":[[[-83.35447,34.72814],[-83.34829,34.69455],[-83.22916,34.61096],[-83.17148,34.60728],[-83.16873,34.59259],[-83.03655,34.48625],[-83.00085,34.47238],[-82.90231,34.48625],[-82.87519,34.47408],[-82.83365,34.36419],[-82.79657,34.34039],[-82.78043,34.29672],[-82.75057,34.2709],[-82.74095,34.20789],[-82.71658,34.14882],[-82.64311,34.0951],[-82.64105,34.06809],[-82.59538,34.02855],[-82.56414,33.95567],[-82.39008,33.85651],[-82.3015,33.80062],[-82.20022,33.66214],[-82.19747,33.63013],[-82.1343,33.59095],[-82.10752,33.59782],[-82.05019,33.56464],[-81.99766,33.51342],[-81.9877,33.48794],[-81.92968,33.46674],[-81.91354,33.43953],[-81.93964,33.34609],[-81.85587,33.30248],[-81.85037,33.24622],[-81.78858,33.20716],[-81.77553,33.2198],[-81.75974,33.19912],[-81.7721,33.18303],[-81.76248,33.15947],[-81.70824,33.11807],[-81.64163,33.09276],[-81.62103,33.09564],[-81.49538,33.00988],[-81.50087,32.93557],[-81.45555,32.84851],[-81.42809,32.84101],[-81.39581,32.65101],[-81.41847,32.63193],[-81.38277,32.59086],[-81.3244,32.55788],[-81.28595,32.55904],[-81.27771,32.53531],[-81.19257,32.46292],[-81.20767,32.42409],[-81.11772,32.29127],[-81.15823,32.24017],[-81.11978,32.1937],[-81.11635,32.11638],[-81.05386,32.08497],[-81.00511,32.1001],[-80.92066,32.03667],[-80.49837,32.0326],[-77.99552,33.38485],[-79.68109,34.81124],[-80.78796,34.82252],[-80.79346,34.94193],[-80.9198,35.08815],[-81.04889,35.04993],[-81.04889,35.15105],[-82.39746,35.20044],[-83.10796,35.0011],[-83.09869,34.99098],[-83.12616,34.9544],[-83.11689,34.94033],[-83.2374,34.87417],[-83.32357,34.78878],[-83.32048,34.75861],[-83.35447,34.72814]]]}},{"type":"Feature","properties":{"id":"US-TN"},"geometry":{"type":"Polygon","coordinates":[[[-90.31242,34.99989],[-88.20029,34.99532],[-88.20305,35.00664],[-85.60478,34.98639],[-84.32551,34.99393],[-84.28711,35.224],[-84.22943,35.27335],[-84.1333,35.24419],[-84.03717,35.29129],[-84.0097,35.4324],[-83.74603,35.56209],[-83.50159,35.56433],[-83.13904,35.76961],[-82.97424,35.78744],[-82.64191,36.06989],[-82.57324,35.96103],[-82.22992,36.16086],[-82.06238,36.11872],[-81.82617,36.36682],[-81.72455,36.33806],[-81.66962,36.58987],[-81.64833,36.61206],[-81.92299,36.61647],[-81.93466,36.5947],[-83.67455,36.60056],[-83.69109,36.58281],[-86.51355,36.65555],[-86.56848,36.63572],[-86.5932,36.65555],[-87.80993,36.63792],[-88.06536,36.67979],[-88.05987,36.49674],[-89.41668,36.50778],[-89.45127,36.46431],[-89.48972,36.46541],[-89.47187,36.55813],[-89.52268,36.5846],[-89.57212,36.56144],[-89.52268,36.46762],[-89.5474,36.43117],[-89.57212,36.24754],[-89.6916,36.24089],[-89.58997,36.1478],[-89.61332,36.10897],[-89.73701,36.00048],[-89.64638,35.91489],[-89.67384,35.8804],[-89.73564,35.91044],[-89.77409,35.87261],[-89.70131,35.83366],[-89.72878,35.8047],[-89.95399,35.73228],[-89.94301,35.66871],[-89.89357,35.64528],[-90.11604,35.38483],[-90.07072,35.1314],[-90.16136,35.13252],[-90.31242,34.99989]]]}},{"type":"Feature","properties":{"id":"US-VT"},"geometry":{"type":"Polygon","coordinates":[[[-73.4436,44.07055],[-73.35571,43.77182],[-73.43811,43.57117],[-73.33649,43.62686],[-73.25409,43.57117],[-73.26498,42.74494],[-72.45969,42.72515],[-72.49456,42.77368],[-72.51722,42.7636],[-72.51173,42.78174],[-72.54331,42.81147],[-72.55705,42.85427],[-72.55361,42.88497],[-72.53301,42.89553],[-72.52546,42.93626],[-72.53233,42.95134],[-72.46504,42.98099],[-72.46092,43.05479],[-72.43688,43.08388],[-72.441,43.13702],[-72.45611,43.14654],[-72.39362,43.35659],[-72.41491,43.36557],[-72.38127,43.49472],[-72.40049,43.51265],[-72.32702,43.63553],[-72.30093,43.70754],[-72.20617,43.77302],[-72.16291,43.8919],[-72.12309,43.9201],[-72.11141,43.9977],[-72.04,44.08408],[-72.06335,44.27514],[-72.0249,44.31741],[-71.9782,44.33411],[-71.90679,44.34688],[-71.86834,44.33706],[-71.81753,44.35866],[-71.79693,44.39891],[-71.7008,44.41558],[-71.639,44.47343],[-71.59368,44.49302],[-71.59231,44.56351],[-71.56347,44.56351],[-71.54287,44.58601],[-71.63351,44.74912],[-71.57583,44.79592],[-71.55386,44.86214],[-71.49618,44.90788],[-71.54287,44.98758],[-71.50067,45.01357],[-73.35025,45.00942],[-73.3255,44.25969],[-73.4436,44.07055]]]}},{"type":"Feature","properties":{"id":"US-VA"},"geometry":{"type":"Polygon","coordinates":[[[-83.67455,36.60056],[-81.93466,36.5947],[-81.92299,36.61647],[-81.64833,36.61206],[-81.66962,36.58987],[-80.30148,36.54837],[-75.79776,36.55091],[-75.16879,38.02735],[-75.62472,37.99597],[-75.65768,37.94509],[-75.88153,37.90934],[-76.23034,37.8985],[-76.61074,38.15704],[-76.92248,38.23475],[-77.03784,38.42973],[-77.20813,38.35223],[-77.28778,38.38454],[-77.28915,38.50178],[-77.21225,38.6038],[-77.12161,38.63385],[-77.12023,38.68103],[-77.08041,38.70568],[-77.04196,38.70568],[-77.03906,38.79153],[-77.03021,38.86133],[-77.04592,38.87557],[-77.0491,38.87343],[-77.05493,38.87964],[-77.05957,38.88152],[-77.06759,38.89895],[-77.07047,38.90106],[-77.08897,38.90436],[-77.10201,38.91264],[-77.10592,38.91912],[-77.11536,38.92787],[-77.11962,38.93441],[-77.1477,38.9699],[-77.22186,38.97417],[-77.25619,39.00192],[-77.23971,39.02006],[-77.30975,39.05846],[-77.46081,39.07872],[-77.48416,39.11282],[-77.51849,39.12135],[-77.5281,39.14691],[-77.51162,39.18311],[-77.47729,39.18844],[-77.45532,39.22462],[-77.49102,39.25227],[-77.54596,39.27247],[-77.56655,39.30861],[-77.61462,39.3033],[-77.68054,39.32667],[-77.73135,39.32349],[-77.82818,39.13337],[-78.34728,39.46705],[-78.34934,39.42676],[-78.3617,39.40872],[-78.34385,39.38909],[-78.3672,39.35883],[-78.33973,39.35352],[-78.42075,39.25736],[-78.40084,39.24566],[-78.43792,39.19725],[-78.40496,39.16851],[-78.5725,39.03156],[-78.55396,39.01716],[-78.6034,38.96539],[-78.62743,38.98408],[-78.69198,38.91519],[-78.71944,38.90557],[-78.71807,38.93602],[-78.7421,38.92748],[-78.75858,38.90183],[-78.78742,38.88794],[-78.86982,38.7633],[-78.99479,38.84998],[-79.09504,38.7092],[-79.08543,38.68133],[-79.10191,38.65345],[-79.12663,38.66418],[-79.21864,38.48918],[-79.30515,38.41282],[-79.47681,38.458],[-79.53587,38.55257],[-79.6471,38.59015],[-79.67319,38.53646],[-79.66221,38.51175],[-79.70066,38.49133],[-79.68967,38.45693],[-79.69517,38.42358],[-79.73362,38.37838],[-79.72538,38.36115],[-79.73911,38.35146],[-79.76383,38.35792],[-79.80915,38.30837],[-79.7858,38.26849],[-79.91901,38.18326],[-79.93412,38.10334],[-80.00278,37.99519],[-80.17994,37.85762],[-80.29667,37.68719],[-80.22251,37.62522],[-80.32413,37.56646],[-80.2953,37.51746],[-80.47245,37.42373],[-80.51228,37.48042],[-80.76633,37.37573],[-80.79243,37.39973],[-80.85835,37.43136],[-80.88306,37.38337],[-80.84873,37.34735],[-80.91602,37.30913],[-80.97782,37.29056],[-80.98606,37.30148],[-81.12339,37.27526],[-81.17695,37.26214],[-81.22776,37.23591],[-81.36372,37.33643],[-81.42002,37.27089],[-81.50242,37.2534],[-81.55735,37.20966],[-81.6782,37.20201],[-81.76334,37.27744],[-81.8581,37.28509],[-81.87595,37.32988],[-81.92814,37.36154],[-81.93775,37.43791],[-81.99268,37.4608],[-81.99543,37.4826],[-81.9405,37.50766],[-81.9707,37.53839],[-82.34698,37.2744],[-82.72326,37.12564],[-82.73425,37.04018],[-82.86059,36.98316],[-82.89904,36.88437],[-83.08032,36.84701],[-83.14074,36.75244],[-83.42125,36.66798],[-83.5239,36.66716],[-83.67455,36.60056]]]}},{"type":"Feature","properties":{"id":"US-WV"},"geometry":{"type":"Polygon","coordinates":[[[-82.64361,38.1673],[-82.63263,38.13922],[-82.46646,37.98569],[-82.49392,37.93372],[-82.41702,37.84593],[-82.33462,37.77758],[-82.29754,37.67656],[-82.17669,37.63416],[-82.13961,37.56235],[-81.9707,37.53839],[-81.9405,37.50766],[-81.99543,37.4826],[-81.99268,37.4608],[-81.93775,37.43791],[-81.92814,37.36154],[-81.87595,37.32988],[-81.8581,37.28509],[-81.76334,37.27744],[-81.6782,37.20201],[-81.55735,37.20966],[-81.50242,37.2534],[-81.42002,37.27089],[-81.36372,37.33643],[-81.22776,37.23591],[-81.17695,37.26214],[-81.12339,37.27526],[-80.98606,37.30148],[-80.97782,37.29056],[-80.91602,37.30913],[-80.84873,37.34735],[-80.88306,37.38337],[-80.85835,37.43136],[-80.79243,37.39973],[-80.76633,37.37573],[-80.51228,37.48042],[-80.47245,37.42373],[-80.2953,37.51746],[-80.32413,37.56646],[-80.22251,37.62522],[-80.29667,37.68719],[-80.17994,37.85762],[-80.00278,37.99519],[-79.93412,38.10334],[-79.91901,38.18326],[-79.7858,38.26849],[-79.80915,38.30837],[-79.76383,38.35792],[-79.73911,38.35146],[-79.72538,38.36115],[-79.73362,38.37838],[-79.69517,38.42358],[-79.68967,38.45693],[-79.70066,38.49133],[-79.66221,38.51175],[-79.67319,38.53646],[-79.6471,38.59015],[-79.53587,38.55257],[-79.47681,38.458],[-79.30515,38.41282],[-79.21864,38.48918],[-79.12663,38.66418],[-79.10191,38.65345],[-79.08543,38.68133],[-79.09504,38.7092],[-78.99479,38.84998],[-78.86982,38.7633],[-78.78742,38.88794],[-78.75858,38.90183],[-78.7421,38.92748],[-78.71807,38.93602],[-78.71944,38.90557],[-78.69198,38.91519],[-78.62743,38.98408],[-78.6034,38.96539],[-78.55396,39.01716],[-78.5725,39.03156],[-78.40496,39.16851],[-78.43792,39.19725],[-78.40084,39.24566],[-78.42075,39.25736],[-78.33973,39.35352],[-78.3672,39.35883],[-78.34385,39.38909],[-78.3617,39.40872],[-78.34934,39.42676],[-78.34728,39.46705],[-77.82818,39.13337],[-77.73135,39.32349],[-77.75289,39.42632],[-77.79821,39.43587],[-77.78036,39.49312],[-77.84216,39.49842],[-77.88885,39.55774],[-77.83392,39.56621],[-77.83392,39.60431],[-77.87512,39.617],[-78.01107,39.60113],[-78.05021,39.64768],[-78.10789,39.68203],[-78.18411,39.69524],[-78.31732,39.59479],[-78.40658,39.617],[-78.46872,39.5167],[-78.66167,39.53576],[-78.72621,39.56356],[-78.73789,39.58632],[-78.76364,39.58262],[-78.77771,39.60457],[-78.73892,39.60775],[-78.73789,39.62388],[-78.77702,39.62177],[-78.76501,39.64715],[-78.79659,39.63472],[-78.81925,39.56065],[-78.83951,39.5678],[-78.96688,39.43958],[-79.0455,39.4804],[-79.06885,39.47643],[-79.19657,39.38733],[-79.21579,39.36424],[-79.25321,39.35575],[-79.29235,39.29865],[-79.31158,39.30502],[-79.33801,39.29652],[-79.35999,39.27526],[-79.37853,39.27261],[-79.42831,39.22448],[-79.48702,39.20187],[-79.47663,39.72086],[-80.52292,39.72222],[-80.51902,40.63803],[-80.57837,40.6125],[-80.59948,40.6237],[-80.6345,40.61458],[-80.66608,40.58043],[-80.62935,40.53452],[-80.61287,40.49485],[-80.59639,40.47971],[-80.59776,40.46195],[-80.61424,40.43216],[-80.61356,40.40655],[-80.63313,40.39243],[-80.60944,40.37491],[-80.61253,40.3409],[-80.60017,40.31708],[-80.61733,40.28828],[-80.61871,40.26627],[-80.65475,40.24374],[-80.66917,40.19918],[-80.70763,40.14645],[-80.70591,40.10445],[-80.73681,40.07792],[-80.73097,40.03824],[-80.74265,40.00564],[-80.73818,39.97749],[-80.7605,39.95539],[-80.75329,39.91222],[-80.76702,39.90853],[-80.79655,39.91933],[-80.8065,39.90985],[-80.78899,39.87323],[-80.82436,39.84477],[-80.82092,39.80891],[-80.86555,39.77013],[-80.82813,39.71337],[-80.86384,39.68801],[-80.87139,39.62854],[-80.91534,39.61796],[-81.05541,39.53116],[-81.13232,39.4485],[-81.1845,39.43153],[-81.22021,39.38909],[-81.26141,39.39121],[-81.36303,39.34025],[-81.39874,39.35087],[-81.41796,39.39758],[-81.4674,39.41243],[-81.5086,39.36573],[-81.55529,39.35724],[-81.56902,39.27649],[-81.65417,39.28287],[-81.69537,39.26161],[-81.68987,39.2297],[-81.7503,39.1914],[-81.74206,39.1062],[-81.76952,39.07635],[-81.81622,39.08915],[-81.81622,39.05716],[-81.76128,39.01876],[-81.786,38.96966],[-81.74755,38.93762],[-81.76403,38.91839],[-81.83544,38.94831],[-81.84368,38.9013],[-81.89587,38.8735],[-81.93157,38.89702],[-81.90136,38.92694],[-81.93707,38.98888],[-82.02496,39.02943],[-82.08813,38.9782],[-82.1513,38.89488],[-82.14306,38.83714],[-82.21722,38.80933],[-82.17876,38.59713],[-82.28588,38.59283],[-82.31884,38.45101],[-82.54406,38.39937],[-82.59692,38.42382],[-82.59692,38.342],[-82.57495,38.32261],[-82.58319,38.25039],[-82.61203,38.24176],[-82.59692,38.21156],[-82.60791,38.17378],[-82.64361,38.1673]]]}},{"type":"Feature","properties":{"id":"UA-43"},"geometry":{"type":"Polygon","coordinates":[[[31.62627,45.50633],[32.99857,44.48323],[33.28621,44.94345],[33.5643,44.84057],[33.5849,44.80794],[33.60275,44.81086],[33.6776,44.78577],[33.68172,44.77017],[33.61305,44.75018],[33.61649,44.71237],[33.72772,44.71579],[33.7751,44.68968],[33.71502,44.62081],[33.73528,44.60199],[33.77853,44.6125],[33.92616,44.42082],[33.85784,44.41886],[33.76171,44.38918],[33.66142,43.9825],[36.61884,44.89556],[36.52546,45.20215],[36.68476,45.40306],[36.66828,45.63016],[35.23066,45.79231],[34.96015,45.75634],[34.79905,45.81009],[34.80153,45.90047],[34.75479,45.90705],[34.66679,45.97136],[34.60861,45.99347],[34.55889,45.99347],[34.52011,45.95097],[34.48729,45.94267],[34.44155,45.95995],[34.41221,46.00245],[34.33912,46.06114],[34.25111,46.0532],[34.181,46.06804],[34.12929,46.10494],[34.07311,46.11769],[34.05272,46.10838],[33.91549,46.15938],[33.85234,46.19863],[33.79715,46.20482],[33.74047,46.18555],[33.646,46.23028],[33.61517,46.22615],[33.63854,46.14147],[33.61467,46.13561],[33.57318,46.10317],[33.59087,46.06013],[33.54017,46.0123],[31.62627,45.50633]]]}},{"type":"Feature","properties":{"id":"UA-40"},"geometry":{"type":"Polygon","coordinates":[[[32.99857,44.48323],[33.66142,43.9825],[33.76171,44.38918],[33.85784,44.41886],[33.92616,44.42082],[33.77853,44.6125],[33.73528,44.60199],[33.71502,44.62081],[33.7751,44.68968],[33.72772,44.71579],[33.61649,44.71237],[33.61305,44.75018],[33.68172,44.77017],[33.6776,44.78577],[33.60275,44.81086],[33.5849,44.80794],[33.5643,44.84057],[33.28621,44.94345],[32.99857,44.48323]]]}},{"type":"Feature","properties":{"id":"NL-BQ1"},"geometry":{"type":"Polygon","coordinates":[[[-68.90012,12.62309],[-68.33524,11.78151],[-68.01417,11.77722],[-67.89186,12.4116],[-68.90012,12.62309]]]}},{"type":"Feature","properties":{"id":"CN-GS"},"geometry":{"type":"Polygon","coordinates":[[[92.40874,39.03625],[93.11599,39.17372],[93.42086,38.9092],[94.35607,38.76265],[94.5346,38.35781],[94.99053,38.43638],[95.24459,38.30502],[95.65658,38.36857],[96.29859,38.15669],[96.65702,38.22901],[96.66595,38.48665],[97.05459,38.6284],[96.9358,38.9108],[96.97769,39.20884],[97.3368,39.16733],[98.08937,38.78513],[98.24867,38.88515],[98.28643,39.03358],[98.74923,39.08743],[100.17677,38.2112],[100.09815,38.45735],[100.93105,38.16749],[100.97019,38.01293],[101.3578,37.7916],[101.9878,37.73108],[102.64594,37.10447],[102.47325,36.97238],[102.72319,36.76886],[102.59651,36.71081],[102.71598,36.60009],[102.82859,36.37098],[102.83168,36.33531],[102.88936,36.33338],[102.92026,36.30073],[103.02394,36.25562],[102.97004,36.03299],[102.9467,35.83507],[102.70568,35.86011],[102.80456,35.5758],[102.75169,35.49533],[102.50381,35.58808],[102.3191,35.34369],[102.40218,35.18503],[101.72996,34.70436],[102.15499,34.51221],[102.25318,34.36441],[101.76841,34.06233],[100.94581,34.37404],[100.7611,34.17545],[101.19232,33.79455],[101.17034,33.65463],[101.58782,33.67349],[101.61254,33.51506],[101.76223,33.46925],[101.94625,33.58773],[101.81579,33.10937],[102.46879,33.47211],[102.14332,33.98379],[102.3912,33.97753],[102.45849,34.09929],[102.66792,34.07541],[102.89039,34.33266],[103.17672,34.07484],[103.16162,33.7974],[103.77891,33.66606],[104.10026,33.68435],[104.28634,33.35978],[104.45526,33.32938],[104.33921,33.1979],[104.4326,33.00636],[104.40238,32.79189],[105.11032,32.60004],[105.39321,32.72433],[105.43647,32.94875],[105.47878,32.89406],[105.85945,32.93896],[105.96244,33.15652],[105.72624,33.36322],[105.95764,33.61061],[106.36688,33.61347],[106.51519,33.50246],[106.58523,33.57],[106.41357,33.89777],[106.71295,34.37092],[106.64188,34.38651],[106.6175,34.44797],[106.47399,34.52183],[106.3322,34.51532],[106.33186,34.55972],[106.5612,34.74443],[106.48635,35.05754],[106.76788,35.09238],[107.05833,35.02887],[107.18604,34.9062],[107.8466,34.97487],[107.7079,35.30896],[107.84248,35.26524],[108.58886,35.31176],[108.64654,35.95021],[108.6589,36.41023],[107.34878,36.90378],[107.30346,37.0979],[106.66076,37.19751],[106.49047,36.30848],[106.9313,36.12456],[106.921,35.76824],[106.44241,35.69466],[106.4994,35.35993],[106.36344,35.23889],[106.05857,35.48639],[105.48454,35.72756],[105.32592,35.99911],[105.51544,36.0996],[105.18997,36.95208],[104.29321,37.4367],[103.83865,37.65773],[103.39507,37.88352],[103.36074,38.08809],[103.54476,38.15615],[104.18884,39.12047],[103.9389,39.46482],[102.96798,39.10981],[102.45712,39.23757],[101.8872,39.06931],[101.7279,38.64154],[100.84075,39.18224],[99.70229,39.98659],[100.28045,40.67439],[99.76958,40.97575],[98.3139,40.56806],[97.1777,42.7964],[96.37926,42.72055],[96.04934,42.38796],[96.18324,41.97225],[95.35171,41.54559],[95.19103,41.7498],[94.80789,41.53428],[93.76556,40.66605],[92.92785,40.58058],[92.40874,39.03625]]]}},{"type":"Feature","properties":{"id":"NL-BQ2"},"geometry":{"type":"Polygon","coordinates":[[[-63.58819,17.61311],[-63.22932,17.32592],[-63.07669,17.79659],[-63.29212,17.90532],[-63.58819,17.61311]]]}},{"type":"Feature","properties":{"id":"IN-DL"},"geometry":{"type":"Polygon","coordinates":[[[76.83915,28.58301],[76.84584,28.55037],[76.86249,28.54487],[76.87391,28.5282],[76.88738,28.51998],[76.88043,28.50543],[76.89116,28.50037],[76.89657,28.50686],[76.90206,28.50671],[76.90635,28.51395],[76.92034,28.50678],[76.95334,28.50509],[76.97845,28.5213],[76.99111,28.51365],[76.9969,28.51949],[77.00982,28.51466],[77.01703,28.52104],[77.01476,28.52398],[77.00098,28.53114],[77.00544,28.53947],[77.0139,28.54068],[77.02424,28.5328],[77.03209,28.53118],[77.04344,28.52513],[77.0434,28.52409],[77.04862,28.52107],[77.0463,28.5167],[77.05226,28.51512],[77.05746,28.51297],[77.06093,28.51225],[77.06428,28.51285],[77.06703,28.51199],[77.07153,28.51787],[77.07235,28.52025],[77.07505,28.51877],[77.08003,28.51815],[77.09265,28.51434],[77.09863,28.51146],[77.09575,28.50713],[77.09775,28.50493],[77.11973,28.4952],[77.11277,28.4731],[77.13076,28.43984],[77.14043,28.43848],[77.14651,28.43692],[77.16161,28.42922],[77.17208,28.40559],[77.17389,28.40446],[77.17818,28.40921],[77.22058,28.41344],[77.24169,28.42763],[77.24628,28.4496],[77.24101,28.45616],[77.23156,28.45609],[77.23483,28.46982],[77.24298,28.47917],[77.26238,28.48762],[77.26991,28.48791],[77.27527,28.49358],[77.28832,28.49673],[77.30053,28.49419],[77.30053,28.49007],[77.30855,28.48823],[77.31413,28.4837],[77.32563,28.49004],[77.34615,28.51651],[77.29886,28.55723],[77.29293,28.57634],[77.29916,28.58772],[77.30422,28.58587],[77.31049,28.59085],[77.31332,28.59661],[77.3246,28.59789],[77.33066,28.60098],[77.33645,28.60174],[77.3419,28.60524],[77.34087,28.62299],[77.31594,28.64152],[77.31988,28.65188],[77.32027,28.66234],[77.32551,28.67827],[77.32997,28.67861],[77.33345,28.68147],[77.32409,28.69864],[77.33207,28.71317],[77.3134,28.71366],[77.29971,28.71008],[77.29628,28.70526],[77.29036,28.70602],[77.2865,28.7143],[77.29083,28.72273],[77.28688,28.7248],[77.27667,28.73564],[77.26057,28.73564],[77.25547,28.73861],[77.25658,28.7444],[77.26006,28.75039],[77.25512,28.7558],[77.24886,28.75524],[77.23839,28.75908],[77.20659,28.78451],[77.20418,28.80527],[77.20985,28.81429],[77.2229,28.82091],[77.21157,28.8573],[77.17457,28.85865],[77.15681,28.8367],[77.14282,28.83858],[77.14616,28.85572],[77.13406,28.86301],[77.12265,28.8573],[77.11063,28.86918],[77.09166,28.87098],[77.08316,28.88315],[77.07939,28.87135],[77.06171,28.86963],[77.04282,28.8355],[76.99398,28.84068],[76.98077,28.82113],[76.97064,28.82783],[76.96146,28.81474],[76.95116,28.81798],[76.94292,28.79955],[76.95433,28.79045],[76.94807,28.78097],[76.95579,28.76841],[76.94403,28.75419],[76.95811,28.7432],[76.96068,28.73161],[76.94832,28.71264],[76.96781,28.69917],[76.95776,28.68448],[76.95502,28.66988],[76.93751,28.66942],[76.92378,28.64999],[76.94601,28.63289],[76.93528,28.61865],[76.91725,28.63395],[76.90738,28.62393],[76.8903,28.63274],[76.86429,28.58602],[76.83915,28.58301]]]}},{"type":"Feature","properties":{"id":"NO-21"},"geometry":{"type":"Polygon","coordinates":[[[-3.52068,82.6752],[16.4353,73.61229],[33.12005,75.46568],[35.22046,80.57056],[-3.52068,82.6752]]]}},{"type":"Feature","properties":{"id":"NO-22"},"geometry":{"type":"Polygon","coordinates":[[[-10.71459,70.09565],[-5.93364,70.76368],[-9.68082,72.73731],[-10.71459,70.09565]]]}},{"type":"Feature","properties":{"id":"IN-GJ"},"geometry":{"type":"Polygon","coordinates":[[[68.11329,23.53945],[68.83233,21.42207],[70.8467,20.44438],[70.87331,20.73203],[71.00154,20.74648],[72.83901,20.48555],[72.90801,20.43087],[72.89291,20.36748],[72.47526,20.38318],[72.4768,20.16425],[72.80021,20.12622],[72.8754,20.22869],[72.9808,20.21323],[72.92346,20.26348],[72.94578,20.35331],[73.12156,20.36909],[73.18508,20.29407],[73.06766,20.21806],[73.06354,20.18487],[73.171,20.19744],[73.23383,20.14266],[73.29048,20.1549],[73.29769,20.20453],[73.4333,20.2055],[73.44978,20.71726],[73.6695,20.56208],[73.7495,20.5624],[73.88854,20.72946],[73.93901,20.73588],[73.91635,20.92232],[73.7325,21.10163],[73.58573,21.15591],[73.74538,21.14279],[73.74315,21.16568],[73.83447,21.19337],[73.83619,21.26953],[73.94897,21.29737],[74.01712,21.42047],[74.05265,21.41983],[74.06776,21.48118],[74.11085,21.44492],[74.26328,21.46265],[74.33332,21.50945],[74.30911,21.5655],[74.21161,21.53101],[74.10724,21.5639],[73.85662,21.49939],[73.79379,21.62041],[73.83636,21.84684],[74.14947,21.95323],[74.17282,22.06846],[74.07909,22.24652],[74.08149,22.35896],[74.27787,22.38373],[74.05677,22.48115],[74.26929,22.64633],[74.36714,22.62858],[74.47769,22.86004],[74.3201,23.0573],[74.24182,23.19244],[73.99017,23.33405],[73.89953,23.33248],[73.83172,23.44749],[73.64135,23.4393],[73.6211,23.66024],[73.35639,23.77591],[73.42231,23.92601],[73.37425,24.13359],[73.2431,24.00319],[73.0783,24.20829],[73.2328,24.36476],[73.00277,24.48777],[72.97084,24.34866],[72.74116,24.35491],[72.69996,24.44527],[72.50083,24.40088],[72.44144,24.49214],[72.18292,24.60894],[71.09405,24.69017],[70.97594,24.60904],[71.00341,24.46038],[71.12838,24.42662],[71.04461,24.34657],[70.94985,24.3791],[70.85784,24.30903],[70.88393,24.27398],[70.71502,24.23517],[70.57906,24.27774],[70.5667,24.43787],[70.11712,24.30915],[70.03428,24.172],[69.73335,24.17007],[69.59579,24.29777],[69.29778,24.28712],[69.19341,24.25646],[69.07806,24.29777],[68.97781,24.26021],[68.90914,24.33156],[68.7416,24.31904],[68.74643,23.97027],[68.39339,23.96838],[68.20763,23.85849],[68.11329,23.53945]]]}},{"type":"Feature","properties":{"id":"SO"},"geometry":{"type":"Polygon","coordinates":[[[40.98767,2.82959],[41.00099,-0.83068],[41.56,-1.59812],[41.56362,-1.66375],[41.75542,-1.85308],[49.16337,2.78611],[52.253,11.68582],[51.12877,12.56479],[48.95249,11.56816],[43.42425,11.70983],[42.95776,10.98533],[42.69452,10.62672],[42.87643,10.18441],[43.0937,9.90579],[43.23518,9.84605],[43.32613,9.59205],[44.19222,8.93028],[46.99339,7.9989],[47.92477,8.00111],[47.97917,8.00124],[44.98104,4.91821],[44.02436,4.9451],[43.40263,4.79289],[43.04177,4.57923],[42.97746,4.44032],[42.84526,4.28357],[42.55853,4.20518],[42.07619,4.17667],[41.89488,3.97375],[41.31368,3.14314],[40.98767,2.82959]]]}},{"type":"Feature","properties":{"id":"CN-SX"},"geometry":{"type":"Polygon","coordinates":[[[110.2272,34.90733],[110.23921,34.62784],[110.41122,34.58432],[110.8905,34.65354],[111.1576,34.81831],[111.22695,34.79294],[111.57508,34.84649],[111.82228,35.07159],[112.03994,35.04517],[112.05505,35.27981],[112.76916,35.20635],[113.02802,35.35937],[113.48945,35.52943],[113.60961,35.67626],[113.57597,35.81614],[113.65287,35.83507],[113.72909,36.36103],[113.59554,36.4616],[113.47297,36.6976],[113.78917,36.88071],[113.74488,37.0738],[114.09851,37.58594],[114.127,37.69387],[114.02984,37.72972],[113.82797,38.16263],[113.55194,38.23871],[113.53683,38.50787],[113.84101,38.76854],[113.75518,38.94499],[113.94676,39.09489],[114.3402,39.07997],[114.5613,39.55276],[114.39376,39.60568],[114.41093,39.83121],[113.89114,40.0192],[114.54482,40.3366],[114.30519,40.36695],[114.28527,40.51327],[114.1246,40.74569],[114.0652,40.67634],[114.07001,40.54093],[113.93783,40.50544],[113.85234,40.44511],[113.6721,40.4425],[113.53443,40.3345],[113.31504,40.31304],[113.24809,40.41349],[112.88761,40.32822],[112.84572,40.20169],[112.73963,40.1626],[112.61947,40.23891],[112.45399,40.29995],[112.30293,40.25463],[112.10758,39.97527],[111.97059,39.78822],[111.91909,39.61468],[111.72615,39.59537],[111.67121,39.62525],[111.53217,39.65698],[111.43295,39.64006],[111.4199,39.50245],[111.36188,39.47489],[111.33235,39.42081],[111.21288,39.42638],[111.12945,39.4025],[111.11881,39.36403],[111.19228,39.30508],[111.23897,39.30003],[111.1576,39.10741],[111.09134,39.02985],[111.04637,39.02158],[110.97495,38.97836],[111.00963,38.90706],[110.95745,38.75836],[110.88775,38.65522],[110.87333,38.45842],[110.80192,38.44713],[110.5149,38.20905],[110.49636,38.02862],[110.58357,37.92578],[110.77377,37.62946],[110.74665,37.45169],[110.39474,36.99816],[110.4895,36.56039],[110.43594,36.1606],[110.61035,35.64948],[110.40572,35.30728],[110.36041,35.139],[110.2272,34.90733]]]}},{"type":"Feature","properties":{"id":"IN-ML"},"geometry":{"type":"Polygon","coordinates":[[[89.81208,25.37244],[89.84086,25.31854],[89.83371,25.29548],[89.87629,25.28337],[89.90478,25.31038],[90.1155,25.22686],[90.40034,25.1534],[90.65042,25.17788],[90.87427,25.15799],[91.25517,25.20677],[91.63648,25.12846],[92.0316,25.1834],[92.33957,25.07593],[92.39147,25.01471],[92.79464,25.21612],[92.6477,25.57465],[92.13958,25.69846],[92.30438,26.06788],[91.94732,25.99755],[91.84432,26.10982],[91.24969,25.71887],[90.95443,25.95001],[90.61729,25.87714],[90.48065,26.0031],[90.10642,25.96113],[89.89906,25.73867],[90.01922,25.60314],[89.89562,25.5635],[89.81208,25.37244]]]}},{"type":"Feature","properties":{"id":"IN-TN"},"geometry":{"type":"Polygon","coordinates":[[[76.23344,11.51871],[76.53934,11.35079],[76.43531,11.19456],[76.7237,11.20736],[76.6492,10.924],[76.81777,10.86163],[76.84112,10.81138],[76.8988,10.77327],[76.85691,10.68051],[76.87236,10.63226],[76.80679,10.63159],[76.82327,10.3237],[76.96403,10.221],[77.21465,10.36423],[77.27645,10.13382],[77.20298,10.11759],[77.25997,10.02971],[77.24693,9.80447],[77.16865,9.61632],[77.42065,9.51543],[77.15045,9.01496],[77.26135,8.843],[77.17655,8.7385],[77.27954,8.52296],[77.20024,8.50734],[77.22358,8.44758],[76.72283,7.82138],[77.66782,7.85769],[79.37385,8.98767],[79.45362,9.159],[79.42124,9.80115],[80.48418,10.20786],[80.42198,10.81981],[79.8088,10.81374],[79.68658,10.99107],[80.41717,10.96613],[80.35262,11.6751],[79.59525,11.86735],[79.70031,12.10378],[80.31898,12.0091],[80.67259,13.46443],[80.32104,13.44372],[80.29632,13.37626],[80.2153,13.48512],[80.02784,13.52718],[79.92484,13.33884],[79.77996,13.21254],[79.53414,13.30944],[79.36832,13.30711],[79.40025,13.142],[79.2152,13.13063],[79.14997,13.00422],[78.84681,13.07613],[78.63292,12.97143],[78.46401,12.61454],[78.2151,12.68991],[78.23295,12.76526],[78.12309,12.76861],[78.08678,12.83146],[78.03322,12.85406],[78.00215,12.80359],[77.97005,12.83105],[77.95349,12.85966],[77.94782,12.8354],[77.91975,12.82828],[77.93683,12.88192],[77.83281,12.86151],[77.79247,12.84092],[77.81032,12.82987],[77.78062,12.76928],[77.79384,12.74768],[77.7662,12.73663],[77.76277,12.72658],[77.77582,12.72273],[77.76329,12.6956],[77.73994,12.69962],[77.74114,12.67065],[77.71196,12.6817],[77.71265,12.66395],[77.67943,12.65625],[77.67514,12.68363],[77.60089,12.66579],[77.58853,12.51803],[77.63523,12.49088],[77.61806,12.36649],[77.48931,12.2766],[77.45738,12.20681],[77.725,12.17963],[77.77925,12.112],[77.72724,12.05409],[77.67917,11.94898],[77.49704,11.9426],[77.46665,11.84887],[77.42906,11.76199],[77.25465,11.81241],[77.12059,11.71863],[76.98772,11.81291],[76.97193,11.77628],[76.91013,11.79308],[76.84249,11.66938],[76.85623,11.59506],[76.61281,11.60717],[76.539,11.69123],[76.42948,11.66568],[76.37145,11.59136],[76.23722,11.58699],[76.23344,11.51871]]]}},{"type":"Feature","properties":{"id":"TJ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[67.33226,39.23739],[67.67833,39.14479],[67.68915,39.00775],[68.09704,39.02589],[68.19743,38.85985],[68.06948,38.82115],[68.12877,38.73677],[68.05598,38.71641],[68.0807,38.64136],[68.05873,38.56087],[68.11366,38.47169],[68.06274,38.39435],[68.13289,38.40822],[68.40343,38.19484],[68.27159,37.91477],[68.12635,37.93],[67.81566,37.43107],[67.8474,37.31594],[67.78329,37.1834],[67.7803,37.08978],[67.87917,37.0591],[68.02194,36.91923],[68.18542,37.02074],[68.27605,37.00977],[68.29253,37.10621],[68.41201,37.10402],[68.41888,37.13906],[68.61851,37.19815],[68.6798,37.27906],[68.81438,37.23862],[68.80889,37.32494],[68.91189,37.26704],[68.88168,37.33368],[68.96407,37.32603],[69.03274,37.25174],[69.25152,37.09426],[69.39529,37.16752],[69.45022,37.23315],[69.36645,37.40462],[69.44954,37.4869],[69.51888,37.5844],[69.80041,37.5746],[69.84435,37.60616],[69.93362,37.61378],[69.95971,37.5659],[70.15015,37.52519],[70.28243,37.66706],[70.27694,37.81258],[70.1863,37.84296],[70.17206,37.93276],[70.4898,38.12546],[70.54673,38.24541],[70.60407,38.28046],[70.61526,38.34774],[70.64966,38.34999],[70.69189,38.37031],[70.6761,38.39144],[70.67438,38.40597],[70.69807,38.41861],[70.72485,38.4131],[70.75455,38.4252],[70.77132,38.45548],[70.78581,38.45502],[70.78702,38.45031],[70.79766,38.44944],[70.80521,38.44447],[70.81697,38.44507],[70.82538,38.45394],[70.84376,38.44688],[70.88719,38.46826],[70.92728,38.43021],[70.98693,38.48862],[71.03545,38.44779],[71.0556,38.40176],[71.09542,38.42517],[71.10592,38.42077],[71.10957,38.40671],[71.1451,38.40106],[71.21291,38.32797],[71.33114,38.30339],[71.33869,38.27335],[71.37803,38.25641],[71.36444,38.15358],[71.29878,38.04429],[71.28922,38.01272],[71.27622,37.99946],[71.27278,37.96496],[71.24969,37.93031],[71.2809,37.91995],[71.296,37.93403],[71.32871,37.88564],[71.51565,37.95349],[71.58843,37.92425],[71.59255,37.79956],[71.55752,37.78677],[71.54324,37.77104],[71.53053,37.76534],[71.55234,37.73209],[71.54186,37.69691],[71.51972,37.61945],[71.5065,37.60912],[71.49693,37.53527],[71.50616,37.50733],[71.5256,37.47971],[71.49612,37.4279],[71.47685,37.40281],[71.4862,37.33405],[71.49821,37.31975],[71.50674,37.31502],[71.48536,37.26017],[71.4824,37.24921],[71.48339,37.23937],[71.47386,37.2269],[71.4555,37.21418],[71.4494,37.18137],[71.44127,37.11856],[71.43097,37.05855],[71.45578,37.03094],[71.46923,36.99925],[71.48481,36.93218],[71.51502,36.89128],[71.57195,36.74943],[71.67083,36.67346],[71.83229,36.68084],[72.31676,36.98115],[72.54095,37.00007],[72.66381,37.02014],[72.79693,37.22222],[73.06884,37.31729],[73.29633,37.46495],[73.77197,37.4417],[73.76647,37.33913],[73.61129,37.27469],[73.64974,37.23643],[73.82552,37.22659],[73.8564,37.26158],[74.20308,37.34208],[74.23339,37.41116],[74.41055,37.3948],[74.56161,37.37734],[74.68383,37.3948],[74.8294,37.3435],[74.88887,37.23275],[75.12328,37.31839],[75.09719,37.37297],[75.15899,37.41443],[75.06011,37.52779],[74.94338,37.55501],[74.8912,37.67576],[75.00935,37.77486],[74.92416,37.83428],[74.9063,38.03033],[74.82665,38.07359],[74.80331,38.19889],[74.69894,38.22155],[74.69619,38.42947],[74.51217,38.47034],[74.17022,38.65504],[73.97933,38.52945],[73.79806,38.61106],[73.80656,38.66449],[73.7033,38.84782],[73.7445,38.93867],[73.82964,38.91517],[73.81728,39.04007],[73.75823,39.023],[73.60638,39.24534],[73.54572,39.27567],[73.55396,39.3543],[73.5004,39.38402],[73.59241,39.40843],[73.59831,39.46425],[73.45096,39.46677],[73.31912,39.38615],[73.18454,39.35536],[72.85934,39.35116],[72.62027,39.39696],[72.33173,39.33093],[72.23834,39.17248],[72.17242,39.2661],[72.09689,39.26823],[72.04059,39.36704],[71.90601,39.27674],[71.79202,39.27355],[71.7522,39.32031],[71.80164,39.40631],[71.76816,39.45456],[71.62688,39.44056],[71.5517,39.45722],[71.55856,39.57588],[71.49814,39.61397],[71.08752,39.50704],[71.06418,39.41586],[70.7854,39.38933],[70.64087,39.58792],[70.44757,39.60128],[70.2869,39.53141],[70.11111,39.58223],[69.87491,39.53882],[69.68677,39.59281],[69.3594,39.52516],[69.26938,39.8127],[69.35649,40.01994],[69.43134,39.98431],[69.43557,39.92877],[69.53615,39.93991],[69.5057,40.03277],[69.53855,40.0887],[69.53794,40.11833],[69.55555,40.12296],[69.57615,40.10524],[69.64704,40.12165],[69.67001,40.10639],[70.01283,40.23288],[70.58297,40.00891],[70.57384,39.99394],[70.47557,39.93216],[70.55033,39.96619],[70.58912,39.95211],[70.65946,39.9878],[70.65827,40.0981],[70.7928,40.12797],[70.80495,40.16813],[70.9818,40.22392],[70.8607,40.217],[70.62342,40.17396],[70.56394,40.26421],[70.57149,40.3442],[70.37511,40.38605],[70.32626,40.45174],[70.49871,40.52503],[70.80009,40.72825],[70.45251,41.04438],[70.38028,41.02014],[70.36655,40.90296],[69.69434,40.62615],[69.59441,40.70181],[69.53021,40.77621],[69.38327,40.7918],[69.32834,40.70233],[69.3455,40.57988],[69.2643,40.57506],[69.21063,40.54469],[69.27066,40.49274],[69.28525,40.41894],[69.30774,40.36102],[69.33794,40.34819],[69.32833,40.29794],[69.30808,40.2821],[69.24817,40.30357],[69.25229,40.26362],[69.30104,40.24502],[69.30448,40.18774],[69.2074,40.21488],[69.15659,40.2162],[69.04544,40.22904],[68.85832,40.20885],[68.84357,40.18604],[68.79276,40.17555],[68.77902,40.20492],[68.5332,40.14826],[68.52771,40.11676],[68.62796,40.07789],[69.01523,40.15771],[69.01935,40.11466],[68.96579,40.06949],[68.84906,40.04952],[68.93695,39.91167],[68.88889,39.87163],[68.63071,39.85265],[68.61972,39.68905],[68.54166,39.53929],[68.12053,39.56317],[67.70992,39.66156],[67.62889,39.60234],[67.44899,39.57799],[67.46547,39.53564],[67.39681,39.52505],[67.46822,39.46146],[67.45998,39.315],[67.36522,39.31287],[67.33226,39.23739]]],[[[70.52631,39.86989],[70.54998,39.85137],[70.59667,39.83542],[70.63105,39.77923],[70.74189,39.86319],[70.53651,39.89155],[70.52631,39.86989]]],[[[70.54223,40.98787],[70.57501,40.98941],[70.6721,40.90555],[70.68112,40.90612],[70.6158,40.97661],[70.56077,41.00642],[70.54223,40.98787]]]]}},{"type":"Feature","properties":{"id":"US"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-179.55295,50.81807],[-133.92876,54.62289],[-130.61931,54.70835],[-130.64499,54.76912],[-130.44184,54.85377],[-130.27203,54.97174],[-130.18765,55.07744],[-130.08035,55.21556],[-129.97513,55.28029],[-130.15373,55.74895],[-130.00857,55.91344],[-130.00093,56.00325],[-130.10173,56.12178],[-130.33965,56.10849],[-130.77769,56.36185],[-131.8271,56.62247],[-133.38523,58.42773],[-133.84645,58.73543],[-134.27175,58.8634],[-134.48059,59.13231],[-134.55699,59.1297],[-134.7047,59.2458],[-135.00267,59.28745],[-135.03069,59.56208],[-135.48007,59.79937],[-136.31566,59.59083],[-136.22381,59.55526],[-136.33727,59.44466],[-136.47323,59.46617],[-136.52365,59.16752],[-136.82619,59.16198],[-137.4925,58.89415],[-137.60623,59.24465],[-138.62145,59.76431],[-138.71149,59.90728],[-139.05365,59.99655],[-139.20603,60.08896],[-139.05831,60.35205],[-139.68991,60.33693],[-139.98024,60.18027],[-140.45648,60.30919],[-140.5227,60.22077],[-141.00116,60.30648],[-141.00555,72.20369],[-168.25765,71.99091],[-168.95635,65.98512],[-169.03888,65.48473],[-172.76104,63.77445],[-179.55295,57.62081],[-179.55295,50.81807]]],[[[-179.2458,29.18869],[-179.23433,18.15359],[-154.13058,18.17333],[-154.14205,29.20682],[-176.83456,29.19028],[-177.8563,29.18961],[-179.2458,29.18869]]],[[[-177.43928,1.65656],[-177.43039,-1.43294],[-175.33482,-1.40631],[-175.33167,1.67574],[-177.43928,1.65656]]],[[[-174.18596,-12.48057],[-171.14953,-12.4725],[-171.14262,-14.93704],[-167.73854,-14.92809],[-167.75195,-10.12005],[-174.17993,-10.13616],[-174.18596,-12.48057]]],[[[-169.97341,16.32808],[-169.02347,16.32808],[-169.02347,17.13856],[-169.97341,17.13856],[-169.97341,16.32808]]],[[[-163.24994,7.12322],[-163.24478,5.24198],[-161.06795,5.2462],[-161.0731,7.1291],[-163.24994,7.12322]]],[[[-161.05669,1.11722],[-161.04969,-1.36251],[-158.62058,-1.35506],[-158.62734,1.1296],[-161.05669,1.11722]]],[[[-125.69978,42.00605],[-122.18305,33.57011],[-118.48109,32.5991],[-117.1243,32.53427],[-115.88053,32.63624],[-114.71871,32.71894],[-114.76736,32.64094],[-114.80584,32.62028],[-114.81141,32.55543],[-114.79524,32.55731],[-114.82011,32.49609],[-112.34553,31.7357],[-111.07523,31.33232],[-109.05235,31.3333],[-108.20979,31.33316],[-108.20899,31.78534],[-106.529,31.784],[-106.52266,31.77509],[-106.51251,31.76922],[-106.50962,31.76155],[-106.50111,31.75714],[-106.48815,31.74769],[-106.47298,31.75054],[-106.46726,31.75998],[-106.45244,31.76523],[-106.43419,31.75478],[-106.41773,31.75196],[-106.38003,31.73151],[-106.3718,31.71165],[-106.34864,31.69663],[-106.33419,31.66303],[-106.30305,31.62154],[-106.28084,31.56173],[-106.24612,31.54193],[-106.23711,31.51262],[-106.20346,31.46305],[-106.09025,31.40569],[-106.00363,31.39181],[-104.77674,30.4236],[-104.5171,29.64671],[-104.3969,29.57105],[-104.39363,29.55396],[-104.37752,29.54255],[-103.15787,28.93865],[-102.60596,29.8192],[-101.47277,29.7744],[-101.05686,29.44738],[-101.01128,29.36947],[-100.96725,29.3477],[-100.94579,29.34523],[-100.94056,29.33371],[-100.87982,29.296],[-100.79696,29.24688],[-100.67294,29.09744],[-100.63689,28.90812],[-100.59809,28.88197],[-100.52313,28.75598],[-100.5075,28.74066],[-100.51222,28.70679],[-100.50029,28.66117],[-99.55409,27.61314],[-99.51478,27.55836],[-99.52955,27.49747],[-99.50208,27.50021],[-99.48045,27.49016],[-99.482,27.47128],[-99.49744,27.43746],[-99.53573,27.30926],[-99.08477,26.39849],[-99.03053,26.41249],[-99.00546,26.3925],[-98.35126,26.15129],[-98.30491,26.10475],[-98.27075,26.09457],[-98.24603,26.07191],[-97.97017,26.05232],[-97.95155,26.0625],[-97.66511,26.01708],[-97.52025,25.88518],[-97.49828,25.89877],[-97.45669,25.86874],[-97.42511,25.83969],[-97.37332,25.83854],[-97.35946,25.92189],[-97.13927,25.96583],[-96.92418,25.97377],[-93.77551,29.43998],[-88.93054,28.25639],[-88.37952,30.00457],[-87.51915,30.07055],[-83.18732,24.36791],[-82.02215,24.23074],[-80.16442,23.44484],[-79.36558,27.02964],[-81.34929,30.71298],[-80.49837,32.0326],[-77.99552,33.38485],[-74.86753,35.41538],[-75.79776,36.55091],[-75.16879,38.02735],[-74.98718,38.4507],[-73.81773,39.66512],[-71.6391,40.94332],[-71.10101,41.43444],[-69.97282,40.56828],[-69.42513,41.52748],[-70.04999,42.81005],[-67.16117,44.20069],[-66.93432,44.82597],[-66.96824,44.83078],[-66.98249,44.87071],[-66.96824,44.90965],[-67.0216,44.95333],[-67.11316,45.11176],[-67.15965,45.16179],[-67.19603,45.16771],[-67.20349,45.1722],[-67.22751,45.16344],[-67.27039,45.1934],[-67.29748,45.18173],[-67.29754,45.14865],[-67.34927,45.122],[-67.48201,45.27351],[-67.42394,45.37969],[-67.50578,45.48971],[-67.42144,45.50584],[-67.43815,45.59162],[-67.6049,45.60725],[-67.80705,45.69528],[-67.80653,45.80022],[-67.75654,45.82324],[-67.80961,45.87531],[-67.75196,45.91814],[-67.78111,45.9392],[-67.78578,47.06473],[-67.87993,47.10377],[-67.94843,47.1925],[-68.23244,47.35712],[-68.37458,47.35851],[-68.38332,47.28723],[-68.57914,47.28431],[-68.60575,47.24659],[-68.70125,47.24399],[-68.89222,47.1807],[-69.05039,47.2456],[-69.05073,47.30076],[-69.05148,47.42012],[-69.22119,47.46461],[-69.99966,46.69543],[-70.05812,46.41768],[-70.18547,46.35357],[-70.29078,46.18832],[-70.23855,46.1453],[-70.31025,45.96424],[-70.24694,45.95138],[-70.25976,45.89675],[-70.41523,45.79497],[-70.38934,45.73215],[-70.54019,45.67291],[-70.68516,45.56964],[-70.72651,45.49771],[-70.62518,45.42286],[-70.65383,45.37592],[-70.78372,45.43269],[-70.82638,45.39828],[-70.80236,45.37444],[-70.84816,45.22698],[-70.89864,45.2398],[-70.91169,45.29849],[-70.95193,45.33895],[-71.0107,45.34819],[-71.01866,45.31573],[-71.08364,45.30623],[-71.14568,45.24128],[-71.19723,45.25438],[-71.22338,45.25184],[-71.29371,45.29996],[-71.37133,45.24624],[-71.44252,45.2361],[-71.40364,45.21382],[-71.42778,45.12624],[-71.48735,45.07784],[-71.50067,45.01357],[-73.35025,45.00942],[-74.32699,44.99029],[-74.66689,45.00646],[-74.8447,45.00606],[-74.99101,44.98051],[-75.01363,44.95608],[-75.2193,44.87821],[-75.41441,44.76614],[-75.76813,44.51537],[-75.8217,44.43176],[-75.95947,44.34463],[-76.00018,44.34896],[-76.16285,44.28262],[-76.1664,44.23051],[-76.244,44.19643],[-76.31222,44.19894],[-76.35324,44.13493],[-76.43859,44.09393],[-76.79706,43.63099],[-79.25796,43.54052],[-79.06921,43.26183],[-79.05512,43.25375],[-79.05544,43.21224],[-79.05002,43.20133],[-79.05384,43.17418],[-79.04652,43.16396],[-79.0427,43.13934],[-79.06881,43.12029],[-79.05671,43.10937],[-79.07486,43.07845],[-79.01055,43.06659],[-78.99941,43.05612],[-79.02424,43.01983],[-79.02074,42.98444],[-78.98126,42.97],[-78.96312,42.95509],[-78.93224,42.95229],[-78.90905,42.93022],[-78.90712,42.89733],[-78.93684,42.82887],[-79.77073,42.55308],[-80.53581,42.29896],[-82.67862,41.67615],[-83.11184,41.95671],[-83.14962,42.04089],[-83.12724,42.2376],[-83.09837,42.28877],[-83.07837,42.30978],[-83.02253,42.33045],[-82.82964,42.37355],[-82.64242,42.55594],[-82.58873,42.54984],[-82.57583,42.5718],[-82.51858,42.611],[-82.51063,42.66025],[-82.46613,42.76615],[-82.4826,42.8068],[-82.45331,42.93139],[-82.4253,42.95423],[-82.4146,42.97626],[-82.42469,42.992],[-82.48419,45.30225],[-83.59589,45.82131],[-83.43746,45.99749],[-83.57017,46.105],[-83.83329,46.12169],[-83.90453,46.05922],[-83.95399,46.05634],[-84.1096,46.23987],[-84.09756,46.25512],[-84.11615,46.2681],[-84.11254,46.32329],[-84.13451,46.39218],[-84.11196,46.50248],[-84.12885,46.53068],[-84.17723,46.52753],[-84.1945,46.54061],[-84.2264,46.53337],[-84.26351,46.49508],[-84.29893,46.49127],[-84.34174,46.50683],[-84.42101,46.49853],[-84.4481,46.48972],[-84.47607,46.45225],[-84.55635,46.45974],[-84.85871,46.88881],[-88.37033,48.30586],[-89.48837,48.01412],[-89.57972,48.00023],[-89.77248,48.02607],[-89.89974,47.98109],[-90.07418,48.11043],[-90.56312,48.09488],[-90.56444,48.12184],[-90.75045,48.09143],[-90.87588,48.2484],[-91.08016,48.18096],[-91.25025,48.08522],[-91.43248,48.04912],[-91.45829,48.07454],[-91.58025,48.04339],[-91.55649,48.10611],[-91.70451,48.11805],[-91.71231,48.19875],[-91.86125,48.21278],[-91.98929,48.25409],[-92.05339,48.35958],[-92.14732,48.36578],[-92.202,48.35252],[-92.26662,48.35651],[-92.30939,48.31251],[-92.27167,48.25046],[-92.37185,48.22259],[-92.48147,48.36609],[-92.45588,48.40624],[-92.50712,48.44921],[-92.65606,48.43471],[-92.71323,48.46081],[-92.69927,48.49573],[-92.62747,48.50278],[-92.6342,48.54133],[-92.7287,48.54005],[-92.94973,48.60866],[-93.25391,48.64266],[-93.33946,48.62787],[-93.3712,48.60599],[-93.39758,48.60364],[-93.40693,48.60948],[-93.44472,48.59147],[-93.47022,48.54357],[-93.66382,48.51845],[-93.79267,48.51631],[-93.80939,48.52439],[-93.80676,48.58232],[-93.83288,48.62745],[-93.85769,48.63284],[-94.23215,48.65202],[-94.25104,48.65729],[-94.25172,48.68404],[-94.27153,48.70232],[-94.4174,48.71049],[-94.44258,48.69223],[-94.53826,48.70216],[-94.54885,48.71543],[-94.58903,48.71803],[-94.69335,48.77883],[-94.69669,48.80918],[-94.70486,48.82365],[-94.70087,48.8339],[-94.687,48.84077],[-94.75017,49.09931],[-94.77355,49.11998],[-94.82487,49.29483],[-94.8159,49.32299],[-94.85381,49.32492],[-94.95681,49.37035],[-94.99532,49.36579],[-95.01419,49.35647],[-95.05825,49.35311],[-95.12903,49.37056],[-95.15357,49.384],[-95.15355,48.9996],[-97.24024,48.99952],[-101.36198,48.99935],[-104.05004,48.99925],[-110.0051,48.99901],[-114.0683,48.99885],[-116.04938,48.99999],[-117.03266,49.00056],[-123.32163,49.00419],[-123.0093,48.83186],[-123.0093,48.76586],[-123.26565,48.6959],[-123.15614,48.35395],[-123.50039,48.21223],[-125.03842,48.53282],[-125.2772,46.2631],[-125.69978,42.00605]]],[[[-75.27909,18.17213],[-74.76465,18.06252],[-74.7289,18.71009],[-75.24866,18.6531],[-75.27909,18.17213]]],[[[-68.20301,17.83927],[-65.27974,17.56928],[-64.35558,17.48384],[-64.646,18.10286],[-64.64067,18.36478],[-64.86049,18.39954],[-65.02435,18.73231],[-67.99519,18.97186],[-68.20301,17.83927]]],[[[143.82485,13.92273],[144.61642,12.82462],[146.25931,13.85876],[146.6755,21.00809],[144.18594,21.03576],[143.82485,13.92273]]],[[[166.27257,19.60026],[166.27567,19.02484],[166.94111,19.02804],[166.93801,19.60345],[166.27257,19.60026]]],[[[171.97544,51.06331],[180,51.0171],[180,53.34113],[172.01045,53.385],[171.97544,51.06331]]]]}},{"type":"Feature","properties":{"id":"UZ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[55.97584,44.99322],[56.00314,41.32584],[57.03423,41.25435],[57.13796,41.36625],[57.03359,41.41777],[56.96218,41.80383],[57.03633,41.92043],[57.30275,42.14076],[57.6296,42.16519],[57.84932,42.18555],[57.92897,42.24047],[57.90975,42.4374],[57.99214,42.50021],[58.3492,42.43335],[58.40688,42.29535],[58.51674,42.30348],[58.29427,42.56497],[58.14321,42.62159],[58.27504,42.69632],[58.57991,42.64988],[58.6266,42.79314],[58.93422,42.5407],[59.17317,42.52248],[59.2955,42.37064],[59.4341,42.29738],[59.94633,42.27655],[60.00539,42.212],[59.96419,42.1428],[60.04659,42.08982],[60.0356,42.01028],[59.95046,41.97966],[60.33223,41.75058],[60.08504,41.80997],[60.06032,41.76287],[60.18117,41.60082],[60.06581,41.4363],[60.5078,41.21694],[61.03261,41.25691],[61.22212,41.14946],[61.33199,41.14946],[61.39732,41.19873],[61.4446,41.29407],[61.87856,41.12257],[62.11751,40.58242],[62.34273,40.43206],[62.43337,39.98528],[63.6913,39.27666],[63.70778,39.22349],[64.19086,38.95561],[64.32576,38.98691],[65.55873,38.29052],[65.83913,38.25733],[66.24013,38.16238],[66.41042,38.02403],[66.56697,38.0435],[66.67684,37.96776],[66.53676,37.80084],[66.52852,37.58568],[66.65761,37.45497],[66.52303,37.39827],[66.55743,37.35409],[66.64699,37.32958],[66.95598,37.40162],[67.08232,37.35469],[67.13039,37.27168],[67.2224,37.24545],[67.2581,37.17216],[67.51868,37.26102],[67.78329,37.1834],[67.8474,37.31594],[67.81566,37.43107],[68.12635,37.93],[68.27159,37.91477],[68.40343,38.19484],[68.13289,38.40822],[68.06274,38.39435],[68.11366,38.47169],[68.05873,38.56087],[68.0807,38.64136],[68.05598,38.71641],[68.12877,38.73677],[68.06948,38.82115],[68.19743,38.85985],[68.09704,39.02589],[67.68915,39.00775],[67.67833,39.14479],[67.33226,39.23739],[67.36522,39.31287],[67.45998,39.315],[67.46822,39.46146],[67.39681,39.52505],[67.46547,39.53564],[67.44899,39.57799],[67.62889,39.60234],[67.70992,39.66156],[68.12053,39.56317],[68.54166,39.53929],[68.61972,39.68905],[68.63071,39.85265],[68.88889,39.87163],[68.93695,39.91167],[68.84906,40.04952],[68.96579,40.06949],[69.01935,40.11466],[69.01523,40.15771],[68.62796,40.07789],[68.52771,40.11676],[68.5332,40.14826],[68.77902,40.20492],[68.79276,40.17555],[68.84357,40.18604],[68.85832,40.20885],[69.04544,40.22904],[69.15659,40.2162],[69.2074,40.21488],[69.30448,40.18774],[69.30104,40.24502],[69.25229,40.26362],[69.24817,40.30357],[69.30808,40.2821],[69.32833,40.29794],[69.33794,40.34819],[69.30774,40.36102],[69.28525,40.41894],[69.27066,40.49274],[69.21063,40.54469],[69.2643,40.57506],[69.3455,40.57988],[69.32834,40.70233],[69.38327,40.7918],[69.53021,40.77621],[69.59441,40.70181],[69.69434,40.62615],[70.36655,40.90296],[70.38028,41.02014],[70.45251,41.04438],[70.80009,40.72825],[70.49871,40.52503],[70.32626,40.45174],[70.37511,40.38605],[70.57149,40.3442],[70.56394,40.26421],[70.62342,40.17396],[70.8607,40.217],[70.9818,40.22392],[70.95789,40.28761],[71.05901,40.28765],[71.13042,40.34106],[71.36663,40.31593],[71.4246,40.28619],[71.51215,40.26943],[71.51549,40.22986],[71.61725,40.20615],[71.61931,40.26775],[71.68386,40.26984],[71.70569,40.20391],[71.69621,40.18492],[71.71719,40.17886],[71.73054,40.14818],[71.82646,40.21872],[71.85002,40.25647],[72.05464,40.27586],[71.96401,40.31907],[72.18648,40.49893],[72.24368,40.46091],[72.40346,40.4007],[72.44191,40.48222],[72.41513,40.50856],[72.38384,40.51535],[72.41714,40.55736],[72.34406,40.60144],[72.40517,40.61917],[72.47795,40.5532],[72.66713,40.5219],[72.66713,40.59076],[72.69579,40.59778],[72.73995,40.58409],[72.74768,40.58051],[72.74862,40.57131],[72.75982,40.57273],[72.74894,40.59592],[72.74866,40.60873],[72.80137,40.67856],[72.84754,40.67229],[72.85372,40.7116],[72.8722,40.71111],[72.93296,40.73089],[72.99133,40.76457],[73.0612,40.76678],[73.13412,40.79122],[73.13267,40.83512],[73.01869,40.84681],[72.94454,40.8094],[72.84291,40.85512],[72.68157,40.84942],[72.59136,40.86947],[72.55109,40.96046],[72.48742,40.97136],[72.45206,41.03018],[72.38511,41.02785],[72.36138,41.04384],[72.34757,41.06104],[72.34026,41.04539],[72.324,41.03381],[72.18339,40.99571],[72.17594,41.02377],[72.21061,41.05607],[72.1792,41.10621],[72.14864,41.13363],[72.17594,41.15522],[72.16433,41.16483],[72.10745,41.15483],[72.07249,41.11739],[71.85964,41.19081],[71.91457,41.2982],[71.83914,41.3546],[71.76625,41.4466],[71.71132,41.43012],[71.73054,41.54713],[71.65914,41.49599],[71.6787,41.42111],[71.57227,41.29175],[71.46688,41.31883],[71.43814,41.19644],[71.46148,41.13958],[71.40198,41.09436],[71.34877,41.16807],[71.27187,41.11015],[71.25813,41.18796],[71.11806,41.15359],[71.02193,41.19494],[70.9615,41.16393],[70.86263,41.23833],[70.77885,41.24813],[70.78572,41.36419],[70.67586,41.47953],[70.48909,41.40335],[70.17682,41.5455],[70.69777,41.92554],[71.28719,42.18033],[71.13263,42.28356],[70.94483,42.26238],[69.49545,41.545],[69.45751,41.56863],[69.39485,41.51518],[69.45081,41.46246],[69.37468,41.46555],[69.35554,41.47211],[69.29778,41.43673],[69.25059,41.46693],[69.23332,41.45847],[69.22671,41.46298],[69.20439,41.45391],[69.18528,41.45175],[69.17701,41.43769],[69.15137,41.43078],[69.05006,41.36183],[69.01308,41.22804],[68.7217,41.05025],[68.73945,40.96989],[68.65662,40.93861],[68.62221,41.03019],[68.49983,40.99669],[68.58444,40.91447],[68.63,40.59358],[68.49983,40.56437],[67.96736,40.83798],[68.1271,41.0324],[68.08273,41.08148],[67.98511,41.02794],[67.9644,41.14611],[66.69129,41.1311],[66.53302,41.87388],[66.00546,41.94455],[66.09482,42.93426],[65.85194,42.85481],[65.53277,43.31856],[65.18666,43.48835],[64.96464,43.74748],[64.53885,43.56941],[63.34656,43.64003],[62.01711,43.51008],[61.01475,44.41383],[58.59711,45.58671],[55.97842,44.99622],[55.97832,44.99622],[55.97822,44.99617],[55.97811,44.99617],[55.97801,44.99612],[55.97801,44.99607],[55.97791,44.99607],[55.9778,44.99607],[55.9777,44.99601],[55.9777,44.99596],[55.9776,44.99591],[55.97749,44.99591],[55.97739,44.99591],[55.97739,44.99586],[55.97729,44.99586],[55.97718,44.99581],[55.97708,44.99576],[55.97698,44.9957],[55.97698,44.99565],[55.97687,44.9956],[55.97677,44.9956],[55.97677,44.99555],[55.97677,44.9955],[55.97667,44.99545],[55.97656,44.99539],[55.97646,44.99534],[55.97646,44.99529],[55.97636,44.99524],[55.97636,44.99519],[55.97625,44.99514],[55.97615,44.99508],[55.97615,44.99503],[55.97615,44.99498],[55.97615,44.99493],[55.97615,44.99483],[55.97615,44.99477],[55.97605,44.99477],[55.97605,44.99467],[55.97605,44.99462],[55.97605,44.99457],[55.97605,44.99452],[55.97594,44.99446],[55.97584,44.99441],[55.97584,44.99436],[55.97584,44.99431],[55.97584,44.99426],[55.97584,44.99421],[55.97584,44.99415],[55.97584,44.99405],[55.97584,44.994],[55.97584,44.9939],[55.97584,44.99384],[55.97584,44.99374],[55.97584,44.99369],[55.97584,44.99359],[55.97584,44.99353],[55.97584,44.99348],[55.97584,44.99343],[55.97584,44.99338],[55.97584,44.99328],[55.97584,44.99322]],[[70.54223,40.98787],[70.56077,41.00642],[70.6158,40.97661],[70.68112,40.90612],[70.6721,40.90555],[70.57501,40.98941],[70.54223,40.98787]]],[[[71.00236,40.18154],[71.01035,40.05481],[71.11037,40.01984],[71.11668,39.99291],[71.09063,39.99],[71.10501,39.95568],[71.04979,39.89808],[71.10531,39.91354],[71.16101,39.88423],[71.23067,39.93581],[71.1427,39.95026],[71.21139,40.03369],[71.12218,40.03052],[71.06305,40.1771],[71.00236,40.18154]]],[[[71.71511,39.96348],[71.7504,39.93701],[71.84316,39.95582],[71.86463,39.98598],[71.78838,40.01404],[71.71511,39.96348]]]]}},{"type":"Feature","properties":{"id":"ZA"},"geometry":{"type":"Polygon","coordinates":[[[15.70388,-29.23989],[16.22632,-35.03863],[38.88176,-48.03306],[33.10054,-26.92273],[32.89816,-26.8579],[32.35222,-26.86027],[32.29584,-26.852],[32.22302,-26.84136],[32.19409,-26.84032],[32.13315,-26.84345],[32.09664,-26.80721],[32.00893,-26.8096],[31.97463,-27.11057],[31.97592,-27.31675],[31.49834,-27.31549],[31.15027,-27.20151],[30.96088,-27.0245],[30.97757,-26.92706],[30.88826,-26.79622],[30.81101,-26.84722],[30.78927,-26.48271],[30.95819,-26.26303],[31.13073,-25.91558],[31.31237,-25.7431],[31.4175,-25.71886],[31.86881,-25.99973],[31.974,-25.95387],[31.92649,-25.84216],[32.00631,-25.65044],[31.97875,-25.46356],[32.01676,-25.38117],[32.03196,-25.10785],[31.9835,-24.29983],[31.90368,-24.18892],[31.87707,-23.95293],[31.77445,-23.90082],[31.70223,-23.72695],[31.67942,-23.60858],[31.56539,-23.47268],[31.55779,-23.176],[31.30611,-22.422],[31.16344,-22.32599],[31.08932,-22.34884],[30.86696,-22.28907],[30.6294,-22.32599],[30.48686,-22.31368],[30.38614,-22.34533],[30.28351,-22.35587],[30.2265,-22.2961],[30.13147,-22.30841],[29.92242,-22.19408],[29.76848,-22.14128],[29.64609,-22.12917],[29.37703,-22.19581],[29.21955,-22.17771],[29.18974,-22.18599],[29.15268,-22.21399],[29.10881,-22.21202],[29.0151,-22.22907],[28.91889,-22.44299],[28.63287,-22.55887],[28.34874,-22.5694],[28.04562,-22.8394],[28.04752,-22.90243],[27.93729,-22.96194],[27.93539,-23.04941],[27.74154,-23.2137],[27.6066,-23.21894],[27.52393,-23.37952],[27.33768,-23.40917],[26.99749,-23.65486],[26.84165,-24.24885],[26.51667,-24.47219],[26.46346,-24.60358],[26.39409,-24.63468],[25.8515,-24.75727],[25.84295,-24.78661],[25.88571,-24.87802],[25.72702,-25.25503],[25.69661,-25.29284],[25.6643,-25.4491],[25.58543,-25.6343],[25.33076,-25.76616],[25.12266,-25.75931],[25.01718,-25.72507],[24.8946,-25.80723],[24.67319,-25.81749],[24.44703,-25.73021],[24.36531,-25.773],[24.18287,-25.62916],[23.9244,-25.64286],[23.47588,-25.29971],[23.03497,-25.29971],[22.86012,-25.50572],[22.70808,-25.99186],[22.56365,-26.19668],[22.41921,-26.23078],[22.21206,-26.3773],[22.06192,-26.61882],[21.90703,-26.66808],[21.83291,-26.65959],[21.77114,-26.69015],[21.7854,-26.79199],[21.69322,-26.86152],[21.37869,-26.82083],[21.13353,-26.86661],[20.87031,-26.80047],[20.68596,-26.9039],[20.63275,-26.78181],[20.61754,-26.4692],[20.86081,-26.14892],[20.64795,-25.47827],[20.29826,-24.94869],[20.03678,-24.81004],[20.02809,-24.78725],[19.99817,-24.76768],[19.99882,-28.42622],[18.99885,-28.89165],[17.4579,-28.68718],[17.15405,-28.08573],[16.90446,-28.057],[16.59922,-28.53246],[16.46592,-28.57126],[16.45332,-28.63117],[15.70388,-29.23989]],[[27.01016,-29.65439],[27.33464,-29.48161],[27.4358,-29.33465],[27.47254,-29.31968],[27.45125,-29.29708],[27.48679,-29.29349],[27.54258,-29.25575],[27.5158,-29.2261],[27.55974,-29.18954],[27.75458,-28.89839],[27.8907,-28.91612],[27.88933,-28.88156],[27.9392,-28.84864],[27.98675,-28.8787],[28.02503,-28.85991],[28.1317,-28.7293],[28.2348,-28.69471],[28.30518,-28.69531],[28.40612,-28.6215],[28.65091,-28.57025],[28.68043,-28.58744],[29.40524,-29.21246],[29.44883,-29.3772],[29.33204,-29.45598],[29.28545,-29.58456],[29.12553,-29.76266],[29.16548,-29.91706],[28.9338,-30.05072],[28.80222,-30.10579],[28.68627,-30.12885],[28.399,-30.1592],[28.2319,-30.28476],[28.12073,-30.68072],[27.74814,-30.60635],[27.69467,-30.55862],[27.67819,-30.53437],[27.6521,-30.51707],[27.62137,-30.50509],[27.56781,-30.44562],[27.56901,-30.42504],[27.45452,-30.32239],[27.38108,-30.33456],[27.36649,-30.27246],[27.37293,-30.19401],[27.40778,-30.14577],[27.32555,-30.14785],[27.29603,-30.05473],[27.22719,-30.00718],[27.09489,-29.72796],[27.01016,-29.65439]]]}},{"type":"Feature","properties":{"id":"EU"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-63.35989,18.06012],[-63.33064,17.9615],[-63.13584,18.0541],[-63.11096,18.05368],[-63.09686,18.04608],[-63.07759,18.04943],[-63.0579,18.06614],[-63.04039,18.05619],[-63.02323,18.05757],[-62.93924,18.02904],[-62.75637,18.13489],[-62.86666,18.19278],[-63.35989,18.06012]]],[[[-62.17275,16.35721],[-61.81728,15.58058],[-61.44899,15.79571],[-60.95725,15.70997],[-60.71337,16.48911],[-61.44461,16.81958],[-61.83929,16.66647],[-62.17275,16.35721]]],[[[-61.51867,14.96709],[-61.26561,14.25664],[-60.5958,14.23076],[-60.69955,15.22234],[-61.51867,14.96709]]],[[[-54.6084,2.32856],[-54.16286,2.10779],[-53.78743,2.34412],[-52.96539,2.1881],[-52.6906,2.37298],[-52.31787,3.17896],[-51.85573,3.83427],[-51.82312,3.85825],[-51.79599,3.89336],[-51.61983,4.14596],[-51.63798,4.51394],[-51.35485,4.8383],[-53.7094,6.2264],[-54.01074,5.68785],[-54.01877,5.52789],[-54.26916,5.26909],[-54.4717,4.91964],[-54.38444,4.13222],[-54.19367,3.84387],[-54.05128,3.63557],[-53.98914,3.627],[-53.9849,3.58697],[-54.28534,2.67798],[-54.42864,2.42442],[-54.6084,2.32856]]],[[[-32.42346,39.07068],[-15.92339,29.50503],[-18.67893,29.62861],[-18.8556,26.96222],[-14.43883,27.02969],[-12.42686,29.61659],[-14.33337,30.94071],[-7.37282,36.96896],[-7.27694,35.93599],[-5.64962,35.93752],[-5.10878,36.05227],[-2.85819,35.63219],[-2.27707,35.35051],[2.46645,37.97429],[5.18061,39.43581],[3.4481,42.4358],[7.52234,41.54558],[7.89009,38.19924],[11.2718,37.6713],[12.02012,35.25036],[12.80065,35.1178],[13.4634,35.88474],[14.74801,35.36688],[15.10171,36.26215],[18.75365,39.82496],[18.83516,40.36999],[16.15283,42.18525],[18.45131,42.21682],[18.54128,42.39171],[18.52152,42.42302],[18.43588,42.48556],[18.44307,42.51077],[18.43735,42.55921],[18.36197,42.61423],[18.24318,42.6112],[17.88201,42.83668],[17.80854,42.9182],[17.7948,42.89556],[17.68151,42.92725],[17.6444,42.88641],[17.5392,42.92787],[17.70879,42.97223],[17.64268,43.08595],[17.46986,43.16559],[17.286,43.33065],[17.25579,43.40353],[17.29699,43.44542],[17.24411,43.49376],[17.15828,43.49376],[17.00585,43.58037],[16.80736,43.76011],[16.75316,43.77157],[16.70922,43.84887],[16.55472,43.95326],[16.50528,44.0244],[16.43629,44.02826],[16.43662,44.07523],[16.36864,44.08263],[16.18688,44.27012],[16.21346,44.35231],[16.12969,44.38275],[16.16814,44.40679],[16.10566,44.52586],[16.03012,44.55572],[16.00884,44.58605],[16.05828,44.61538],[15.89348,44.74964],[15.8255,44.71501],[15.72584,44.82334],[15.79472,44.8455],[15.76096,44.87045],[15.74723,44.96818],[15.78568,44.97401],[15.74585,45.0638],[15.78842,45.11519],[15.76371,45.16508],[15.83512,45.22459],[15.98412,45.23088],[16.12153,45.09616],[16.29036,44.99732],[16.35404,45.00241],[16.35863,45.03529],[16.3749,45.05206],[16.38219,45.05139],[16.38309,45.05955],[16.40023,45.1147],[16.4634,45.14522],[16.49155,45.21153],[16.52982,45.22713],[16.5501,45.2212],[16.56559,45.22307],[16.60194,45.23042],[16.64962,45.20714],[16.74845,45.20393],[16.78219,45.19002],[16.81137,45.18434],[16.83804,45.18951],[16.92405,45.27607],[16.9385,45.22742],[17.0415,45.20759],[17.18438,45.14764],[17.24325,45.146],[17.25131,45.14957],[17.26815,45.18444],[17.32092,45.16246],[17.33573,45.14521],[17.41229,45.13335],[17.4498,45.16119],[17.45615,45.12523],[17.47589,45.12656],[17.51469,45.10791],[17.59104,45.10816],[17.66571,45.13408],[17.84826,45.04489],[17.87148,45.04645],[17.93706,45.08016],[17.97336,45.12245],[17.97834,45.13831],[17.99479,45.14958],[18.01594,45.15163],[18.03121,45.12632],[18.1624,45.07654],[18.24387,45.13699],[18.32077,45.1021],[18.41896,45.11083],[18.47939,45.05871],[18.65723,45.07544],[18.78357,44.97741],[18.80661,44.93561],[18.76369,44.93707],[18.76347,44.90669],[18.8704,44.85097],[19.01994,44.85493],[18.98957,44.90645],[19.02871,44.92541],[19.06853,44.89915],[19.15573,44.95409],[19.05205,44.97692],[19.1011,45.01191],[19.07952,45.14668],[19.14063,45.12972],[19.19144,45.17863],[19.43589,45.17137],[19.41941,45.23475],[19.28208,45.23813],[19.10774,45.29547],[18.97446,45.37528],[18.99918,45.49333],[19.08364,45.48804],[19.07471,45.53086],[18.94562,45.53712],[18.88776,45.57253],[18.96691,45.66731],[18.90305,45.71863],[18.85783,45.85493],[18.81394,45.91329],[18.99712,45.93537],[19.01284,45.96529],[19.0791,45.96458],[19.10388,46.04015],[19.14543,45.9998],[19.28826,45.99694],[19.52473,46.1171],[19.56113,46.16824],[19.66007,46.19005],[19.81491,46.1313],[19.93508,46.17553],[20.01816,46.17696],[20.03533,46.14509],[20.09713,46.17315],[20.26068,46.12332],[20.35862,45.99356],[20.54818,45.89939],[20.65645,45.82801],[20.70069,45.7493],[20.77416,45.75601],[20.78446,45.78522],[20.82364,45.77738],[20.80361,45.65875],[20.76798,45.60969],[20.83321,45.53567],[20.77217,45.49788],[20.86026,45.47295],[20.87948,45.42743],[21.09894,45.30144],[21.17612,45.32566],[21.20392,45.2677],[21.29398,45.24148],[21.48278,45.19557],[21.51299,45.15345],[21.4505,45.04294],[21.35855,45.01941],[21.54938,44.9327],[21.56328,44.89502],[21.48202,44.87199],[21.44013,44.87613],[21.35643,44.86364],[21.38802,44.78133],[21.55007,44.77304],[21.60019,44.75208],[21.61942,44.67059],[21.67504,44.67107],[21.71692,44.65349],[21.7795,44.66165],[21.99364,44.63395],[22.08016,44.49844],[22.13234,44.47444],[22.18315,44.48179],[22.30844,44.6619],[22.45301,44.7194],[22.61917,44.61489],[22.69196,44.61587],[22.76749,44.54446],[22.70981,44.51852],[22.61368,44.55719],[22.56493,44.53419],[22.54021,44.47836],[22.45436,44.47258],[22.56012,44.30712],[22.68166,44.28206],[22.67173,44.21564],[22.61711,44.16938],[22.61688,44.06534],[22.41449,44.00514],[22.35558,43.81281],[22.41043,43.69566],[22.47582,43.6558],[22.53397,43.47225],[22.82036,43.33665],[22.89727,43.22417],[23.00806,43.19279],[22.98104,43.11199],[22.89521,43.03625],[22.78397,42.98253],[22.74826,42.88701],[22.54302,42.87774],[22.43309,42.82057],[22.4997,42.74144],[22.43983,42.56851],[22.55669,42.50144],[22.51961,42.3991],[22.47498,42.3915],[22.45919,42.33822],[22.34773,42.31725],[22.38136,42.30339],[22.47251,42.20393],[22.50289,42.19527],[22.51224,42.15457],[22.67701,42.06614],[22.86749,42.02275],[22.90254,41.87587],[22.96682,41.77137],[23.01239,41.76527],[23.03342,41.71034],[22.95513,41.63265],[22.96331,41.35782],[22.93334,41.34104],[22.81199,41.3398],[22.76408,41.32225],[22.74538,41.16321],[22.71266,41.13945],[22.65306,41.18168],[22.62852,41.14385],[22.58295,41.11568],[22.5549,41.13065],[22.42285,41.11921],[22.26744,41.16409],[22.17629,41.15969],[22.1424,41.12449],[22.06527,41.15617],[21.90869,41.09191],[21.91102,41.04786],[21.7556,40.92525],[21.69601,40.9429],[21.57448,40.86076],[21.53007,40.90759],[21.41555,40.9173],[21.35595,40.87578],[21.25779,40.86165],[21.21105,40.8855],[21.15262,40.85546],[20.97887,40.85475],[20.98396,40.79109],[20.95752,40.76982],[20.98134,40.76046],[21.05833,40.66586],[21.03932,40.56299],[20.96908,40.51526],[20.94925,40.46625],[20.83688,40.47882],[20.7906,40.42726],[20.78234,40.35803],[20.71789,40.27739],[20.67162,40.09433],[20.62566,40.0897],[20.61081,40.07866],[20.55593,40.06524],[20.51297,40.08168],[20.48487,40.06271],[20.42373,40.06777],[20.37911,39.99058],[20.31135,39.99438],[20.41546,39.82832],[20.41475,39.81437],[20.38572,39.78516],[20.30804,39.81563],[20.29152,39.80421],[20.31961,39.72799],[20.27412,39.69884],[20.22707,39.67459],[20.22376,39.64532],[20.15988,39.652],[20.12956,39.65805],[20.05189,39.69112],[20.00957,39.69227],[19.98042,39.6504],[19.92466,39.69533],[19.97622,39.78684],[19.95905,39.82857],[19.0384,40.35325],[19.20409,39.7532],[22.5213,33.45682],[29.73302,35.92555],[29.69611,36.10365],[29.61805,36.14179],[29.61002,36.1731],[29.48192,36.18377],[29.30783,36.01033],[28.23708,36.56812],[27.95037,36.46155],[27.89482,36.69898],[27.46117,36.53789],[27.24613,36.71622],[27.45627,36.9008],[27.20312,36.94571],[27.14757,37.32],[26.95583,37.64989],[26.99377,37.69034],[27.16428,37.72343],[27.05537,37.9131],[26.21136,38.17558],[26.24183,38.44695],[26.32173,38.48731],[26.21136,38.65436],[26.61814,38.81372],[26.70773,39.0312],[26.43357,39.43096],[25.94257,39.39358],[25.61285,40.17161],[26.04292,40.3958],[25.94795,40.72797],[26.03489,40.73051],[26.0754,40.72772],[26.08638,40.73214],[26.12495,40.74283],[26.12854,40.77339],[26.15685,40.80709],[26.21351,40.83298],[26.20856,40.86048],[26.26169,40.9168],[26.29441,40.89119],[26.28623,40.93005],[26.32259,40.94042],[26.35894,40.94292],[26.33297,40.98388],[26.3606,41.02027],[26.31928,41.07386],[26.32259,41.24929],[26.39861,41.25053],[26.5209,41.33993],[26.5837,41.32131],[26.62997,41.34613],[26.61767,41.42281],[26.59742,41.48058],[26.59196,41.60491],[26.5209,41.62592],[26.47958,41.67037],[26.35957,41.71149],[26.32952,41.73637],[26.33589,41.76802],[26.36952,41.82265],[26.53968,41.82653],[26.57961,41.90024],[26.56051,41.92995],[26.62996,41.97644],[26.79143,41.97386],[26.95638,42.00741],[27.03277,42.0809],[27.08486,42.08735],[27.19251,42.06028],[27.22376,42.10152],[27.27411,42.10409],[27.45478,41.96591],[27.52379,41.93756],[27.55191,41.90928],[27.69949,41.97515],[27.81235,41.94803],[27.83492,41.99709],[27.91479,41.97902],[28.02971,41.98066],[28.32297,41.98371],[29.24336,43.70874],[30.04414,45.08461],[29.69272,45.19227],[29.65428,45.25629],[29.68175,45.26885],[29.59798,45.38857],[29.42632,45.44545],[29.24779,45.43388],[28.96077,45.33164],[28.94292,45.28045],[28.81383,45.3384],[28.78911,45.24179],[28.71358,45.22631],[28.5735,45.24759],[28.34554,45.32102],[28.28504,45.43907],[28.21139,45.46895],[28.18741,45.47358],[28.08927,45.6051],[28.16568,45.6421],[28.13111,45.92819],[28.08612,46.01105],[28.13684,46.18099],[28.10937,46.22852],[28.19864,46.31869],[28.18902,46.35283],[28.25769,46.43334],[28.22281,46.50481],[28.24808,46.64305],[28.12173,46.82283],[28.09095,46.97621],[27.81892,47.1381],[27.73172,47.29248],[27.68706,47.28962],[27.60263,47.32507],[27.55731,47.46637],[27.47942,47.48113],[27.3979,47.59473],[27.32202,47.64009],[27.25519,47.71366],[27.29069,47.73722],[27.1618,47.92391],[27.15622,47.98538],[27.02985,48.09083],[27.04118,48.12522],[26.96119,48.13003],[26.98042,48.15752],[26.94265,48.1969],[26.87708,48.19919],[26.81161,48.25049],[26.62823,48.25804],[26.55202,48.22445],[26.33504,48.18418],[26.17711,47.99246],[26.05901,47.9897],[25.77723,47.93919],[25.63878,47.94924],[25.23778,47.89403],[25.11144,47.75203],[24.88896,47.7234],[24.81893,47.82031],[24.70632,47.84428],[24.61994,47.95062],[24.43578,47.97131],[24.34926,47.9244],[24.22566,47.90231],[24.11281,47.91487],[24.06466,47.95317],[24.02999,47.95087],[24.00801,47.968],[23.98553,47.96076],[23.96337,47.96672],[23.94192,47.94868],[23.89352,47.94512],[23.8602,47.9329],[23.80904,47.98142],[23.75188,47.99705],[23.66262,47.98786],[23.63894,48.00293],[23.5653,48.00499],[23.52803,48.01818],[23.4979,47.96858],[23.33577,48.0237],[23.27397,48.08245],[23.15999,48.12188],[23.1133,48.08061],[23.08858,48.00716],[23.0158,47.99338],[22.92241,48.02002],[22.94301,47.96672],[22.89849,47.95851],[22.84276,47.98602],[22.87847,48.04665],[22.81804,48.11363],[22.73427,48.12005],[22.66835,48.09162],[22.58733,48.10813],[22.59007,48.15121],[22.49806,48.25189],[22.38133,48.23726],[22.2083,48.42534],[22.14689,48.4005],[22.16023,48.56548],[22.21379,48.6218],[22.34151,48.68893],[22.42934,48.92857],[22.48296,48.99172],[22.54338,49.01424],[22.56155,49.08865],[22.89122,49.00725],[22.86336,49.10513],[22.72009,49.20288],[22.748,49.32759],[22.69444,49.49378],[22.64534,49.53094],[22.78304,49.65543],[22.80261,49.69098],[22.83179,49.69875],[22.99329,49.84249],[23.28221,50.0957],[23.67635,50.33385],[23.71382,50.38248],[23.79445,50.40481],[23.99563,50.41289],[24.03668,50.44507],[24.07048,50.5071],[24.0996,50.60752],[24.0595,50.71625],[23.95925,50.79271],[23.99254,50.83847],[24.0952,50.83262],[24.14524,50.86128],[24.04576,50.90196],[23.92217,51.00836],[23.90376,51.07697],[23.80678,51.18405],[23.63858,51.32182],[23.69905,51.40871],[23.62751,51.50512],[23.56236,51.53673],[23.57053,51.55938],[23.53198,51.74298],[23.62691,51.78208],[23.61523,51.92066],[23.68733,51.9906],[23.64066,52.07626],[23.61,52.11264],[23.54314,52.12148],[23.47859,52.18215],[23.20071,52.22848],[23.18196,52.28812],[23.34141,52.44845],[23.45112,52.53774],[23.58296,52.59868],[23.73615,52.6149],[23.93763,52.71332],[23.91805,52.94016],[23.94689,52.95919],[23.92184,53.02079],[23.87548,53.0831],[23.91393,53.16469],[23.85657,53.22923],[23.81995,53.24131],[23.62004,53.60942],[23.51284,53.95052],[23.61677,53.92691],[23.71726,53.93379],[23.80543,53.89558],[23.81309,53.94205],[23.95098,53.9613],[23.98837,53.92554],[24.19638,53.96405],[24.34128,53.90076],[24.44411,53.90076],[24.62275,54.00217],[24.69652,54.01901],[24.69185,53.96543],[24.74279,53.96663],[24.85311,54.02862],[24.77131,54.11091],[24.96894,54.17589],[24.991,54.14241],[25.0728,54.13419],[25.19199,54.219],[25.22705,54.26271],[25.35559,54.26544],[25.509,54.30267],[25.56823,54.25212],[25.51452,54.17799],[25.54724,54.14925],[25.64875,54.1259],[25.71084,54.16704],[25.78563,54.15747],[25.78553,54.23327],[25.68513,54.31727],[25.55425,54.31591],[25.5376,54.33158],[25.63371,54.42075],[25.62203,54.4656],[25.64813,54.48704],[25.68045,54.5321],[25.75977,54.57252],[25.74122,54.80108],[25.89462,54.93438],[25.99129,54.95705],[26.05907,54.94631],[26.13386,54.98924],[26.20397,54.99729],[26.26941,55.08032],[26.23202,55.10439],[26.30628,55.12536],[26.35121,55.1525],[26.46249,55.12814],[26.51481,55.16051],[26.54753,55.14181],[26.69243,55.16718],[26.68075,55.19787],[26.72983,55.21788],[26.73017,55.24226],[26.835,55.28182],[26.83266,55.30444],[26.80929,55.31642],[26.6714,55.33902],[26.5709,55.32572],[26.44937,55.34832],[26.5522,55.40277],[26.55094,55.5093],[26.63167,55.57887],[26.63231,55.67968],[26.64888,55.70515],[26.71802,55.70645],[26.76872,55.67658],[26.87448,55.7172],[26.97153,55.8102],[27.1559,55.85032],[27.27804,55.78299],[27.3541,55.8089],[27.61683,55.78558],[27.63065,55.89687],[27.97865,56.11849],[28.15217,56.16964],[28.23716,56.27588],[28.16599,56.37806],[28.19057,56.44637],[28.10069,56.524],[28.13526,56.57989],[28.04768,56.59004],[27.86101,56.88204],[27.66511,56.83921],[27.86101,57.29402],[27.52453,57.42826],[27.56832,57.53728],[27.34698,57.52242],[27.31919,57.57672],[27.40393,57.62125],[27.3746,57.66834],[27.52615,57.72843],[27.50171,57.78842],[27.56689,57.83356],[27.78526,57.83963],[27.81841,57.89244],[27.67282,57.92627],[27.62393,58.09462],[27.48541,58.22615],[27.55489,58.39525],[27.36366,58.78381],[27.74429,58.98351],[27.80482,59.1116],[27.87978,59.18097],[27.90911,59.24353],[28.00689,59.28351],[28.14215,59.28934],[28.19284,59.35791],[28.20537,59.36491],[28.21137,59.38058],[28.19061,59.39962],[28.04187,59.47017],[27.85643,59.58538],[26.90044,59.63819],[26.32936,60.00121],[27.44953,60.22766],[27.71177,60.3893],[27.77352,60.52722],[28.47974,60.93365],[28.82816,61.1233],[29.01829,61.17448],[31.10136,62.43042],[31.38369,62.66284],[31.58535,62.91642],[31.29294,63.09035],[31.23244,63.22239],[30.49637,63.46666],[29.98213,63.75795],[30.25437,63.83364],[30.55687,64.09036],[30.4762,64.25728],[30.06279,64.35782],[30.01238,64.57513],[30.12329,64.64862],[30.05271,64.79072],[29.68972,64.80789],[29.61914,65.05993],[29.84096,65.1109],[29.8813,65.22101],[29.61914,65.23791],[29.68972,65.31803],[29.84096,65.56945],[29.74013,65.64025],[29.97205,65.70256],[30.16363,65.66935],[29.91155,66.13863],[28.9839,66.94139],[29.91155,67.51507],[30.02041,67.67523],[29.66955,67.79872],[29.34179,68.06655],[28.62982,68.19816],[28.43941,68.53366],[28.78224,68.86696],[28.45957,68.91417],[28.91738,69.04774],[28.81248,69.11997],[28.8629,69.22395],[29.31664,69.47994],[29.12697,69.69193],[28.36883,69.81658],[28.32849,69.88605],[27.97558,69.99671],[27.95542,70.0965],[27.57226,70.06215],[27.05802,69.92069],[26.64461,69.96565],[26.40261,69.91377],[25.96904,69.68397],[25.69679,69.27039],[25.75729,68.99383],[25.61613,68.89602],[25.42455,68.90328],[25.12206,68.78684],[25.10189,68.63307],[24.93048,68.61102],[24.90023,68.55579],[24.74898,68.65143],[24.18432,68.73936],[24.02299,68.81601],[23.781,68.84514],[23.68017,68.70276],[23.13064,68.64684],[22.53321,68.74393],[22.38367,68.71561],[22.27276,68.89514],[21.63833,69.27485],[21.27827,69.31281],[21.00732,69.22755],[20.98641,69.18809],[21.11099,69.10291],[21.05775,69.0356],[20.72171,69.11874],[20.55258,69.06069],[20.0695,69.04469],[20.28444,68.93283],[20.33435,68.80174],[20.22027,68.67246],[19.95647,68.55546],[20.22027,68.48759],[19.93508,68.35911],[18.97255,68.52416],[18.63032,68.50849],[18.39503,68.58672],[18.1241,68.53721],[18.13836,68.20874],[17.90787,67.96537],[17.30416,68.11591],[16.7409,67.91037],[16.38441,67.52923],[16.12774,67.52106],[16.09922,67.4364],[16.39154,67.21653],[16.35589,67.06419],[15.37197,66.48217],[15.49318,66.28509],[15.05113,66.15572],[14.53778,66.12399],[14.50926,65.31786],[13.64276,64.58402],[14.11117,64.46674],[14.16051,64.18725],[13.98222,64.00953],[13.23411,64.09087],[12.74105,64.02171],[12.14928,63.59373],[12.19919,63.47935],[11.98529,63.27487],[12.19919,63.00104],[12.07085,62.6297],[12.29187,62.25699],[12.14746,61.7147],[12.40595,61.57226],[12.57707,61.56547],[12.86939,61.35427],[12.69115,61.06584],[12.2277,61.02442],[12.59133,60.50559],[12.52003,60.13846],[12.36317,59.99259],[12.15641,59.8926],[11.87121,59.86039],[11.92112,59.69531],[11.69297,59.59442],[11.8213,59.24985],[11.65732,58.90177],[11.45199,58.89604],[11.4601,58.99022],[11.34459,59.11672],[11.15367,59.07862],[11.08911,58.98745],[10.64958,58.89391],[10.40861,58.38489],[7.28637,57.35913],[8.02459,55.09613],[5.45168,54.20039],[2.56575,51.85301],[2.18458,51.52087],[1.17405,50.74239],[-2.02963,49.91866],[-2.09454,49.46288],[-1.83944,49.23037],[-2.00491,48.86706],[-2.5234,48.91595],[-2.56423,49.22209],[-2.9511,49.31141],[-5.81385,48.52441],[-1.81005,43.59738],[-10.14298,44.17365],[-9.14112,41.86623],[-30.18705,41.4962],[-32.42346,39.07068]],[[-5.40134,36.14896],[-5.38545,36.15481],[-5.36494,36.15496],[-5.34536,36.15501],[-5.33822,36.15272],[-5.27801,36.14942],[-5.28217,36.09907],[-5.3004,36.07439],[-5.32837,36.05935],[-5.36503,36.06205],[-5.39074,36.10278],[-5.40134,36.14896]],[[1.41245,42.53539],[1.4234,42.55959],[1.44529,42.56722],[1.42512,42.58292],[1.44197,42.60217],[1.47986,42.61346],[1.46718,42.63296],[1.48043,42.65203],[1.50867,42.64483],[1.55418,42.65669],[1.60085,42.62703],[1.63485,42.62957],[1.6625,42.61982],[1.68267,42.62533],[1.73452,42.61515],[1.72588,42.59098],[1.7858,42.57698],[1.73683,42.55492],[1.72515,42.50338],[1.70571,42.48867],[1.66826,42.50779],[1.65674,42.47125],[1.58933,42.46275],[1.57953,42.44957],[1.55937,42.45808],[1.55073,42.43299],[1.5127,42.42959],[1.44529,42.43724],[1.43838,42.47848],[1.41648,42.48315],[1.46661,42.50949],[1.44759,42.54431],[1.41245,42.53539]],[[5.95781,46.12925],[5.9641,46.14412],[5.97508,46.15863],[5.98188,46.17392],[5.98846,46.17046],[5.99573,46.18587],[5.96515,46.19638],[5.97542,46.21525],[6.02461,46.23313],[6.03342,46.2383],[6.04602,46.23127],[6.05029,46.23518],[6.0633,46.24583],[6.07072,46.24085],[6.08563,46.24651],[6.10071,46.23772],[6.12446,46.25059],[6.11926,46.2634],[6.1013,46.28512],[6.11697,46.29547],[6.1198,46.31157],[6.13876,46.33844],[6.15738,46.3491],[6.16987,46.36759],[6.15985,46.37721],[6.15016,46.3778],[6.09926,46.40768],[6.06407,46.41676],[6.08427,46.44305],[6.07269,46.46244],[6.1567,46.54402],[6.11084,46.57649],[6.27135,46.68251],[6.38351,46.73171],[6.45209,46.77502],[6.43216,46.80336],[6.46456,46.88865],[6.43341,46.92703],[6.71531,47.0494],[6.68823,47.06616],[6.76788,47.1208],[6.8489,47.15933],[6.9508,47.24338],[6.95108,47.26428],[6.94316,47.28747],[7.05305,47.33304],[7.0564,47.35134],[7.03125,47.36996],[6.87959,47.35335],[6.88542,47.37262],[6.93744,47.40714],[6.93953,47.43388],[7.0024,47.45264],[6.98425,47.49432],[7.0231,47.50522],[7.07425,47.48863],[7.12781,47.50371],[7.16249,47.49025],[7.19583,47.49455],[7.17026,47.44312],[7.24669,47.4205],[7.33526,47.44186],[7.35603,47.43432],[7.40308,47.43638],[7.43088,47.45846],[7.4462,47.46264],[7.4583,47.47216],[7.42923,47.48628],[7.43356,47.49712],[7.47534,47.47932],[7.51076,47.49651],[7.49804,47.51798],[7.5229,47.51644],[7.53199,47.5284],[7.51904,47.53515],[7.50588,47.52856],[7.49691,47.53821],[7.50873,47.54546],[7.51723,47.54578],[7.52831,47.55347],[7.53634,47.55553],[7.55652,47.56779],[7.55689,47.57232],[7.56548,47.57617],[7.56684,47.57785],[7.58386,47.57536],[7.58945,47.59017],[7.60523,47.58519],[7.60459,47.57869],[7.61929,47.57683],[7.64309,47.59151],[7.64213,47.5944],[7.64599,47.59695],[7.67395,47.59212],[7.68229,47.59905],[7.69385,47.60099],[7.68486,47.59601],[7.67115,47.5871],[7.68904,47.57133],[7.67655,47.56435],[7.63338,47.56256],[7.65083,47.54662],[7.66174,47.54554],[7.6656,47.53752],[7.68101,47.53232],[7.69642,47.53297],[7.71961,47.54219],[7.75261,47.54599],[7.79486,47.55691],[7.80098,47.56335],[7.811279,47.56946],[7.81901,47.58798],[7.833466,47.58663],[7.84167,47.58196],[7.862692,47.58808],[7.88664,47.58854],[7.898354,47.58408],[7.90673,47.57674],[7.911615,47.56686],[7.906809,47.56037],[7.91251,47.55031],[7.920585,47.5469],[7.932644,47.54704],[7.94614,47.5436],[7.95682,47.55789],[7.97581,47.55493],[8.00113,47.55616],[8.02136,47.55096],[8.04383,47.55443],[8.06663,47.56374],[8.08557,47.55768],[8.10002,47.56504],[8.10395,47.57918],[8.11543,47.5841],[8.13662,47.58432],[8.13823,47.59147],[8.14947,47.59558],[8.1652,47.5945],[8.19378,47.61636],[8.20617,47.62141],[8.22011,47.6181],[8.22577,47.60385],[8.23809,47.61204],[8.25863,47.61571],[8.26313,47.6103],[8.2824,47.61225],[8.29722,47.60603],[8.29524,47.5919],[8.30277,47.58607],[8.32735,47.57133],[8.38273,47.56608],[8.39477,47.57826],[8.43235,47.56617],[8.49431,47.58107],[8.48949,47.588],[8.46637,47.58389],[8.45578,47.60121],[8.50747,47.61897],[8.51686,47.63476],[8.55756,47.62394],[8.57586,47.59537],[8.60348,47.61204],[8.59545,47.64298],[8.60701,47.65271],[8.61471,47.64514],[8.60412,47.63735],[8.62049,47.63757],[8.62884,47.65098],[8.61113,47.66332],[8.6052,47.67258],[8.57683,47.66158],[8.56141,47.67088],[8.52801,47.66059],[8.5322,47.64687],[8.49656,47.64709],[8.46605,47.64103],[8.4667,47.65747],[8.44711,47.65379],[8.42264,47.66667],[8.41346,47.66676],[8.40473,47.67499],[8.4211,47.68407],[8.40569,47.69855],[8.44807,47.72426],[8.45771,47.7493],[8.48868,47.77215],[8.56814,47.78001],[8.56415,47.80633],[8.61657,47.79998],[8.62408,47.7626],[8.64425,47.76398],[8.65292,47.80066],[8.68022,47.78599],[8.68985,47.75686],[8.71778,47.76571],[8.74251,47.75168],[8.70543,47.73121],[8.73671,47.7169],[8.72617,47.69651],[8.72809,47.69282],[8.75856,47.68969],[8.79511,47.67462],[8.79966,47.70222],[8.76965,47.7075],[8.77309,47.72059],[8.80663,47.73821],[8.82002,47.71458],[8.86989,47.70504],[8.85065,47.68209],[8.87383,47.67045],[8.87625,47.65441],[8.89946,47.64769],[8.94093,47.65596],[9.02093,47.6868],[9.09891,47.67801],[9.13845,47.66389],[9.15181,47.66904],[9.1705,47.65513],[9.1755,47.65584],[9.17593,47.65399],[9.18203,47.65598],[9.25619,47.65939],[9.55125,47.53629],[9.56312,47.49495],[9.58208,47.48344],[9.59482,47.46305],[9.60205,47.46165],[9.60484,47.46358],[9.60841,47.47178],[9.62158,47.45858],[9.62475,47.45685],[9.6423,47.45599],[9.65728,47.45383],[9.65863,47.44847],[9.64483,47.43842],[9.6446,47.43233],[9.65043,47.41937],[9.65136,47.40504],[9.6629,47.39591],[9.67334,47.39191],[9.67445,47.38429],[9.6711,47.37824],[9.66243,47.37136],[9.65427,47.36824],[9.62476,47.36639],[9.59978,47.34671],[9.58513,47.31334],[9.55857,47.29919],[9.54773,47.2809],[9.53116,47.27029],[9.56766,47.24281],[9.55176,47.22585],[9.56981,47.21926],[9.58264,47.20673],[9.56539,47.17124],[9.62623,47.14685],[9.63395,47.08443],[9.61216,47.07732],[9.60717,47.06091],[9.87935,47.01337],[9.88266,46.93343],[9.98058,46.91434],[10.10715,46.84296],[10.22675,46.86942],[10.24128,46.93147],[10.30031,46.92093],[10.36933,47.00212],[10.48376,46.93891],[10.47197,46.85698],[10.38659,46.67847],[10.40475,46.63671],[10.44686,46.64162],[10.49375,46.62049],[10.46136,46.53164],[10.25309,46.57432],[10.23674,46.63484],[10.10307,46.61003],[10.03715,46.44479],[10.165,46.41051],[10.10506,46.3372],[10.17862,46.25626],[10.14439,46.22992],[10.07055,46.21668],[9.95249,46.38045],[9.73086,46.35071],[9.71273,46.29266],[9.57015,46.2958],[9.46117,46.37481],[9.45936,46.50873],[9.40487,46.46621],[9.36128,46.5081],[9.28136,46.49685],[9.25502,46.43743],[9.29226,46.32717],[9.24503,46.23616],[9.01618,46.04928],[8.99257,45.9698],[9.09065,45.89906],[9.06642,45.8761],[9.04546,45.84968],[9.04059,45.8464],[9.03505,45.83976],[9.03793,45.83548],[9.03279,45.82865],[9.0298,45.82127],[9.00324,45.82055],[8.99663,45.83466],[8.9621,45.83707],[8.94737,45.84285],[8.91129,45.8388],[8.93504,45.86245],[8.94372,45.86587],[8.93649,45.86775],[8.88904,45.95465],[8.86688,45.96135],[8.85121,45.97239],[8.8319,45.9879],[8.79362,45.99207],[8.78585,45.98973],[8.79414,46.00913],[8.85617,46.0748],[8.80778,46.10085],[8.75697,46.10395],[8.62242,46.12112],[8.45032,46.26869],[8.46317,46.43712],[8.42464,46.46367],[8.30648,46.41587],[8.31162,46.38044],[8.08814,46.26692],[8.16866,46.17817],[8.11383,46.11577],[8.02906,46.10331],[7.98881,45.99867],[7.9049,45.99945],[7.85949,45.91485],[7.56343,45.97421],[7.10685,45.85653],[7.04151,45.92435],[7.00946,45.9944],[6.93862,46.06502],[6.87868,46.03855],[6.89321,46.12548],[6.78968,46.14058],[6.86052,46.28512],[6.77152,46.34784],[6.8024,46.39171],[6.82312,46.42661],[6.53358,46.45431],[6.25432,46.3632],[6.21981,46.31304],[6.24826,46.30175],[6.25137,46.29014],[6.23775,46.27822],[6.24952,46.26255],[6.26749,46.24745],[6.29474,46.26221],[6.31041,46.24417],[6.29663,46.22688],[6.27694,46.21566],[6.26007,46.21165],[6.24821,46.20531],[6.23913,46.20511],[6.23544,46.20714],[6.22175,46.20045],[6.22222,46.19888],[6.21844,46.19837],[6.21603,46.19507],[6.21273,46.19409],[6.21114,46.1927],[6.20539,46.19163],[6.19807,46.18369],[6.19552,46.18401],[6.18707,46.17999],[6.18871,46.16644],[6.18116,46.16187],[6.15305,46.15194],[6.13397,46.1406],[6.09926,46.14373],[6.09199,46.15191],[6.07491,46.14879],[6.05203,46.15191],[6.04564,46.14031],[6.03614,46.13712],[6.01791,46.14228],[5.9871,46.14499],[5.97893,46.13303],[5.95781,46.12925]],[[7.40903,43.7296],[7.41113,43.73156],[7.41291,43.73168],[7.41298,43.73311],[7.41233,43.73439],[7.42062,43.73977],[7.42299,43.74176],[7.42443,43.74087],[7.42809,43.74396],[7.43013,43.74895],[7.43624,43.75014],[7.43708,43.75197],[7.4389,43.75151],[7.4379,43.74963],[7.45448,43.7432],[7.53358,43.53609],[7.50102,43.51859],[7.41855,43.72479],[7.40903,43.7296]],[[12.40415,43.95485],[12.41414,43.95273],[12.42005,43.9578],[12.43662,43.95698],[12.44684,43.96597],[12.46205,43.97463],[12.47853,43.98052],[12.49406,43.98492],[12.50678,43.99113],[12.51463,43.99122],[12.5154,43.98508],[12.51064,43.98165],[12.51109,43.97201],[12.50622,43.97131],[12.50875,43.96198],[12.50655,43.95796],[12.51427,43.94897],[12.51553,43.94096],[12.50496,43.93017],[12.50269,43.92363],[12.49724,43.92248],[12.49247,43.91774],[12.49429,43.90973],[12.48771,43.89706],[12.45648,43.89369],[12.44184,43.90498],[12.41641,43.89991],[12.40935,43.9024],[12.41233,43.90956],[12.40733,43.92379],[12.41551,43.92984],[12.41165,43.93769],[12.40506,43.94325],[12.40415,43.95485]],[[12.44582,41.90194],[12.44815,41.90326],[12.44984,41.90545],[12.45091,41.90625],[12.45543,41.90738],[12.45561,41.90629],[12.45762,41.9058],[12.45755,41.9033],[12.45826,41.90281],[12.45834,41.90174],[12.4577,41.90115],[12.45691,41.90125],[12.45626,41.90172],[12.45435,41.90143],[12.45446,41.90028],[12.45181,41.90056],[12.44834,41.90095],[12.44582,41.90194]],[[18.57853,55.25302],[20.60454,55.40986],[20.95181,55.27994],[21.26425,55.24456],[21.35465,55.28427],[21.38446,55.29348],[21.46766,55.21115],[21.51095,55.18507],[21.55605,55.20311],[21.64954,55.1791],[21.85521,55.09493],[21.96505,55.07353],[21.99543,55.08691],[22.03984,55.07888],[22.02582,55.05078],[22.06087,55.02935],[22.11697,55.02131],[22.14267,55.05345],[22.31562,55.0655],[22.47688,55.04408],[22.58907,55.07085],[22.60075,55.01863],[22.65451,54.97037],[22.68723,54.9811],[22.76422,54.92521],[22.85083,54.88711],[22.87317,54.79492],[22.73631,54.72952],[22.73397,54.66604],[22.75467,54.6483],[22.74225,54.64339],[22.7522,54.63525],[22.68021,54.58486],[22.71293,54.56454],[22.67788,54.532],[22.70208,54.45312],[22.7253,54.41732],[22.79705,54.36264],[21.41123,54.32395],[20.63871,54.3706],[19.8038,54.44203],[19.64312,54.45423],[18.57853,55.25302]]],[[[-13.3292,54.24486],[-10.17712,50.85267],[-5.79914,52.03902],[-5.37267,53.63269],[-5.83481,53.87749],[-6.26218,54.09785],[-6.29003,54.11278],[-6.32694,54.09337],[-6.36279,54.11248],[-6.36605,54.07234],[-6.47849,54.06947],[-6.62842,54.03503],[-6.66264,54.0666],[-6.6382,54.17071],[-6.70175,54.20218],[-6.74575,54.18788],[-6.81583,54.22791],[-6.85179,54.29176],[-6.87775,54.34682],[-7.02034,54.4212],[-7.19145,54.31296],[-7.14908,54.22732],[-7.25012,54.20063],[-7.26316,54.13863],[-7.29493,54.12013],[-7.29687,54.1354],[-7.28017,54.16714],[-7.29157,54.17191],[-7.34005,54.14698],[-7.30553,54.11869],[-7.32834,54.11475],[-7.44567,54.1539],[-7.4799,54.12239],[-7.55812,54.12239],[-7.69501,54.20731],[-7.81397,54.20159],[-7.8596,54.21779],[-7.87101,54.29299],[-8.04555,54.36292],[-8.179,54.46763],[-8.04538,54.48941],[-7.99812,54.54427],[-7.8596,54.53671],[-7.70315,54.62077],[-7.93293,54.66603],[-7.83352,54.73854],[-7.75041,54.7103],[-7.64449,54.75265],[-7.54671,54.74606],[-7.54508,54.79401],[-7.47626,54.83084],[-7.4473,54.87003],[-7.44404,54.9403],[-7.40004,54.94498],[-7.4033,55.00391],[-7.34464,55.04688],[-7.2471,55.06933],[-6.9734,55.19878],[-6.71944,55.27952],[-6.79943,55.54107],[-7.93366,55.84142],[-13.3292,54.24486]]],[[[-5.38491,35.92591],[-5.37338,35.88417],[-5.35844,35.87375],[-5.34379,35.8711],[-5.27056,35.88794],[-5.27635,35.91222],[-5.38491,35.92591]]],[[[-2.97035,35.28852],[-2.96507,35.28801],[-2.96826,35.28296],[-2.96516,35.27967],[-2.95431,35.2728],[-2.95065,35.26576],[-2.93893,35.26737],[-2.92674,35.27313],[-2.92181,35.28599],[-2.92224,35.3401],[-2.96038,35.31609],[-2.96648,35.30475],[-2.96978,35.29459],[-2.97035,35.28852]]],[[[8.65769,47.68928],[8.66837,47.68437],[8.68985,47.69552],[8.70847,47.68904],[8.71773,47.69088],[8.70237,47.71453],[8.66416,47.71367],[8.67508,47.6979],[8.65769,47.68928]]],[[[8.95861,45.96485],[8.97604,45.96151],[8.97741,45.98317],[8.96668,45.98436],[8.95861,45.96485]]],[[[31.71872,35.1606],[32.10341,34.37973],[32.74412,34.43926],[32.75515,34.64985],[32.76136,34.68318],[32.79433,34.67883],[32.82717,34.70622],[32.86014,34.70585],[32.86167,34.68734],[32.9068,34.66102],[32.91398,34.67343],[32.93043,34.67091],[32.92807,34.66736],[32.93449,34.66241],[32.93693,34.67027],[32.94379,34.67111],[32.94683,34.67907],[32.95539,34.68471],[32.99135,34.68061],[32.98668,34.67268],[32.99014,34.65518],[32.97736,34.65277],[32.97079,34.66112],[32.95325,34.66462],[32.94796,34.6587],[32.94976,34.65204],[32.95471,34.64528],[32.95323,34.64075],[32.95891,34.62919],[32.96718,34.63446],[32.96968,34.64046],[33.0138,34.64424],[33.26744,34.49942],[33.83531,34.73974],[33.70575,34.97947],[33.70639,34.99303],[33.71514,35.00294],[33.69731,35.01754],[33.69938,35.03123],[33.67678,35.03866],[33.67742,35.05963],[33.68474,35.06602],[33.69095,35.06237],[33.70861,35.07644],[33.7161,35.07279],[33.70209,35.04882],[33.71482,35.03722],[33.73824,35.05321],[33.76106,35.04253],[33.78581,35.05104],[33.82067,35.07826],[33.84168,35.06823],[33.8541,35.07201],[33.87479,35.08881],[33.87097,35.09389],[33.87622,35.10457],[33.87224,35.12293],[33.88561,35.12449],[33.88943,35.12007],[33.88737,35.11408],[33.89853,35.11377],[33.91789,35.08688],[33.91299,35.07579],[33.90247,35.07686],[33.89485,35.06873],[33.88367,35.07877],[33.85261,35.0574],[33.8355,35.05777],[33.82051,35.0667],[33.8012,35.04786],[33.81524,35.04192],[33.83055,35.02865],[33.82875,35.01685],[33.84045,35.00616],[33.85216,35.00579],[33.85891,35.001],[33.85621,34.98956],[33.83505,34.98108],[33.84811,34.97075],[33.86432,34.97592],[33.90075,34.96623],[33.98684,34.76642],[35.48515,34.70851],[35.51152,36.10954],[32.82353,35.70297],[31.71872,35.1606]]],[[[33.7343,35.01178],[33.74144,35.01053],[33.7492,35.01319],[33.74983,35.02274],[33.74265,35.02329],[33.73781,35.02181],[33.7343,35.01178]]],[[[33.75682,34.99916],[33.76605,34.99543],[33.76738,34.99188],[33.7778,34.98981],[33.77843,34.988],[33.78149,34.98854],[33.78318,34.98699],[33.78571,34.98951],[33.78917,34.98854],[33.79191,34.98914],[33.78516,34.99582],[33.77553,34.99518],[33.77312,34.9976],[33.75994,35.00113],[33.75682,34.99916]]],[[[44.75722,-12.58368],[44.82644,-13.30845],[45.54824,-13.22353],[45.45962,-12.30345],[44.75722,-12.58368]]],[[[54.32269,-20.37973],[54.43368,-22.02482],[56.73473,-21.9174],[56.62373,-20.2711],[54.32269,-20.37973]]]]}},{"type":"Feature","properties":{"id":"UM"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-177.8563,29.18961],[-177.84531,27.68616],[-176.81808,27.68129],[-176.83456,29.19028],[-177.8563,29.18961]]],[[[-177.43928,1.65656],[-177.43039,-1.43294],[-175.33482,-1.40631],[-175.33167,1.67574],[-177.43928,1.65656]]],[[[-169.97341,16.32808],[-169.02347,16.32808],[-169.02347,17.13856],[-169.97341,17.13856],[-169.97341,16.32808]]],[[[-163.24994,7.12322],[-163.24478,5.24198],[-161.06795,5.2462],[-161.0731,7.1291],[-163.24994,7.12322]]],[[[-161.05669,1.11722],[-161.04969,-1.36251],[-158.62058,-1.35506],[-158.62734,1.1296],[-161.05669,1.11722]]],[[[-75.27909,18.17213],[-74.76465,18.06252],[-74.7289,18.71009],[-75.24866,18.6531],[-75.27909,18.17213]]],[[[166.27257,19.60026],[166.27567,19.02484],[166.94111,19.02804],[166.93801,19.60345],[166.27257,19.60026]]]]}},{"type":"Feature","properties":{"id":"PS"},"geometry":{"type":"MultiPolygon","coordinates":[[[[34.052,31.46619],[34.21853,31.32363],[34.23572,31.2966],[34.24012,31.29591],[34.26742,31.21998],[34.29417,31.24194],[34.36523,31.28963],[34.37381,31.30598],[34.36505,31.36404],[34.40077,31.40926],[34.48892,31.48365],[34.56797,31.54197],[34.48681,31.59711],[34.29262,31.70393],[34.052,31.46619]]],[[[34.87833,31.39321],[34.88932,31.37093],[34.92571,31.34337],[35.02459,31.35979],[35.13033,31.3551],[35.22921,31.37445],[35.39675,31.49572],[35.47672,31.49578],[35.55941,31.76535],[35.52758,31.9131],[35.54375,31.96587],[35.52012,32.04076],[35.57111,32.21877],[35.55807,32.38674],[35.42078,32.41562],[35.41048,32.43706],[35.41598,32.45593],[35.42034,32.46009],[35.40224,32.50136],[35.35212,32.52047],[35.30685,32.51024],[35.29306,32.50947],[35.25049,32.52453],[35.2244,32.55289],[35.15937,32.50466],[35.10882,32.4757],[35.10024,32.47856],[35.09236,32.47614],[35.08564,32.46948],[35.07059,32.4585],[35.05423,32.41754],[35.05311,32.4024],[35.0421,32.38242],[35.05142,32.3667],[35.04243,32.35008],[35.01772,32.33863],[35.01119,32.28684],[35.02939,32.2671],[35.01841,32.23981],[34.98885,32.20758],[34.95703,32.19522],[34.96009,32.17503],[34.99039,32.14626],[34.98507,32.12606],[34.99437,32.10962],[34.9863,32.09551],[35.00261,32.027],[34.98682,31.96935],[35.00124,31.93264],[35.03489,31.92448],[35.03978,31.89276],[35.03489,31.85919],[34.99712,31.85569],[34.9724,31.83352],[35.01978,31.82944],[35.05617,31.85685],[35.07677,31.85627],[35.14174,31.81325],[35.18603,31.80901],[35.18169,31.82542],[35.19461,31.82687],[35.21469,31.81835],[35.216,31.83894],[35.21128,31.863],[35.20381,31.86716],[35.20673,31.88151],[35.20791,31.8821],[35.20945,31.8815],[35.21016,31.88237],[35.21276,31.88153],[35.2136,31.88241],[35.22014,31.88264],[35.22294,31.87889],[35.22567,31.86745],[35.22817,31.8638],[35.2249,31.85433],[35.2304,31.84222],[35.24816,31.8458],[35.25753,31.8387],[35.251,31.83085],[35.26404,31.82567],[35.25573,31.81362],[35.26058,31.79064],[35.25225,31.7678],[35.26319,31.74846],[35.25182,31.73945],[35.24981,31.72543],[35.2438,31.7201],[35.24315,31.71244],[35.23972,31.70896],[35.22392,31.71899],[35.21937,31.71578],[35.20538,31.72388],[35.18023,31.72067],[35.16478,31.73242],[35.15474,31.73352],[35.15119,31.73634],[35.13931,31.73012],[35.12933,31.7325],[35.11895,31.71454],[35.10782,31.71594],[35.08226,31.69107],[35.00879,31.65426],[34.95249,31.59813],[34.9415,31.55601],[34.94356,31.50743],[34.93258,31.47816],[34.89756,31.43891],[34.87833,31.39321]]]]}},{"type":"Feature","properties":{"id":"TF"},"geometry":{"type":"MultiPolygon","coordinates":[[[[39.10324,-21.48967],[40.40841,-23.17181],[43.72277,-16.09877],[41.06663,-17.08802],[39.10324,-21.48967]]],[[[46.31615,-46.28749],[70.67507,-51.14192],[80.15867,-36.04977],[46.31615,-46.28749]]],[[[46.52682,-10.83678],[47.29063,-12.45583],[48.86266,-10.8109],[46.52682,-10.83678]]],[[[54.08717,-15.5001],[54.13761,-16.33002],[54.96649,-16.28353],[54.91606,-15.45342],[54.08717,-15.5001]]]]}},{"type":"Feature","properties":{"id":"EA"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-5.38491,35.92591],[-5.37338,35.88417],[-5.35844,35.87375],[-5.34379,35.8711],[-5.27056,35.88794],[-5.27635,35.91222],[-5.38491,35.92591]]],[[[-2.97035,35.28852],[-2.96507,35.28801],[-2.96826,35.28296],[-2.96516,35.27967],[-2.95431,35.2728],[-2.95065,35.26576],[-2.93893,35.26737],[-2.92674,35.27313],[-2.92181,35.28599],[-2.92224,35.3401],[-2.96038,35.31609],[-2.96648,35.30475],[-2.96978,35.29459],[-2.97035,35.28852]]]]}},{"type":"Feature","properties":{"id":"MA"},"geometry":{"type":"Polygon","coordinates":[[[-17.27295,21.93519],[-17.21511,21.34226],[-17.02707,21.34022],[-16.9978,21.36239],[-16.44269,21.39745],[-14.78487,21.36587],[-14.47329,21.63839],[-14.48112,22.00886],[-14.1291,22.41636],[-14.10361,22.75501],[-13.75627,23.77231],[-13.00628,24.01923],[-12.92147,24.39502],[-12.12281,25.13682],[-12.06001,26.04442],[-11.62052,26.05229],[-11.38635,26.611],[-11.23622,26.72023],[-11.35695,26.8505],[-10.68417,26.90984],[-9.81998,26.71379],[-9.56957,26.90042],[-9.08698,26.98639],[-8.71787,26.9898],[-8.77527,27.66663],[-8.66879,27.6666],[-8.6715,28.71194],[-7.61585,29.36252],[-6.95824,29.50924],[-6.78351,29.44634],[-6.69965,29.51623],[-5.75616,29.61407],[-5.72121,29.52322],[-5.58831,29.48103],[-5.21671,29.95253],[-4.6058,30.28343],[-4.31774,30.53229],[-3.64735,30.67539],[-3.65418,30.85566],[-3.54944,31.0503],[-3.77103,31.14984],[-3.77647,31.31912],[-3.66386,31.39202],[-3.66314,31.6339],[-2.82784,31.79459],[-2.93873,32.06557],[-2.46166,32.16603],[-1.22829,32.07832],[-1.15735,32.12096],[-1.24453,32.1917],[-1.24998,32.32993],[-0.9912,32.52467],[-1.37794,32.73628],[-1.54244,32.95499],[-1.46249,33.0499],[-1.67067,33.27084],[-1.59508,33.59929],[-1.73494,33.71721],[-1.64666,34.10405],[-1.78042,34.39018],[-1.69788,34.48056],[-1.84569,34.61907],[-1.73707,34.74226],[-1.97469,34.886],[-1.97833,34.93218],[-2.04734,34.93218],[-2.21445,35.04378],[-2.21248,35.08532],[-2.27707,35.35051],[-2.85819,35.63219],[-5.10878,36.05227],[-5.64962,35.93752],[-7.27694,35.93599],[-12.42686,29.61659],[-14.43883,27.02969],[-17.27295,21.93519]],[[-5.38491,35.92591],[-5.27635,35.91222],[-5.27056,35.88794],[-5.34379,35.8711],[-5.35844,35.87375],[-5.37338,35.88417],[-5.38491,35.92591]],[[-4.30191,35.17419],[-4.29436,35.17149],[-4.30112,35.17058],[-4.30191,35.17419]],[[-3.90602,35.21494],[-3.90288,35.22024],[-3.88617,35.21406],[-3.88926,35.20841],[-3.90602,35.21494]],[[-2.97035,35.28852],[-2.96978,35.29459],[-2.96648,35.30475],[-2.96038,35.31609],[-2.92224,35.3401],[-2.92181,35.28599],[-2.92674,35.27313],[-2.93893,35.26737],[-2.95065,35.26576],[-2.95431,35.2728],[-2.96516,35.27967],[-2.96826,35.28296],[-2.96507,35.28801],[-2.97035,35.28852]],[[-2.44896,35.18777],[-2.41265,35.1877],[-2.41312,35.17111],[-2.44887,35.17075],[-2.44896,35.18777]]]}},{"type":"Feature","properties":{"id":"AU"},"geometry":{"type":"MultiPolygon","coordinates":[[[[71.08716,-53.87687],[75.44182,-53.99822],[72.87012,-51.48322],[71.08716,-53.87687]]],[[[96.53724,-12.46709],[97.24513,-12.47233],[97.25212,-11.57036],[96.54423,-11.5651],[96.53724,-12.46709]]],[[[99.6403,-26.70736],[129,-43.08851],[137.66184,-47.26522],[158.89388,-55.66823],[159.92772,-54.25538],[152.57175,-39.16128],[155.3142,-27.34698],[159.35793,-33.24807],[165.46901,-28.32101],[169.35326,-30.60259],[169.6687,-29.09191],[166.75333,-27.25416],[158.4748,-21.86428],[157.46481,-18.93777],[158.60851,-15.7108],[155.22803,-12.9001],[144.30183,-9.48146],[142.81927,-9.31709],[142.5723,-9.35994],[142.31447,-9.24611],[142.23304,-9.19253],[142.1462,-9.19923],[142.0953,-9.23534],[142.0601,-9.56571],[140.88922,-9.34945],[139.41724,-8.97063],[127.55165,-9.05052],[125.68138,-9.85176],[122.91521,-11.65621],[99.6403,-26.70736]]],[[[105.29647,-10.80676],[105.9699,-10.81333],[105.97626,-10.18257],[105.30283,-10.176],[105.29647,-10.80676]]]]}},{"type":"Feature","properties":{"id":"CH"},"geometry":{"type":"Polygon","coordinates":[[[5.95781,46.12925],[5.97893,46.13303],[5.9871,46.14499],[6.01791,46.14228],[6.03614,46.13712],[6.04564,46.14031],[6.05203,46.15191],[6.07491,46.14879],[6.09199,46.15191],[6.09926,46.14373],[6.13397,46.1406],[6.15305,46.15194],[6.18116,46.16187],[6.18871,46.16644],[6.18707,46.17999],[6.19552,46.18401],[6.19807,46.18369],[6.20539,46.19163],[6.21114,46.1927],[6.21273,46.19409],[6.21603,46.19507],[6.21844,46.19837],[6.22222,46.19888],[6.22175,46.20045],[6.23544,46.20714],[6.23913,46.20511],[6.24821,46.20531],[6.26007,46.21165],[6.27694,46.21566],[6.29663,46.22688],[6.31041,46.24417],[6.29474,46.26221],[6.26749,46.24745],[6.24952,46.26255],[6.23775,46.27822],[6.25137,46.29014],[6.24826,46.30175],[6.21981,46.31304],[6.25432,46.3632],[6.53358,46.45431],[6.82312,46.42661],[6.8024,46.39171],[6.77152,46.34784],[6.86052,46.28512],[6.78968,46.14058],[6.89321,46.12548],[6.87868,46.03855],[6.93862,46.06502],[7.00946,45.9944],[7.04151,45.92435],[7.10685,45.85653],[7.56343,45.97421],[7.85949,45.91485],[7.9049,45.99945],[7.98881,45.99867],[8.02906,46.10331],[8.11383,46.11577],[8.16866,46.17817],[8.08814,46.26692],[8.31162,46.38044],[8.30648,46.41587],[8.42464,46.46367],[8.46317,46.43712],[8.45032,46.26869],[8.62242,46.12112],[8.75697,46.10395],[8.80778,46.10085],[8.85617,46.0748],[8.79414,46.00913],[8.78585,45.98973],[8.79362,45.99207],[8.8319,45.9879],[8.85121,45.97239],[8.86688,45.96135],[8.88904,45.95465],[8.93649,45.86775],[8.94372,45.86587],[8.93504,45.86245],[8.91129,45.8388],[8.94737,45.84285],[8.9621,45.83707],[8.99663,45.83466],[9.00324,45.82055],[9.0298,45.82127],[9.03279,45.82865],[9.03793,45.83548],[9.03505,45.83976],[9.04059,45.8464],[9.04546,45.84968],[9.06642,45.8761],[9.09065,45.89906],[8.99257,45.9698],[9.01618,46.04928],[9.24503,46.23616],[9.29226,46.32717],[9.25502,46.43743],[9.28136,46.49685],[9.36128,46.5081],[9.40487,46.46621],[9.45936,46.50873],[9.46117,46.37481],[9.57015,46.2958],[9.71273,46.29266],[9.73086,46.35071],[9.95249,46.38045],[10.07055,46.21668],[10.14439,46.22992],[10.17862,46.25626],[10.10506,46.3372],[10.165,46.41051],[10.03715,46.44479],[10.10307,46.61003],[10.23674,46.63484],[10.25309,46.57432],[10.46136,46.53164],[10.49375,46.62049],[10.44686,46.64162],[10.40475,46.63671],[10.38659,46.67847],[10.47197,46.85698],[10.48376,46.93891],[10.36933,47.00212],[10.30031,46.92093],[10.24128,46.93147],[10.22675,46.86942],[10.10715,46.84296],[9.98058,46.91434],[9.88266,46.93343],[9.87935,47.01337],[9.60717,47.06091],[9.55721,47.04762],[9.54041,47.06495],[9.47548,47.05257],[9.47139,47.06402],[9.51362,47.08505],[9.52089,47.10019],[9.51044,47.13727],[9.48774,47.17402],[9.4891,47.19346],[9.50318,47.22153],[9.52406,47.24959],[9.53116,47.27029],[9.54773,47.2809],[9.55857,47.29919],[9.58513,47.31334],[9.59978,47.34671],[9.62476,47.36639],[9.65427,47.36824],[9.66243,47.37136],[9.6711,47.37824],[9.67445,47.38429],[9.67334,47.39191],[9.6629,47.39591],[9.65136,47.40504],[9.65043,47.41937],[9.6446,47.43233],[9.64483,47.43842],[9.65863,47.44847],[9.65728,47.45383],[9.6423,47.45599],[9.62475,47.45685],[9.62158,47.45858],[9.60841,47.47178],[9.60484,47.46358],[9.60205,47.46165],[9.59482,47.46305],[9.58208,47.48344],[9.56312,47.49495],[9.55125,47.53629],[9.25619,47.65939],[9.18203,47.65598],[9.17593,47.65399],[9.1755,47.65584],[9.1705,47.65513],[9.15181,47.66904],[9.13845,47.66389],[9.09891,47.67801],[9.02093,47.6868],[8.94093,47.65596],[8.89946,47.64769],[8.87625,47.65441],[8.87383,47.67045],[8.85065,47.68209],[8.86989,47.70504],[8.82002,47.71458],[8.80663,47.73821],[8.77309,47.72059],[8.76965,47.7075],[8.79966,47.70222],[8.79511,47.67462],[8.75856,47.68969],[8.72809,47.69282],[8.72617,47.69651],[8.73671,47.7169],[8.70543,47.73121],[8.74251,47.75168],[8.71778,47.76571],[8.68985,47.75686],[8.68022,47.78599],[8.65292,47.80066],[8.64425,47.76398],[8.62408,47.7626],[8.61657,47.79998],[8.56415,47.80633],[8.56814,47.78001],[8.48868,47.77215],[8.45771,47.7493],[8.44807,47.72426],[8.40569,47.69855],[8.4211,47.68407],[8.40473,47.67499],[8.41346,47.66676],[8.42264,47.66667],[8.44711,47.65379],[8.4667,47.65747],[8.46605,47.64103],[8.49656,47.64709],[8.5322,47.64687],[8.52801,47.66059],[8.56141,47.67088],[8.57683,47.66158],[8.6052,47.67258],[8.61113,47.66332],[8.62884,47.65098],[8.62049,47.63757],[8.60412,47.63735],[8.61471,47.64514],[8.60701,47.65271],[8.59545,47.64298],[8.60348,47.61204],[8.57586,47.59537],[8.55756,47.62394],[8.51686,47.63476],[8.50747,47.61897],[8.45578,47.60121],[8.46637,47.58389],[8.48949,47.588],[8.49431,47.58107],[8.43235,47.56617],[8.39477,47.57826],[8.38273,47.56608],[8.32735,47.57133],[8.30277,47.58607],[8.29524,47.5919],[8.29722,47.60603],[8.2824,47.61225],[8.26313,47.6103],[8.25863,47.61571],[8.23809,47.61204],[8.22577,47.60385],[8.22011,47.6181],[8.20617,47.62141],[8.19378,47.61636],[8.1652,47.5945],[8.14947,47.59558],[8.13823,47.59147],[8.13662,47.58432],[8.11543,47.5841],[8.10395,47.57918],[8.10002,47.56504],[8.08557,47.55768],[8.06663,47.56374],[8.04383,47.55443],[8.02136,47.55096],[8.00113,47.55616],[7.97581,47.55493],[7.95682,47.55789],[7.94614,47.5436],[7.932644,47.54704],[7.920585,47.5469],[7.91251,47.55031],[7.906809,47.56037],[7.911615,47.56686],[7.90673,47.57674],[7.898354,47.58408],[7.88664,47.58854],[7.862692,47.58808],[7.84167,47.58196],[7.833466,47.58663],[7.81901,47.58798],[7.811279,47.56946],[7.80098,47.56335],[7.79486,47.55691],[7.75261,47.54599],[7.71961,47.54219],[7.69642,47.53297],[7.68101,47.53232],[7.6656,47.53752],[7.66174,47.54554],[7.65083,47.54662],[7.63338,47.56256],[7.67655,47.56435],[7.68904,47.57133],[7.67115,47.5871],[7.68486,47.59601],[7.69385,47.60099],[7.68229,47.59905],[7.67395,47.59212],[7.64599,47.59695],[7.64213,47.5944],[7.64309,47.59151],[7.61929,47.57683],[7.60459,47.57869],[7.60523,47.58519],[7.58945,47.59017],[7.58386,47.57536],[7.56684,47.57785],[7.56548,47.57617],[7.55689,47.57232],[7.55652,47.56779],[7.53634,47.55553],[7.52831,47.55347],[7.51723,47.54578],[7.50873,47.54546],[7.49691,47.53821],[7.50588,47.52856],[7.51904,47.53515],[7.53199,47.5284],[7.5229,47.51644],[7.49804,47.51798],[7.51076,47.49651],[7.47534,47.47932],[7.43356,47.49712],[7.42923,47.48628],[7.4583,47.47216],[7.4462,47.46264],[7.43088,47.45846],[7.40308,47.43638],[7.35603,47.43432],[7.33526,47.44186],[7.24669,47.4205],[7.17026,47.44312],[7.19583,47.49455],[7.16249,47.49025],[7.12781,47.50371],[7.07425,47.48863],[7.0231,47.50522],[6.98425,47.49432],[7.0024,47.45264],[6.93953,47.43388],[6.93744,47.40714],[6.88542,47.37262],[6.87959,47.35335],[7.03125,47.36996],[7.0564,47.35134],[7.05305,47.33304],[6.94316,47.28747],[6.95108,47.26428],[6.9508,47.24338],[6.8489,47.15933],[6.76788,47.1208],[6.68823,47.06616],[6.71531,47.0494],[6.43341,46.92703],[6.46456,46.88865],[6.43216,46.80336],[6.45209,46.77502],[6.38351,46.73171],[6.27135,46.68251],[6.11084,46.57649],[6.1567,46.54402],[6.07269,46.46244],[6.08427,46.44305],[6.06407,46.41676],[6.09926,46.40768],[6.15016,46.3778],[6.15985,46.37721],[6.16987,46.36759],[6.15738,46.3491],[6.13876,46.33844],[6.1198,46.31157],[6.11697,46.29547],[6.1013,46.28512],[6.11926,46.2634],[6.12446,46.25059],[6.10071,46.23772],[6.08563,46.24651],[6.07072,46.24085],[6.0633,46.24583],[6.05029,46.23518],[6.04602,46.23127],[6.03342,46.2383],[6.02461,46.23313],[5.97542,46.21525],[5.96515,46.19638],[5.99573,46.18587],[5.98846,46.17046],[5.98188,46.17392],[5.97508,46.15863],[5.9641,46.14412],[5.95781,46.12925]],[[8.65769,47.68928],[8.67508,47.6979],[8.66416,47.71367],[8.70237,47.71453],[8.71773,47.69088],[8.70847,47.68904],[8.68985,47.69552],[8.66837,47.68437],[8.65769,47.68928]],[[8.95861,45.96485],[8.96668,45.98436],[8.97741,45.98317],[8.97604,45.96151],[8.95861,45.96485]]]}},{"type":"Feature","properties":{"id":"DE"},"geometry":{"type":"MultiPolygon","coordinates":[[[[5.45168,54.20039],[6.91025,53.44221],[7.00198,53.32672],[7.19052,53.31866],[7.21679,53.20058],[7.22681,53.18165],[7.17898,53.13817],[7.21694,53.00742],[7.07253,52.81083],[7.04557,52.63318],[6.77307,52.65375],[6.71641,52.62905],[6.69507,52.488],[6.94293,52.43597],[6.99041,52.47235],[7.03417,52.40237],[7.07044,52.37805],[7.02703,52.27941],[7.06365,52.23789],[7.03729,52.22695],[6.9897,52.2271],[6.97189,52.20329],[6.83984,52.11728],[6.76117,52.11895],[6.68128,52.05052],[6.83035,51.9905],[6.82357,51.96711],[6.72319,51.89518],[6.68386,51.91861],[6.58556,51.89386],[6.50231,51.86313],[6.47179,51.85395],[6.38815,51.87257],[6.40704,51.82771],[6.30593,51.84998],[6.29872,51.86801],[6.21443,51.86801],[6.15349,51.90439],[6.11551,51.89769],[6.16902,51.84094],[6.10337,51.84829],[6.06705,51.86136],[5.99848,51.83195],[5.94568,51.82786],[5.98665,51.76944],[5.95003,51.7493],[6.04091,51.71821],[6.02767,51.6742],[6.11759,51.65609],[6.09055,51.60564],[6.18017,51.54096],[6.21724,51.48568],[6.20654,51.40049],[6.22641,51.39948],[6.22674,51.36135],[6.16977,51.33169],[6.07889,51.24432],[6.07889,51.17038],[6.17384,51.19589],[6.16706,51.15677],[5.98292,51.07469],[5.9541,51.03496],[5.9134,51.06736],[5.86735,51.05182],[5.87849,51.01969],[5.90493,51.00198],[5.90296,50.97356],[5.95282,50.98728],[6.02697,50.98303],[6.01615,50.93367],[6.09297,50.92066],[6.07486,50.89307],[6.08805,50.87223],[6.07693,50.86025],[6.07431,50.84674],[6.05702,50.85179],[6.05623,50.8572],[6.01921,50.84435],[6.02328,50.81694],[6.00462,50.80065],[5.98404,50.80988],[5.97497,50.79992],[6.02624,50.77453],[6.01976,50.75398],[6.03889,50.74618],[6.0326,50.72647],[6.0406,50.71848],[6.04428,50.72861],[6.11707,50.72231],[6.17852,50.6245],[6.26957,50.62444],[6.2476,50.60392],[6.24888,50.59869],[6.24005,50.58732],[6.22581,50.5907],[6.20281,50.56952],[6.17739,50.55875],[6.17802,50.54179],[6.19735,50.53576],[6.19579,50.5313],[6.18716,50.52653],[6.19193,50.5212],[6.20599,50.52089],[6.22335,50.49578],[6.26637,50.50272],[6.30809,50.50058],[6.3465,50.48833],[6.34005,50.46083],[6.37219,50.45397],[6.36852,50.40776],[6.34406,50.37994],[6.3688,50.35898],[6.40785,50.33557],[6.40641,50.32425],[6.35701,50.31139],[6.32488,50.32333],[6.29949,50.30887],[6.28797,50.27458],[6.208,50.25179],[6.16853,50.2234],[6.18364,50.20815],[6.18739,50.1822],[6.14588,50.17106],[6.14132,50.14971],[6.15298,50.14126],[6.1379,50.12964],[6.12055,50.09171],[6.11274,50.05916],[6.13458,50.04141],[6.13044,50.02929],[6.14666,50.02207],[6.13794,50.01466],[6.13273,50.02019],[6.1295,50.01849],[6.13806,50.01056],[6.14948,50.00908],[6.14147,49.99563],[6.1701,49.98518],[6.16466,49.97086],[6.17872,49.9537],[6.18554,49.95622],[6.18045,49.96611],[6.19089,49.96991],[6.19856,49.95053],[6.22094,49.94955],[6.22608,49.929],[6.21882,49.92403],[6.22926,49.92096],[6.23496,49.89972],[6.26146,49.88203],[6.28874,49.87592],[6.29692,49.86685],[6.30963,49.87021],[6.32303,49.85133],[6.32098,49.83728],[6.33585,49.83785],[6.34267,49.84974],[6.36576,49.85032],[6.40022,49.82029],[6.42521,49.81591],[6.42905,49.81091],[6.44131,49.81443],[6.45425,49.81164],[6.47111,49.82263],[6.48718,49.81267],[6.50647,49.80916],[6.51215,49.80124],[6.52121,49.81338],[6.53122,49.80666],[6.52169,49.79787],[6.50534,49.78952],[6.51669,49.78336],[6.51056,49.77515],[6.51828,49.76855],[6.51646,49.75961],[6.50174,49.75292],[6.50193,49.73291],[6.51805,49.72425],[6.51397,49.72058],[6.50261,49.72718],[6.49535,49.72645],[6.49694,49.72205],[6.5042,49.71808],[6.50647,49.71353],[6.49785,49.71118],[6.48014,49.69767],[6.46048,49.69092],[6.44654,49.67799],[6.42937,49.66857],[6.42726,49.66078],[6.43768,49.66021],[6.4413,49.65722],[6.41861,49.61723],[6.39822,49.60081],[6.385,49.59946],[6.37464,49.58886],[6.38342,49.5799],[6.38024,49.57593],[6.36676,49.57813],[6.35825,49.57053],[6.38228,49.55855],[6.38072,49.55171],[6.35666,49.52931],[6.36788,49.50377],[6.36907,49.48931],[6.36778,49.46937],[6.38352,49.46463],[6.39168,49.4667],[6.40274,49.46546],[6.42432,49.47683],[6.55404,49.42464],[6.533,49.40748],[6.60091,49.36864],[6.58807,49.35358],[6.572,49.35027],[6.60186,49.31055],[6.66583,49.28065],[6.69274,49.21661],[6.71843,49.2208],[6.73256,49.20486],[6.71137,49.18808],[6.73765,49.16375],[6.78265,49.16793],[6.83385,49.15162],[6.84703,49.15734],[6.86225,49.18185],[6.85016,49.19354],[6.85119,49.20038],[6.83555,49.21249],[6.85939,49.22376],[6.89298,49.20863],[6.91875,49.22261],[6.93831,49.2223],[6.94028,49.21641],[6.95963,49.203],[6.97273,49.2099],[7.01318,49.19018],[7.03459,49.19096],[7.0274,49.17042],[7.03178,49.15734],[7.04662,49.13724],[7.04409,49.12123],[7.04843,49.11422],[7.05548,49.11185],[7.06642,49.11415],[7.07162,49.1255],[7.09007,49.13094],[7.07859,49.15031],[7.10715,49.15631],[7.10384,49.13787],[7.12504,49.14253],[7.1358,49.1282],[7.1593,49.1204],[7.23473,49.12971],[7.29514,49.11426],[7.3195,49.14231],[7.35995,49.14399],[7.3662,49.17308],[7.44052,49.18354],[7.44455,49.16765],[7.49473,49.17],[7.49172,49.13915],[7.53012,49.09818],[7.56416,49.08136],[7.62575,49.07654],[7.63618,49.05428],[7.75948,49.04562],[7.79557,49.06583],[7.86386,49.03499],[7.93641,49.05544],[7.97783,49.03161],[8.14189,48.97833],[8.22604,48.97352],[8.20031,48.95856],[8.19989,48.95825],[8.12813,48.87985],[8.10253,48.81829],[8.06802,48.78957],[8.0326,48.79017],[8.01534,48.76085],[7.96994,48.75606],[7.96812,48.72491],[7.89002,48.66317],[7.84098,48.64217],[7.80057,48.5857],[7.80167,48.54758],[7.80647,48.51239],[7.76833,48.48945],[7.73109,48.39192],[7.74562,48.32736],[7.69022,48.30018],[7.6648,48.22219],[7.57137,48.12292],[7.56966,48.03265],[7.62302,47.97898],[7.55673,47.87371],[7.52921,47.77747],[7.54761,47.72912],[7.53722,47.71635],[7.51266,47.70197],[7.51915,47.68335],[7.52067,47.66437],[7.53384,47.65115],[7.5591,47.63849],[7.57423,47.61628],[7.58851,47.60794],[7.59301,47.60058],[7.58945,47.59017],[7.60523,47.58519],[7.60459,47.57869],[7.61929,47.57683],[7.64309,47.59151],[7.64213,47.5944],[7.64599,47.59695],[7.67395,47.59212],[7.68229,47.59905],[7.69385,47.60099],[7.68486,47.59601],[7.67115,47.5871],[7.68904,47.57133],[7.67655,47.56435],[7.63338,47.56256],[7.65083,47.54662],[7.66174,47.54554],[7.6656,47.53752],[7.68101,47.53232],[7.69642,47.53297],[7.71961,47.54219],[7.75261,47.54599],[7.79486,47.55691],[7.80098,47.56335],[7.811279,47.56946],[7.81901,47.58798],[7.833466,47.58663],[7.84167,47.58196],[7.862692,47.58808],[7.88664,47.58854],[7.898354,47.58408],[7.90673,47.57674],[7.911615,47.56686],[7.906809,47.56037],[7.91251,47.55031],[7.920585,47.5469],[7.932644,47.54704],[7.94614,47.5436],[7.95682,47.55789],[7.97581,47.55493],[8.00113,47.55616],[8.02136,47.55096],[8.04383,47.55443],[8.06663,47.56374],[8.08557,47.55768],[8.10002,47.56504],[8.10395,47.57918],[8.11543,47.5841],[8.13662,47.58432],[8.13823,47.59147],[8.14947,47.59558],[8.1652,47.5945],[8.19378,47.61636],[8.20617,47.62141],[8.22011,47.6181],[8.22577,47.60385],[8.23809,47.61204],[8.25863,47.61571],[8.26313,47.6103],[8.2824,47.61225],[8.29722,47.60603],[8.29524,47.5919],[8.30277,47.58607],[8.32735,47.57133],[8.35512,47.57014],[8.38273,47.56608],[8.39477,47.57826],[8.43235,47.56617],[8.49431,47.58107],[8.48949,47.588],[8.46637,47.58389],[8.45578,47.60121],[8.50747,47.61897],[8.51686,47.63476],[8.55756,47.62394],[8.57586,47.59537],[8.60348,47.61204],[8.59545,47.64298],[8.60701,47.65271],[8.61471,47.64514],[8.60412,47.63735],[8.62049,47.63757],[8.62884,47.65098],[8.61113,47.66332],[8.6052,47.67258],[8.57683,47.66158],[8.56141,47.67088],[8.52801,47.66059],[8.5322,47.64687],[8.49656,47.64709],[8.46605,47.64103],[8.4667,47.65747],[8.44711,47.65379],[8.42264,47.66667],[8.41346,47.66676],[8.40473,47.67499],[8.4211,47.68407],[8.40569,47.69855],[8.44807,47.72426],[8.45771,47.7493],[8.48868,47.77215],[8.56814,47.78001],[8.56415,47.80633],[8.61657,47.79998],[8.62408,47.7626],[8.64425,47.76398],[8.65292,47.80066],[8.68022,47.78599],[8.68985,47.75686],[8.71778,47.76571],[8.74251,47.75168],[8.70543,47.73121],[8.73671,47.7169],[8.72617,47.69651],[8.72809,47.69282],[8.75856,47.68969],[8.79511,47.67462],[8.79966,47.70222],[8.76965,47.7075],[8.77309,47.72059],[8.80663,47.73821],[8.82002,47.71458],[8.86989,47.70504],[8.85065,47.68209],[8.87383,47.67045],[8.87625,47.65441],[8.89946,47.64769],[8.94093,47.65596],[9.02093,47.6868],[9.09891,47.67801],[9.13845,47.66389],[9.15181,47.66904],[9.1705,47.65513],[9.1755,47.65584],[9.17593,47.65399],[9.18203,47.65598],[9.25619,47.65939],[9.55125,47.53629],[9.72736,47.53457],[9.76748,47.5934],[9.80254,47.59419],[9.82591,47.58158],[9.8189,47.54688],[9.87499,47.52953],[9.87733,47.54688],[9.92407,47.53111],[9.96029,47.53899],[10.00003,47.48216],[10.03859,47.48927],[10.07131,47.45531],[10.09001,47.46005],[10.1052,47.4316],[10.06897,47.40709],[10.09819,47.35724],[10.11805,47.37228],[10.16362,47.36674],[10.17648,47.38889],[10.2127,47.38019],[10.22774,47.38904],[10.23757,47.37609],[10.19998,47.32832],[10.2147,47.31014],[10.17648,47.29149],[10.17531,47.27167],[10.23257,47.27088],[10.33424,47.30813],[10.39851,47.37623],[10.4324,47.38494],[10.4359,47.41183],[10.47446,47.43318],[10.46278,47.47901],[10.44291,47.48453],[10.4324,47.50111],[10.44992,47.5524],[10.43473,47.58394],[10.47329,47.58552],[10.48849,47.54057],[10.56912,47.53584],[10.60337,47.56755],[10.63456,47.5591],[10.68832,47.55752],[10.6965,47.54253],[10.7596,47.53228],[10.77596,47.51729],[10.88814,47.53701],[10.91268,47.51334],[10.86945,47.5015],[10.87061,47.4786],[10.90918,47.48571],[10.93839,47.48018],[10.92437,47.46991],[10.98513,47.42882],[10.97111,47.41617],[10.97111,47.39561],[11.11835,47.39719],[11.12536,47.41222],[11.20482,47.43198],[11.25157,47.43277],[11.22002,47.3964],[11.27844,47.39956],[11.29597,47.42566],[11.33804,47.44937],[11.4175,47.44621],[11.38128,47.47465],[11.4362,47.51413],[11.52618,47.50939],[11.58578,47.52281],[11.58811,47.55515],[11.60681,47.57881],[11.63934,47.59202],[11.84052,47.58354],[11.85572,47.60166],[12.0088,47.62451],[12.02282,47.61033],[12.05788,47.61742],[12.13734,47.60639],[12.17824,47.61506],[12.18145,47.61019],[12.17737,47.60121],[12.18568,47.6049],[12.20398,47.60667],[12.20801,47.61082],[12.19895,47.64085],[12.18507,47.65984],[12.18347,47.66663],[12.16769,47.68167],[12.16217,47.70105],[12.18303,47.70065],[12.22571,47.71776],[12.2542,47.7433],[12.26238,47.73544],[12.24017,47.69534],[12.26004,47.67725],[12.27991,47.68827],[12.336,47.69534],[12.37222,47.68433],[12.43883,47.6977],[12.44117,47.6741],[12.50076,47.62293],[12.53816,47.63553],[12.57438,47.63238],[12.6071,47.6741],[12.7357,47.6787],[12.77777,47.66689],[12.76492,47.64485],[12.82101,47.61493],[12.77427,47.58025],[12.80699,47.54477],[12.84672,47.54556],[12.85256,47.52741],[12.9624,47.47452],[12.98344,47.48716],[12.9998,47.46267],[13.04537,47.49426],[13.03252,47.53373],[13.05355,47.56291],[13.04537,47.58183],[13.06641,47.58577],[13.06407,47.60075],[13.09562,47.63304],[13.07692,47.68814],[13.01382,47.72116],[12.98578,47.7078],[12.92969,47.71094],[12.91333,47.7178],[12.90274,47.72513],[12.91711,47.74026],[12.9353,47.74788],[12.94371,47.76281],[12.93202,47.77302],[12.96311,47.79957],[12.98543,47.82896],[13.00588,47.84374],[12.94163,47.92927],[12.93886,47.94046],[12.93642,47.94436],[12.93419,47.94063],[12.92668,47.93879],[12.91985,47.94069],[12.9211,47.95135],[12.91683,47.95647],[12.87476,47.96195],[12.8549,48.01122],[12.76141,48.07373],[12.74973,48.10885],[12.7617,48.12796],[12.78595,48.12445],[12.80676,48.14979],[12.82673,48.15245],[12.8362,48.15876],[12.836,48.1647],[12.84475,48.16556],[12.87126,48.20318],[12.95306,48.20629],[13.02083,48.25689],[13.0851,48.27711],[13.126,48.27867],[13.18093,48.29577],[13.26039,48.29422],[13.30897,48.31575],[13.40709,48.37292],[13.43929,48.43386],[13.42527,48.45711],[13.45727,48.51092],[13.43695,48.55776],[13.45214,48.56472],[13.46967,48.55157],[13.50663,48.57506],[13.50131,48.58091],[13.51291,48.59023],[13.57535,48.55912],[13.59705,48.57013],[13.62508,48.55501],[13.65186,48.55092],[13.66113,48.53558],[13.72802,48.51208],[13.74816,48.53058],[13.7513,48.5624],[13.76921,48.55324],[13.80519,48.58026],[13.80038,48.59487],[13.82609,48.62345],[13.81901,48.6761],[13.81283,48.68426],[13.81791,48.69832],[13.79337,48.71375],[13.81863,48.73257],[13.82266,48.75544],[13.84023,48.76988],[13.8096,48.77877],[13.78977,48.83319],[13.76994,48.83537],[13.73854,48.88538],[13.67739,48.87886],[13.61624,48.9462],[13.58319,48.96899],[13.50552,48.97441],[13.50221,48.93752],[13.40802,48.98851],[13.39479,49.04812],[13.28242,49.1228],[13.23689,49.11412],[13.20405,49.12303],[13.17019,49.14339],[13.17665,49.16713],[13.05883,49.26259],[13.02957,49.27399],[13.03618,49.30417],[12.94859,49.34079],[12.88249,49.35479],[12.88414,49.33541],[12.78168,49.34618],[12.75854,49.3989],[12.71227,49.42363],[12.669,49.42935],[12.64121,49.47628],[12.64782,49.52565],[12.60155,49.52887],[12.56188,49.6146],[12.53544,49.61888],[12.52553,49.68415],[12.4462,49.70233],[12.40489,49.76321],[12.46603,49.78882],[12.48256,49.83575],[12.55197,49.92094],[12.47264,49.94222],[12.49908,49.97305],[12.30798,50.05719],[12.26111,50.06331],[12.27433,50.0771],[12.23709,50.10213],[12.2073,50.10315],[12.1917,50.13434],[12.21484,50.16399],[12.19335,50.19997],[12.09287,50.25032],[12.13716,50.27396],[12.10907,50.32041],[12.18013,50.32146],[12.20823,50.2729],[12.25119,50.27079],[12.26953,50.25189],[12.24791,50.25525],[12.23943,50.24594],[12.28755,50.22429],[12.28063,50.19544],[12.29232,50.17524],[12.32596,50.17146],[12.33847,50.19432],[12.32445,50.20442],[12.33263,50.24367],[12.35425,50.23993],[12.36594,50.28289],[12.40158,50.29521],[12.39924,50.32302],[12.43371,50.32506],[12.43722,50.33774],[12.46643,50.35527],[12.48256,50.34784],[12.49214,50.35228],[12.48747,50.37278],[12.51356,50.39694],[12.67261,50.41949],[12.70731,50.39948],[12.73044,50.42268],[12.73476,50.43237],[12.82465,50.45738],[12.94058,50.40944],[12.98433,50.42016],[13.02147,50.44763],[13.02038,50.4734],[13.0312,50.50944],[13.08301,50.50132],[13.13424,50.51709],[13.19043,50.50237],[13.25158,50.59268],[13.29454,50.57904],[13.32594,50.58009],[13.32264,50.60317],[13.37805,50.627],[13.37485,50.64931],[13.42189,50.61243],[13.46413,50.60102],[13.49742,50.63133],[13.5226,50.64721],[13.53748,50.67654],[13.52474,50.70394],[13.65977,50.73096],[13.70204,50.71771],[13.76316,50.73487],[13.82942,50.7251],[13.89444,50.74142],[13.89113,50.78533],[13.98864,50.8177],[14.02982,50.80662],[14.22331,50.86049],[14.24314,50.88761],[14.27123,50.89386],[14.30098,50.88448],[14.38691,50.89907],[14.39848,50.93866],[14.31422,50.95243],[14.30251,50.96606],[14.32793,50.97379],[14.32353,50.98556],[14.28776,50.97718],[14.25665,50.98935],[14.30098,51.05515],[14.41335,51.02086],[14.45827,51.03712],[14.49202,51.02286],[14.49154,51.04382],[14.49991,51.04692],[14.50809,51.0427],[14.49873,51.02242],[14.53321,51.01679],[14.53438,51.00374],[14.56432,51.01008],[14.58215,50.99306],[14.59908,50.98685],[14.59702,50.96148],[14.56374,50.922],[14.58024,50.91443],[14.64802,50.93241],[14.65259,50.90513],[14.63434,50.8883],[14.61993,50.86049],[14.70661,50.84096],[14.79139,50.81438],[14.82803,50.86966],[14.81664,50.88148],[14.89681,50.9422],[14.89252,50.94999],[14.92942,50.99744],[14.95529,51.04552],[14.97938,51.07742],[14.98229,51.11354],[14.99689,51.12205],[14.99079,51.14284],[14.99646,51.14365],[15.00083,51.14974],[14.99414,51.15813],[14.99311,51.16249],[15.0047,51.16874],[15.01242,51.21285],[15.04288,51.28387],[14.98008,51.33449],[14.96899,51.38367],[14.9652,51.44793],[14.94749,51.47155],[14.73219,51.52922],[14.72652,51.53902],[14.73047,51.54606],[14.71125,51.56209],[14.7727,51.61263],[14.75759,51.62318],[14.75392,51.67445],[14.69065,51.70842],[14.66386,51.73282],[14.64625,51.79472],[14.60493,51.80473],[14.59089,51.83302],[14.6588,51.88359],[14.6933,51.9044],[14.70601,51.92944],[14.7177,51.94048],[14.72163,51.95188],[14.71836,51.95606],[14.7139,51.95643],[14.70488,51.97679],[14.71339,52.00337],[14.76026,52.06624],[14.72971,52.09167],[14.6917,52.10283],[14.67683,52.13936],[14.70616,52.16927],[14.68344,52.19612],[14.71319,52.22144],[14.70139,52.25038],[14.58149,52.28007],[14.56378,52.33838],[14.55228,52.35264],[14.54423,52.42568],[14.63056,52.48993],[14.60081,52.53116],[14.6289,52.57136],[14.61073,52.59847],[14.22071,52.81175],[14.13806,52.82392],[14.12256,52.84311],[14.15873,52.87715],[14.14056,52.95786],[14.25954,53.00264],[14.35044,53.05829],[14.38679,53.13669],[14.36696,53.16444],[14.37853,53.20405],[14.40662,53.21098],[14.45125,53.26241],[14.44133,53.27427],[14.4215,53.27724],[14.35209,53.49506],[14.3273,53.50587],[14.30416,53.55499],[14.31904,53.61581],[14.2853,53.63392],[14.28477,53.65955],[14.27133,53.66613],[14.2836,53.67721],[14.26782,53.69866],[14.27249,53.74464],[14.21323,53.8664],[14.20823,53.90776],[14.18544,53.91258],[14.20647,53.91671],[14.22634,53.9291],[14.20811,54.12784],[13.93395,54.84044],[12.85844,54.82438],[11.90309,54.38543],[11.00303,54.63689],[10.31111,54.65968],[10.16755,54.73883],[9.89314,54.84171],[9.73563,54.8247],[9.61187,54.85548],[9.62734,54.88057],[9.58937,54.88785],[9.4659,54.83131],[9.43155,54.82586],[9.41213,54.84254],[9.38532,54.83968],[9.36496,54.81749],[9.33849,54.80233],[9.32771,54.80602],[9.2474,54.8112],[9.23445,54.83432],[9.24631,54.84726],[9.20571,54.85841],[9.14275,54.87421],[9.04629,54.87249],[8.92795,54.90452],[8.81178,54.90518],[8.76387,54.8948],[8.63979,54.91069],[8.55769,54.91837],[8.45719,55.06747],[8.02459,55.09613],[5.45168,54.20039]]],[[[8.65769,47.68928],[8.66837,47.68437],[8.68985,47.69552],[8.70847,47.68904],[8.71773,47.69088],[8.70237,47.71453],[8.66416,47.71367],[8.67508,47.6979],[8.65769,47.68928]]]]}},{"type":"Feature","properties":{"id":"BE"},"geometry":{"type":"MultiPolygon","coordinates":[[[[2.18458,51.52087],[2.55904,51.07014],[2.57551,51.00326],[2.63074,50.94746],[2.59093,50.91751],[2.63331,50.81457],[2.71165,50.81295],[2.81056,50.71773],[2.8483,50.72276],[2.86985,50.7033],[2.87937,50.70298],[2.88504,50.70656],[2.90069,50.69263],[2.91036,50.6939],[2.90873,50.702],[2.95019,50.75138],[2.96778,50.75242],[3.00537,50.76588],[3.04314,50.77674],[3.09163,50.77717],[3.10614,50.78303],[3.11206,50.79416],[3.11987,50.79188],[3.1257,50.78603],[3.15017,50.79031],[3.16476,50.76843],[3.18339,50.74981],[3.18811,50.74025],[3.20064,50.73547],[3.19017,50.72569],[3.20845,50.71662],[3.22042,50.71019],[3.24593,50.71389],[3.26063,50.70086],[3.26141,50.69151],[3.2536,50.68977],[3.264,50.67668],[3.23951,50.6585],[3.2729,50.60718],[3.28575,50.52724],[3.37693,50.49538],[3.44629,50.51009],[3.47385,50.53397],[3.51564,50.5256],[3.49509,50.48885],[3.5683,50.50192],[3.58361,50.49049],[3.61014,50.49568],[3.64426,50.46275],[3.66153,50.45165],[3.67494,50.40239],[3.67262,50.38663],[3.65709,50.36873],[3.66976,50.34563],[3.71009,50.30305],[3.70987,50.3191],[3.73911,50.34809],[3.84314,50.35219],[3.90781,50.32814],[3.96771,50.34989],[4.0268,50.35793],[4.0689,50.3254],[4.10237,50.31247],[4.10957,50.30234],[4.11954,50.30425],[4.13665,50.25609],[4.16808,50.25786],[4.15524,50.2833],[4.17347,50.28838],[4.17861,50.27443],[4.20651,50.27333],[4.21945,50.25539],[4.15524,50.21103],[4.16014,50.19239],[4.13561,50.13078],[4.20147,50.13535],[4.23101,50.06945],[4.16294,50.04719],[4.13508,50.01976],[4.14239,49.98034],[4.20532,49.95803],[4.31963,49.97043],[4.35051,49.95315],[4.43488,49.94122],[4.51098,49.94659],[4.5414,49.96911],[4.68695,49.99685],[4.70064,50.09384],[4.75237,50.11314],[4.82438,50.16878],[4.83279,50.15331],[4.88602,50.15182],[4.8382,50.06738],[4.78827,49.95609],[4.88529,49.9236],[4.85134,49.86457],[4.86965,49.82271],[4.85464,49.78995],[4.96714,49.79872],[5.09249,49.76193],[5.14545,49.70287],[5.26232,49.69456],[5.31465,49.66846],[5.33039,49.6555],[5.30214,49.63055],[5.3137,49.61225],[5.33851,49.61599],[5.34837,49.62889],[5.3974,49.61596],[5.43713,49.5707],[5.46734,49.52648],[5.46541,49.49825],[5.55001,49.52729],[5.60909,49.51228],[5.64505,49.55146],[5.75649,49.54321],[5.7577,49.55915],[5.77435,49.56298],[5.79195,49.55228],[5.81838,49.54777],[5.84143,49.5533],[5.84692,49.55663],[5.8424,49.56082],[5.87256,49.57539],[5.86986,49.58756],[5.84971,49.58674],[5.84826,49.5969],[5.8762,49.60898],[5.87609,49.62047],[5.88393,49.62802],[5.88552,49.63507],[5.90599,49.63853],[5.90164,49.6511],[5.9069,49.66377],[5.86175,49.67862],[5.86527,49.69291],[5.88677,49.70951],[5.86503,49.72739],[5.84193,49.72161],[5.82562,49.72395],[5.83149,49.74729],[5.82245,49.75048],[5.78871,49.7962],[5.75409,49.79239],[5.74953,49.81428],[5.74364,49.82058],[5.74844,49.82435],[5.7404,49.83452],[5.74076,49.83823],[5.74975,49.83933],[5.74953,49.84709],[5.75884,49.84811],[5.74567,49.85368],[5.75861,49.85631],[5.75269,49.8711],[5.78415,49.87922],[5.73621,49.89796],[5.77314,49.93646],[5.77291,49.96056],[5.80833,49.96451],[5.81163,49.97142],[5.83467,49.97823],[5.83968,49.9892],[5.82331,49.99662],[5.81866,50.01286],[5.8551,50.02683],[5.86904,50.04614],[5.85474,50.06342],[5.8857,50.07824],[5.89488,50.11476],[5.95929,50.13295],[5.96453,50.17259],[6.02488,50.18283],[6.03093,50.16362],[6.06406,50.15344],[6.08577,50.17246],[6.12028,50.16374],[6.1137,50.13668],[6.1379,50.12964],[6.15298,50.14126],[6.14132,50.14971],[6.14588,50.17106],[6.18739,50.1822],[6.18364,50.20815],[6.16853,50.2234],[6.208,50.25179],[6.28797,50.27458],[6.29949,50.30887],[6.32488,50.32333],[6.35701,50.31139],[6.40641,50.32425],[6.40785,50.33557],[6.3688,50.35898],[6.34406,50.37994],[6.36852,50.40776],[6.37219,50.45397],[6.34005,50.46083],[6.3465,50.48833],[6.30809,50.50058],[6.26637,50.50272],[6.22335,50.49578],[6.20599,50.52089],[6.19193,50.5212],[6.18716,50.52653],[6.19579,50.5313],[6.19735,50.53576],[6.17802,50.54179],[6.17739,50.55875],[6.20281,50.56952],[6.22581,50.5907],[6.24005,50.58732],[6.24888,50.59869],[6.2476,50.60392],[6.26957,50.62444],[6.17852,50.6245],[6.11707,50.72231],[6.04428,50.72861],[6.0406,50.71848],[6.0326,50.72647],[6.03889,50.74618],[6.01976,50.75398],[5.97545,50.75441],[5.95942,50.7622],[5.89132,50.75124],[5.89129,50.75125],[5.88734,50.77092],[5.84888,50.75448],[5.84548,50.76542],[5.80673,50.7558],[5.77513,50.78308],[5.76533,50.78159],[5.74356,50.7691],[5.73904,50.75674],[5.72216,50.76398],[5.69469,50.75529],[5.68091,50.75804],[5.70107,50.7827],[5.68995,50.79641],[5.70118,50.80764],[5.65259,50.82309],[5.64009,50.84742],[5.64504,50.87107],[5.67886,50.88142],[5.69858,50.91046],[5.71626,50.90796],[5.72644,50.91167],[5.72545,50.92312],[5.74644,50.94723],[5.75927,50.95601],[5.74752,50.96202],[5.72875,50.95428],[5.71864,50.96092],[5.76242,50.99703],[5.77688,51.02483],[5.75961,51.03113],[5.77258,51.06196],[5.79835,51.05834],[5.79903,51.09371],[5.82921,51.09328],[5.83226,51.10585],[5.8109,51.10861],[5.80798,51.11661],[5.85508,51.14445],[5.82564,51.16753],[5.77697,51.1522],[5.77735,51.17845],[5.74617,51.18928],[5.70344,51.1829],[5.65528,51.18736],[5.65145,51.19788],[5.5603,51.22249],[5.5569,51.26544],[5.515,51.29462],[5.48476,51.30053],[5.46519,51.2849],[5.4407,51.28169],[5.41672,51.26248],[5.347,51.27502],[5.33886,51.26314],[5.29716,51.26104],[5.26461,51.26693],[5.23814,51.26064],[5.22542,51.26888],[5.24244,51.30495],[5.2002,51.32243],[5.16222,51.31035],[5.13377,51.31592],[5.13105,51.34791],[5.07102,51.39469],[5.10456,51.43163],[5.07891,51.4715],[5.04774,51.47022],[5.03281,51.48679],[5.0106,51.47167],[5.00393,51.44406],[4.92152,51.39487],[4.90016,51.41404],[4.84988,51.41502],[4.78941,51.41102],[4.77229,51.41337],[4.76577,51.43046],[4.78314,51.43319],[4.82946,51.4213],[4.82409,51.44736],[4.84139,51.4799],[4.78803,51.50284],[4.77321,51.50529],[4.74578,51.48937],[4.72935,51.48424],[4.65442,51.42352],[4.57489,51.4324],[4.53521,51.4243],[4.52846,51.45002],[4.54675,51.47265],[4.5388,51.48184],[4.47736,51.4778],[4.38122,51.44905],[4.39747,51.43316],[4.38064,51.41965],[4.43777,51.36989],[4.39292,51.35547],[4.34086,51.35738],[4.33265,51.37687],[4.21923,51.37443],[4.24024,51.35371],[4.16721,51.29348],[4.05165,51.24171],[4.01957,51.24504],[3.97889,51.22537],[3.90125,51.20371],[3.78783,51.2151],[3.78999,51.25766],[3.58939,51.30064],[3.51502,51.28697],[3.52698,51.2458],[3.43488,51.24135],[3.41704,51.25933],[3.38289,51.27331],[3.35847,51.31572],[3.38696,51.33436],[3.36263,51.37112],[2.56575,51.85301],[2.18458,51.52087]]],[[[4.91493,51.4353],[4.92652,51.43329],[4.92952,51.42984],[4.93986,51.43064],[4.94265,51.44003],[4.93471,51.43861],[4.93416,51.44185],[4.94025,51.44193],[4.93544,51.44634],[4.92879,51.44161],[4.92815,51.43856],[4.92566,51.44273],[4.92811,51.4437],[4.92287,51.44741],[4.91811,51.44621],[4.92227,51.44252],[4.91935,51.43634],[4.91493,51.4353]]],[[[4.93295,51.44945],[4.93909,51.44632],[4.9524,51.45014],[4.95244,51.45207],[4.93295,51.44945]]]]}},{"type":"Feature","properties":{"id":"AU-NSW"},"geometry":{"type":"Polygon","coordinates":[[[140.99934,-28.99903],[141.00268,-34.02172],[141.5377,-34.18902],[141.71349,-34.0924],[142.0211,-34.12651],[142.22023,-34.18334],[142.24495,-34.3014],[142.37404,-34.34563],[142.50999,-34.74267],[142.61711,-34.77765],[142.76268,-34.56871],[143.34221,-34.79344],[143.3271,-34.99618],[143.39027,-35.18047],[143.57155,-35.20741],[143.56743,-35.33634],[144.08241,-35.57238],[144.74022,-36.11895],[144.94896,-36.05236],[144.9778,-35.86673],[145.1165,-35.81774],[145.34447,-35.86005],[145.50789,-35.80772],[145.80589,-35.98461],[146.36894,-36.03571],[146.42387,-35.96794],[146.59966,-35.97349],[146.85097,-36.08788],[147.04185,-36.09898],[147.10503,-36.00683],[147.31926,-36.05458],[147.39616,-35.94681],[147.71202,-35.94237],[147.99217,-36.0457],[148.04573,-36.39248],[148.20366,-36.59782],[148.10753,-36.79272],[148.19405,-36.79602],[150.19768,-37.59458],[152.57175,-39.16128],[155.3142,-27.34698],[153.55096,-28.16364],[153.53414,-28.17635],[153.47715,-28.15789],[153.36385,-28.242],[153.18121,-28.25289],[153.10293,-28.35445],[152.87222,-28.30852],[152.74725,-28.35929],[152.60442,-28.27466],[152.57696,-28.3327],[152.49731,-28.25168],[151.94662,-28.54282],[152.06472,-28.69351],[152.01391,-28.89449],[151.7777,-28.9606],[151.72552,-28.86683],[151.55248,-28.94858],[151.39318,-29.17186],[151.30392,-29.15627],[151.27508,-28.94017],[150.74499,-28.63446],[150.43737,-28.66098],[150.30004,-28.53558],[149.67519,-28.62723],[149.58044,-28.57056],[149.48705,-28.58202],[149.37788,-28.68628],[149.19248,-28.77479],[148.95806,-28.99906],[140.99934,-28.99903]],[[148.76247,-35.49504],[148.80951,-35.30698],[149.12159,-35.1241],[149.19746,-35.18502],[149.18956,-35.20157],[149.2469,-35.2285],[149.23488,-35.24336],[149.39418,-35.30362],[149.39796,-35.32435],[149.35058,-35.3518],[149.33719,-35.33976],[149.25136,-35.33024],[149.2057,-35.34732],[149.13429,-35.45338],[149.15283,-35.50566],[149.12983,-35.55288],[149.14219,-35.59337],[149.07936,-35.58193],[149.09549,-35.6411],[149.09824,-35.81223],[149.04811,-35.91684],[148.96194,-35.8971],[148.89602,-35.82504],[148.89431,-35.75095],[148.8768,-35.715],[148.85723,-35.76043],[148.78891,-35.69995],[148.76247,-35.49504]],[[150.59126,-35.17225],[150.60036,-35.1672],[150.60534,-35.15429],[150.59564,-35.15197],[150.59384,-35.14376],[150.66156,-35.11779],[150.70087,-35.12312],[150.78069,-35.10852],[150.7431,-35.20971],[150.59307,-35.18389],[150.59126,-35.17225]]]}},{"type":"Feature","properties":{"id":"AE"},"geometry":{"type":"MultiPolygon","coordinates":[[[[51.41644,24.39615],[51.58871,24.27256],[51.59617,24.12041],[52.56622,22.94341],[55.13599,22.63334],[55.2137,22.71065],[55.22634,23.10378],[55.57358,23.669],[55.48677,23.94946],[55.73301,24.05994],[55.8308,24.01633],[56.01799,24.07426],[55.95472,24.2172],[55.83367,24.20193],[55.77658,24.23476],[55.76558,24.23227],[55.75257,24.23466],[55.75382,24.2466],[55.75939,24.26114],[55.76781,24.26209],[55.79145,24.27914],[55.80747,24.31069],[55.83395,24.32776],[55.83271,24.41521],[55.76461,24.5287],[55.83271,24.68567],[55.83408,24.77858],[55.81348,24.80102],[55.81116,24.9116],[55.85094,24.96858],[55.90849,24.96771],[55.96316,25.00857],[56.05715,24.95727],[56.05106,24.87461],[55.97467,24.89639],[55.97836,24.87673],[56.03535,24.81161],[56.06128,24.74457],[56.13684,24.73699],[56.20062,24.78565],[56.20568,24.85063],[56.30269,24.88334],[56.34873,24.93205],[56.3227,24.97284],[56.86325,25.03856],[56.82555,25.7713],[56.26534,25.62825],[56.25341,25.61443],[56.26636,25.60643],[56.25365,25.60211],[56.20473,25.61119],[56.18363,25.65508],[56.14826,25.66351],[56.13579,25.73524],[56.17416,25.77239],[56.13963,25.82765],[56.19334,25.9795],[56.15498,26.06828],[56.08666,26.05038],[55.81777,26.18798],[55.14145,25.62624],[53.97892,24.64436],[52.82259,25.51697],[52.35509,25.00368],[52.02277,24.75635],[51.83108,24.71675],[51.58834,24.66608],[51.41644,24.39615]],[[56.20838,25.25668],[56.24465,25.27505],[56.25008,25.28843],[56.23362,25.31253],[56.26062,25.33108],[56.3005,25.31815],[56.3111,25.30107],[56.35172,25.30681],[56.34438,25.26653],[56.27628,25.23404],[56.24341,25.22867],[56.20872,25.24104],[56.20838,25.25668]]],[[[56.27086,25.26128],[56.28423,25.26344],[56.29379,25.2754],[56.28102,25.28486],[56.2716,25.27916],[56.27086,25.26128]]]]}},{"type":"Feature","properties":{"id":"BQ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-68.90012,12.62309],[-68.33524,11.78151],[-68.01417,11.77722],[-67.89186,12.4116],[-68.90012,12.62309]]],[[[-63.58819,17.61311],[-63.22932,17.32592],[-63.11114,17.23125],[-62.76692,17.64353],[-63.07669,17.79659],[-63.29212,17.90532],[-63.58819,17.61311]]]]}},{"type":"Feature","properties":{"id":"AM"},"geometry":{"type":"MultiPolygon","coordinates":[[[[43.44984,41.0988],[43.47319,41.02251],[43.58683,40.98961],[43.67712,40.93084],[43.67712,40.84846],[43.74872,40.7365],[43.7425,40.66805],[43.63664,40.54159],[43.54791,40.47413],[43.60862,40.43267],[43.59928,40.34019],[43.71136,40.16673],[43.65221,40.14889],[43.65688,40.11199],[43.92307,40.01787],[44.1057,40.03555],[44.1778,40.02845],[44.26973,40.04866],[44.46635,39.97733],[44.61845,39.8281],[44.75779,39.7148],[44.88354,39.74432],[44.92869,39.72157],[45.06604,39.79277],[45.18554,39.67846],[45.17464,39.58614],[45.21784,39.58074],[45.23535,39.61373],[45.30385,39.61373],[45.29606,39.57654],[45.46992,39.49888],[45.70547,39.60174],[45.80804,39.56716],[45.83,39.46487],[45.79225,39.3695],[45.99774,39.28931],[46.02303,39.09978],[46.06973,39.0744],[46.14785,38.84206],[46.20601,38.85262],[46.34059,38.92076],[46.53497,38.86548],[46.51805,38.94982],[46.54296,39.07078],[46.44022,39.19636],[46.52584,39.18912],[46.54141,39.15895],[46.58032,39.21204],[46.63481,39.23013],[46.56476,39.24942],[46.50093,39.33736],[46.43244,39.35181],[46.37795,39.42039],[46.4013,39.45405],[46.53051,39.47809],[46.51027,39.52373],[46.57721,39.54414],[46.57098,39.56694],[46.52117,39.58734],[46.42465,39.57534],[46.40286,39.63651],[46.18493,39.60533],[45.96543,39.78859],[45.82533,39.82925],[45.7833,39.9475],[45.60895,39.97733],[45.59806,40.0131],[45.78642,40.03218],[45.83779,39.98925],[45.97944,40.181],[45.95609,40.27846],[45.65098,40.37696],[45.42994,40.53804],[45.45484,40.57707],[45.35366,40.65979],[45.4206,40.7424],[45.55914,40.78366],[45.60584,40.87436],[45.40814,40.97904],[45.44083,41.01663],[45.39725,41.02603],[45.35677,40.99784],[45.28859,41.03757],[45.26162,41.0228],[45.25897,41.0027],[45.1994,41.04518],[45.16493,41.05068],[45.1634,41.08082],[45.1313,41.09369],[45.12923,41.06059],[45.06784,41.05379],[45.08028,41.10917],[45.19942,41.13299],[45.1969,41.168],[45.11811,41.19967],[45.05201,41.19211],[45.02932,41.2101],[45.05497,41.2464],[45.0133,41.29747],[44.93493,41.25685],[44.81437,41.30371],[44.80053,41.25949],[44.81749,41.23488],[44.84358,41.23088],[44.89911,41.21366],[44.87887,41.20195],[44.82084,41.21513],[44.72814,41.20338],[44.61462,41.24018],[44.59322,41.1933],[44.46791,41.18204],[44.34417,41.2382],[44.34337,41.20312],[44.32139,41.2079],[44.18148,41.24644],[44.16591,41.19141],[43.84835,41.16329],[43.74717,41.1117],[43.67712,41.13398],[43.4717,41.12611],[43.44984,41.0988]],[[44.95383,41.07553],[44.97169,41.09176],[45.00864,41.09407],[45.03406,41.07931],[45.04517,41.06653],[45.03792,41.03938],[45.00864,41.03411],[44.9903,41.05657],[44.96031,41.06345],[44.95383,41.07553]],[[45.18382,41.0066],[45.20625,41.01484],[45.23487,41.00226],[45.23095,40.97828],[45.21324,40.9817],[45.21219,40.99001],[45.20518,40.99348],[45.19312,40.98998],[45.18382,41.0066]]],[[[45.47927,40.65023],[45.50279,40.58424],[45.56071,40.64765],[45.51825,40.67382],[45.47927,40.65023]]]]}},{"type":"Feature","properties":{"id":"SJ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-10.71459,70.09565],[-5.93364,70.76368],[-9.68082,72.73731],[-10.71459,70.09565]]],[[[-3.52068,82.6752],[16.4353,73.61229],[33.12005,75.46568],[35.22046,80.57056],[-3.52068,82.6752]]]]}},{"type":"Feature","properties":{"id":"CN-GD"},"geometry":{"type":"Polygon","coordinates":[[[108.26073,20.07614],[111.04979,20.2622],[117.76968,23.10828],[117.05863,23.73884],[116.96353,23.90655],[116.97589,23.95739],[116.97933,24.00099],[116.91478,24.09097],[116.99512,24.18402],[116.93023,24.23256],[116.89693,24.40025],[116.7517,24.5415],[116.81076,24.68352],[116.51859,24.60332],[116.48735,24.67977],[116.37062,24.80231],[116.40975,24.84313],[116.34761,24.86899],[116.29886,24.79857],[116.20754,24.84718],[116.05201,24.85528],[115.96618,24.92193],[115.88859,24.94123],[115.89889,24.87833],[115.86696,24.86743],[115.82405,24.91539],[115.76362,24.79109],[115.76499,24.71221],[115.80379,24.69131],[115.78559,24.63703],[115.84327,24.5671],[115.69358,24.54056],[115.65444,24.61799],[115.56381,24.62954],[115.46699,24.76584],[115.37738,24.76803],[115.10822,24.66792],[115.06599,24.70753],[114.93415,24.6467],[114.9266,24.67478],[114.85897,24.55805],[114.8442,24.58178],[114.72576,24.6055],[114.4178,24.48464],[114.35462,24.58865],[114.17541,24.6545],[114.57092,25.08746],[114.74739,25.13036],[114.5565,25.42281],[114.19464,25.29685],[114.00924,25.28319],[113.98315,25.40916],[113.56635,25.30802],[113.26217,25.51486],[113.12725,25.47039],[113.01721,25.34976],[112.89653,25.31501],[112.84984,25.34278],[112.8713,25.24857],[112.98923,25.24857],[113.02854,25.20059],[112.96674,25.16361],[113.00571,24.93999],[112.7798,24.89328],[112.70393,25.08622],[112.65174,25.13751],[112.18963,25.18987],[112.13504,25.00535],[112.11547,24.96582],[112.17143,24.91944],[112.15015,24.82662],[112.04166,24.77987],[111.93145,24.69506],[112.05711,24.35522],[111.87171,24.10978],[111.92596,23.97339],[111.79515,23.8133],[111.65645,23.83308],[111.63276,23.64641],[111.47724,23.62313],[111.34094,23.19654],[111.42711,23.03013],[111.35158,22.96123],[111.31965,22.85086],[110.99452,22.63682],[110.74424,22.56931],[110.73738,22.46307],[110.67626,22.47576],[110.78475,22.27353],[110.64193,22.23349],[110.67317,22.17659],[110.62442,22.15274],[110.34702,22.19567],[110.36556,21.93476],[110.39234,21.91199],[110.3841,21.89096],[110.32127,21.89351],[110.28573,21.91756],[110.24642,21.88332],[110.19458,21.90148],[109.98481,21.87185],[109.94585,21.84397],[109.92216,21.71198],[109.8904,21.65008],[109.76371,21.67178],[109.73968,21.6054],[109.78912,21.47351],[108.26073,20.07614]],[[113.52659,22.18271],[113.53552,22.20607],[113.53301,22.21235],[113.53593,22.2137],[113.54093,22.21314],[113.54333,22.21688],[113.5508,22.21672],[113.56865,22.20973],[113.57123,22.20416],[113.60504,22.20464],[113.63011,22.10782],[113.57191,22.07696],[113.54839,22.10909],[113.54942,22.14519],[113.54093,22.15497],[113.52659,22.18271]],[[113.81621,22.2163],[113.86771,22.42972],[114.03113,22.5065],[114.05438,22.5026],[114.05729,22.51104],[114.06272,22.51617],[114.07267,22.51855],[114.07817,22.52997],[114.08606,22.53276],[114.09048,22.53716],[114.09692,22.53435],[114.1034,22.5352],[114.11181,22.52878],[114.11656,22.53415],[114.12665,22.54003],[114.13823,22.54319],[114.1482,22.54091],[114.15123,22.55163],[114.1597,22.56041],[114.17247,22.55944],[114.18338,22.55444],[114.20655,22.55706],[114.22185,22.55343],[114.22888,22.5436],[114.25154,22.55977],[114.44998,22.55977],[114.50148,22.15017],[113.92195,22.13873],[113.83338,22.1826],[113.81621,22.2163]]]}},{"type":"Feature","properties":{"id":"AZ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[44.75779,39.7148],[44.80977,39.65768],[44.81043,39.62677],[44.88916,39.59653],[44.96746,39.42998],[45.05932,39.36435],[45.08751,39.35052],[45.16168,39.21952],[45.30489,39.18333],[45.40148,39.09007],[45.40452,39.07224],[45.44811,39.04927],[45.44966,38.99243],[45.6131,38.964],[45.6155,38.94304],[45.65172,38.95199],[45.83883,38.90768],[45.90266,38.87739],[45.94624,38.89072],[46.00228,38.87376],[46.06766,38.87861],[46.14785,38.84206],[46.06973,39.0744],[46.02303,39.09978],[45.99774,39.28931],[45.79225,39.3695],[45.83,39.46487],[45.80804,39.56716],[45.70547,39.60174],[45.46992,39.49888],[45.29606,39.57654],[45.30385,39.61373],[45.23535,39.61373],[45.21784,39.58074],[45.17464,39.58614],[45.18554,39.67846],[45.06604,39.79277],[44.92869,39.72157],[44.88354,39.74432],[44.75779,39.7148]]],[[[44.95383,41.07553],[44.96031,41.06345],[44.9903,41.05657],[45.00864,41.03411],[45.03792,41.03938],[45.04517,41.06653],[45.03406,41.07931],[45.00864,41.09407],[44.97169,41.09176],[44.95383,41.07553]]],[[[45.0133,41.29747],[45.05497,41.2464],[45.02932,41.2101],[45.05201,41.19211],[45.11811,41.19967],[45.1969,41.168],[45.19942,41.13299],[45.08028,41.10917],[45.06784,41.05379],[45.12923,41.06059],[45.1313,41.09369],[45.1634,41.08082],[45.16493,41.05068],[45.1994,41.04518],[45.25897,41.0027],[45.26162,41.0228],[45.28859,41.03757],[45.35677,40.99784],[45.39725,41.02603],[45.44083,41.01663],[45.40814,40.97904],[45.60584,40.87436],[45.55914,40.78366],[45.4206,40.7424],[45.35366,40.65979],[45.45484,40.57707],[45.42994,40.53804],[45.65098,40.37696],[45.95609,40.27846],[45.97944,40.181],[45.83779,39.98925],[45.78642,40.03218],[45.59806,40.0131],[45.60895,39.97733],[45.7833,39.9475],[45.82533,39.82925],[45.96543,39.78859],[46.18493,39.60533],[46.40286,39.63651],[46.42465,39.57534],[46.52117,39.58734],[46.57098,39.56694],[46.57721,39.54414],[46.51027,39.52373],[46.53051,39.47809],[46.4013,39.45405],[46.37795,39.42039],[46.43244,39.35181],[46.50093,39.33736],[46.56476,39.24942],[46.63481,39.23013],[46.58032,39.21204],[46.54141,39.15895],[46.52584,39.18912],[46.44022,39.19636],[46.54296,39.07078],[46.51805,38.94982],[46.53497,38.86548],[46.75752,39.03231],[46.83822,39.13143],[46.92539,39.16644],[46.95341,39.13505],[47.05771,39.20143],[47.05927,39.24846],[47.31301,39.37492],[47.38978,39.45999],[47.50099,39.49615],[47.84774,39.66285],[47.98977,39.70999],[48.34264,39.42935],[48.37385,39.37584],[48.15984,39.30028],[48.12404,39.25208],[48.15361,39.19419],[48.31239,39.09278],[48.33884,39.03022],[48.28437,38.97186],[48.08627,38.94434],[48.07734,38.91616],[48.01409,38.90333],[48.02581,38.82705],[48.24773,38.71883],[48.3146,38.59958],[48.45084,38.61013],[48.58793,38.45076],[48.62217,38.40198],[48.70001,38.40564],[48.78979,38.45026],[48.81072,38.44853],[48.84969,38.45015],[48.88288,38.43975],[49.20805,38.40869],[51.7708,40.29239],[48.80971,41.95365],[48.5867,41.84306],[48.55078,41.77917],[48.42301,41.65444],[48.40277,41.60441],[48.2878,41.56221],[48.22064,41.51472],[48.07587,41.49957],[47.87973,41.21798],[47.75831,41.19455],[47.62288,41.22969],[47.54504,41.20275],[47.49004,41.26366],[47.34579,41.27884],[47.10762,41.59044],[47.03757,41.55434],[46.99554,41.59743],[47.00955,41.63583],[46.8134,41.76252],[46.75269,41.8623],[46.58924,41.80547],[46.5332,41.87389],[46.42738,41.91323],[46.3984,41.84399],[46.30863,41.79133],[46.23962,41.75811],[46.20538,41.77205],[46.17891,41.72094],[46.19759,41.62327],[46.24429,41.59883],[46.26531,41.63339],[46.28182,41.60089],[46.3253,41.60912],[46.34039,41.5947],[46.34126,41.57454],[46.29794,41.5724],[46.33925,41.4963],[46.40307,41.48464],[46.4669,41.43331],[46.63658,41.37727],[46.72375,41.28609],[46.66148,41.20533],[46.63969,41.09515],[46.55096,41.1104],[46.48558,41.0576],[46.456,41.09984],[46.37661,41.10805],[46.27698,41.19011],[46.13221,41.19479],[45.95786,41.17956],[45.80842,41.2229],[45.69946,41.29545],[45.75705,41.35157],[45.71035,41.36208],[45.68389,41.3539],[45.45973,41.45898],[45.4006,41.42402],[45.31352,41.47168],[45.26285,41.46433],[45.1797,41.42231],[45.09867,41.34065],[45.0133,41.29747]],[[45.47927,40.65023],[45.51825,40.67382],[45.56071,40.64765],[45.50279,40.58424],[45.47927,40.65023]]],[[[45.18382,41.0066],[45.19312,40.98998],[45.20518,40.99348],[45.21219,40.99001],[45.21324,40.9817],[45.23095,40.97828],[45.23487,41.00226],[45.20625,41.01484],[45.18382,41.0066]]]]}},{"type":"Feature","properties":{"id":"OM-MU"},"geometry":{"type":"MultiPolygon","coordinates":[[[[55.81777,26.18798],[56.08666,26.05038],[56.15498,26.06828],[56.19334,25.9795],[56.13963,25.82765],[56.17416,25.77239],[56.13579,25.73524],[56.14826,25.66351],[56.18363,25.65508],[56.20473,25.61119],[56.25365,25.60211],[56.26636,25.60643],[56.25341,25.61443],[56.26534,25.62825],[56.82555,25.7713],[56.79239,26.41236],[56.68954,26.76645],[56.2644,26.58649],[55.81777,26.18798]]],[[[56.20838,25.25668],[56.20872,25.24104],[56.24341,25.22867],[56.27628,25.23404],[56.34438,25.26653],[56.35172,25.30681],[56.3111,25.30107],[56.3005,25.31815],[56.26062,25.33108],[56.23362,25.31253],[56.25008,25.28843],[56.24465,25.27505],[56.20838,25.25668]],[[56.27086,25.26128],[56.2716,25.27916],[56.28102,25.28486],[56.29379,25.2754],[56.28423,25.26344],[56.27086,25.26128]]]]}},{"type":"Feature","properties":{"id":"CY"},"geometry":{"type":"MultiPolygon","coordinates":[[[[31.71872,35.1606],[32.10341,34.37973],[32.74412,34.43926],[32.75515,34.64985],[32.76136,34.68318],[32.79433,34.67883],[32.82717,34.70622],[32.86014,34.70585],[32.86167,34.68734],[32.9068,34.66102],[32.91398,34.67343],[32.93043,34.67091],[32.92807,34.66736],[32.93449,34.66241],[32.93693,34.67027],[32.94379,34.67111],[32.94683,34.67907],[32.95539,34.68471],[32.99135,34.68061],[32.98668,34.67268],[32.99014,34.65518],[32.97736,34.65277],[32.97079,34.66112],[32.95325,34.66462],[32.94796,34.6587],[32.94976,34.65204],[32.95471,34.64528],[32.95323,34.64075],[32.95891,34.62919],[32.96718,34.63446],[32.96968,34.64046],[33.0138,34.64424],[33.26744,34.49942],[33.83531,34.73974],[33.70575,34.97947],[33.70639,34.99303],[33.71514,35.00294],[33.69731,35.01754],[33.69938,35.03123],[33.67678,35.03866],[33.67742,35.05963],[33.68474,35.06602],[33.69095,35.06237],[33.70861,35.07644],[33.7161,35.07279],[33.70209,35.04882],[33.71482,35.03722],[33.73824,35.05321],[33.76106,35.04253],[33.78581,35.05104],[33.82067,35.07826],[33.84168,35.06823],[33.8541,35.07201],[33.87479,35.08881],[33.87097,35.09389],[33.87622,35.10457],[33.87224,35.12293],[33.88561,35.12449],[33.88943,35.12007],[33.88737,35.11408],[33.89853,35.11377],[33.91789,35.08688],[33.91299,35.07579],[33.90247,35.07686],[33.89485,35.06873],[33.88367,35.07877],[33.85261,35.0574],[33.8355,35.05777],[33.82051,35.0667],[33.8012,35.04786],[33.81524,35.04192],[33.83055,35.02865],[33.82875,35.01685],[33.84045,35.00616],[33.85216,35.00579],[33.85891,35.001],[33.85621,34.98956],[33.83505,34.98108],[33.84811,34.97075],[33.86432,34.97592],[33.90075,34.96623],[33.98684,34.76642],[35.48515,34.70851],[35.51152,36.10954],[32.82353,35.70297],[31.71872,35.1606]]],[[[33.7343,35.01178],[33.74144,35.01053],[33.7492,35.01319],[33.74983,35.02274],[33.74265,35.02329],[33.73781,35.02181],[33.7343,35.01178]]],[[[33.75682,34.99916],[33.76605,34.99543],[33.76738,34.99188],[33.7778,34.98981],[33.77843,34.988],[33.78149,34.98854],[33.78318,34.98699],[33.78571,34.98951],[33.78917,34.98854],[33.79191,34.98914],[33.78516,34.99582],[33.77553,34.99518],[33.77312,34.9976],[33.75994,35.00113],[33.75682,34.99916]]]]}},{"type":"Feature","properties":{"id":"IN-PY"},"geometry":{"type":"MultiPolygon","coordinates":[[[[75.18012,11.478],[75.19042,11.45378],[75.53787,11.6872],[75.55864,11.72317],[75.53821,11.76384],[75.52551,11.6993],[75.18012,11.478]]],[[[79.59525,11.86735],[80.35262,11.6751],[80.31898,12.0091],[79.70031,12.10378],[79.59525,11.86735]]],[[[79.68658,10.99107],[79.8088,10.81374],[80.42198,10.81981],[80.41717,10.96613],[79.68658,10.99107]]],[[[82.19078,16.71874],[82.9708,16.27499],[83.0072,16.31915],[82.21618,16.76378],[82.19078,16.71874]]]]}},{"type":"Feature","properties":{"id":"DK"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-74.12379,75.70014],[-53.68108,62.9266],[-45.64471,55.43944],[-25.70385,67.46637],[-10.71459,70.09565],[-9.68082,72.73731],[-3.52068,82.6752],[-34.32457,84.11035],[-59.93819,82.31398],[-63.1988,81.66522],[-67.48417,80.75493],[-73.91222,78.42484],[-74.12379,75.70014]]],[[[-8.51774,62.35338],[-6.51083,60.95272],[-5.70102,62.77194],[-8.51774,62.35338]]],[[[7.28637,57.35913],[8.02459,55.09613],[8.45719,55.06747],[8.55769,54.91837],[8.63979,54.91069],[8.76387,54.8948],[8.81178,54.90518],[8.92795,54.90452],[9.04629,54.87249],[9.14275,54.87421],[9.20571,54.85841],[9.24631,54.84726],[9.23445,54.83432],[9.2474,54.8112],[9.32771,54.80602],[9.33849,54.80233],[9.36496,54.81749],[9.38532,54.83968],[9.41213,54.84254],[9.43155,54.82586],[9.4659,54.83131],[9.58937,54.88785],[9.62734,54.88057],[9.61187,54.85548],[9.73563,54.8247],[9.89314,54.84171],[10.16755,54.73883],[10.31111,54.65968],[11.00303,54.63689],[11.90309,54.38543],[12.85844,54.82438],[13.93395,54.84044],[15.36991,54.73263],[15.79951,55.54655],[14.89259,55.5623],[14.28399,55.1553],[12.84405,55.13257],[12.60345,55.42675],[12.88472,55.63369],[12.6372,55.91371],[12.65312,56.04345],[12.07466,56.29488],[12.16597,56.60205],[10.40861,58.38489],[7.28637,57.35913]]]]}},{"type":"Feature","properties":{"id":"ES"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-18.8556,26.96222],[-14.43883,27.02969],[-12.42686,29.61659],[-14.33337,30.94071],[-15.92339,29.50503],[-18.67893,29.62861],[-18.8556,26.96222]]],[[[-10.14298,44.17365],[-9.14112,41.86623],[-8.87157,41.86488],[-8.81794,41.90375],[-8.75712,41.92833],[-8.74606,41.9469],[-8.7478,41.96282],[-8.69071,41.98862],[-8.6681,41.99703],[-8.65832,42.02972],[-8.64626,42.03668],[-8.63791,42.04691],[-8.59493,42.05708],[-8.58086,42.05147],[-8.54563,42.0537],[-8.5252,42.06264],[-8.52837,42.07658],[-8.48185,42.0811],[-8.44123,42.08218],[-8.42512,42.07199],[-8.40143,42.08052],[-8.38323,42.07683],[-8.36353,42.09065],[-8.33912,42.08358],[-8.32161,42.10218],[-8.29809,42.106],[-8.2732,42.12396],[-8.24681,42.13993],[-8.22406,42.1328],[-8.1986,42.15402],[-8.18947,42.13853],[-8.19406,42.12141],[-8.18178,42.06436],[-8.11729,42.08537],[-8.08847,42.05767],[-8.08796,42.01398],[-8.16232,41.9828],[-8.2185,41.91237],[-8.19551,41.87459],[-8.16944,41.87944],[-8.16455,41.81753],[-8.0961,41.81024],[-8.01136,41.83453],[-7.9804,41.87337],[-7.92336,41.8758],[-7.90707,41.92432],[-7.88751,41.92553],[-7.88055,41.84571],[-7.84188,41.88065],[-7.69848,41.90977],[-7.65774,41.88308],[-7.58603,41.87944],[-7.62188,41.83089],[-7.52737,41.83939],[-7.49803,41.87095],[-7.45566,41.86488],[-7.44759,41.84451],[-7.42854,41.83262],[-7.42864,41.80589],[-7.37092,41.85031],[-7.32366,41.8406],[-7.18677,41.88793],[-7.18549,41.97515],[-7.14115,41.98855],[-7.08574,41.97401],[-7.07596,41.94977],[-7.01078,41.94977],[-6.98144,41.9728],[-6.95537,41.96553],[-6.94396,41.94403],[-6.82174,41.94493],[-6.81196,41.99097],[-6.76959,41.98734],[-6.75004,41.94129],[-6.61967,41.94008],[-6.58544,41.96674],[-6.5447,41.94371],[-6.56752,41.88429],[-6.51374,41.8758],[-6.56426,41.74219],[-6.54633,41.68623],[-6.49907,41.65823],[-6.44204,41.68258],[-6.29863,41.66432],[-6.19128,41.57638],[-6.26777,41.48796],[-6.3306,41.37677],[-6.38553,41.38655],[-6.38551,41.35274],[-6.55937,41.24417],[-6.65046,41.24725],[-6.68286,41.21641],[-6.69711,41.1858],[-6.77319,41.13049],[-6.75655,41.10187],[-6.79241,41.05397],[-6.80942,41.03629],[-6.84781,41.02692],[-6.88843,41.03027],[-6.913,41.03922],[-6.9357,41.02888],[-6.8527,40.93958],[-6.84292,40.89771],[-6.80707,40.88047],[-6.79892,40.84842],[-6.82337,40.84472],[-6.82826,40.74603],[-6.79567,40.65955],[-6.84292,40.56801],[-6.80218,40.55067],[-6.7973,40.51723],[-6.84944,40.46394],[-6.84618,40.42177],[-6.78426,40.36468],[-6.80218,40.33239],[-6.86085,40.2976],[-6.86085,40.26776],[-7.00426,40.23169],[-7.02544,40.18564],[-7.00589,40.12087],[-6.94233,40.10716],[-6.86737,40.01986],[-6.91463,39.86618],[-6.97492,39.81488],[-7.01613,39.66877],[-7.24707,39.66576],[-7.33507,39.64569],[-7.54121,39.66717],[-7.49477,39.58794],[-7.2927,39.45847],[-7.3149,39.34857],[-7.23403,39.27579],[-7.23566,39.20132],[-7.12811,39.17101],[-7.14929,39.11287],[-7.10692,39.10275],[-7.04011,39.11919],[-6.97004,39.07619],[-6.95211,39.0243],[-7.051,38.907],[-7.03848,38.87221],[-7.26174,38.72107],[-7.265,38.61674],[-7.32529,38.44336],[-7.15581,38.27597],[-7.09389,38.17227],[-6.93418,38.21454],[-7.00375,38.01914],[-7.05966,38.01966],[-7.10366,38.04404],[-7.12648,38.00296],[-7.24544,37.98884],[-7.27314,37.90145],[-7.33441,37.81193],[-7.41981,37.75729],[-7.51759,37.56119],[-7.46878,37.47127],[-7.43974,37.38913],[-7.43227,37.25152],[-7.41854,37.23813],[-7.41133,37.20314],[-7.39769,37.16868],[-7.37282,36.96896],[-7.27694,35.93599],[-5.64962,35.93752],[-5.10878,36.05227],[-2.85819,35.63219],[-2.27707,35.35051],[2.46645,37.97429],[5.18061,39.43581],[3.4481,42.4358],[3.17156,42.43545],[3.11379,42.43646],[3.10027,42.42621],[3.08167,42.42748],[3.03734,42.47363],[2.96518,42.46692],[2.94283,42.48174],[2.92107,42.4573],[2.88413,42.45938],[2.86983,42.46843],[2.85675,42.45444],[2.84335,42.45724],[2.77464,42.41046],[2.75497,42.42578],[2.72056,42.42298],[2.65311,42.38771],[2.6747,42.33974],[2.57934,42.35808],[2.55516,42.35351],[2.54382,42.33406],[2.48457,42.33933],[2.43508,42.37568],[2.43299,42.39423],[2.38504,42.39977],[2.25551,42.43757],[2.20578,42.41633],[2.16599,42.42314],[2.12789,42.41291],[2.11621,42.38393],[2.06241,42.35906],[2.00488,42.35399],[1.96482,42.37787],[1.9574,42.42401],[1.94084,42.43039],[1.94061,42.43333],[1.94292,42.44316],[1.93663,42.45439],[1.88853,42.4501],[1.83037,42.48395],[1.76335,42.48863],[1.72515,42.50338],[1.70571,42.48867],[1.66826,42.50779],[1.65674,42.47125],[1.58933,42.46275],[1.57953,42.44957],[1.55937,42.45808],[1.55073,42.43299],[1.5127,42.42959],[1.44529,42.43724],[1.43838,42.47848],[1.41648,42.48315],[1.46661,42.50949],[1.44759,42.54431],[1.41245,42.53539],[1.4234,42.55959],[1.44529,42.56722],[1.42512,42.58292],[1.44197,42.60217],[1.35562,42.71944],[1.15928,42.71407],[1.0804,42.78569],[0.98292,42.78754],[0.96166,42.80629],[0.93089,42.79154],[0.711,42.86372],[0.66121,42.84021],[0.65421,42.75872],[0.67873,42.69458],[0.40214,42.69779],[0.36251,42.72282],[0.29407,42.67431],[0.25336,42.7174],[0.17569,42.73424],[-0.02468,42.68513],[-0.10519,42.72761],[-0.16141,42.79535],[-0.17939,42.78974],[-0.3122,42.84788],[-0.38833,42.80132],[-0.41319,42.80776],[-0.44334,42.79939],[-0.50863,42.82713],[-0.55497,42.77846],[-0.67637,42.88303],[-0.69837,42.87945],[-0.72608,42.89318],[-0.73422,42.91228],[-0.72037,42.92541],[-0.75478,42.96916],[-0.81652,42.95166],[-0.97133,42.96239],[-1.00963,42.99279],[-1.10333,43.0059],[-1.22881,43.05534],[-1.25244,43.04164],[-1.30531,43.06859],[-1.30052,43.09581],[-1.27118,43.11961],[-1.32209,43.1127],[-1.34419,43.09665],[-1.35272,43.02658],[-1.44067,43.047],[-1.47555,43.08372],[-1.41562,43.12815],[-1.3758,43.24511],[-1.40942,43.27272],[-1.45289,43.27049],[-1.50992,43.29481],[-1.55963,43.28828],[-1.57674,43.25269],[-1.61341,43.25269],[-1.63052,43.28591],[-1.62481,43.30726],[-1.69407,43.31378],[-1.73074,43.29481],[-1.7397,43.32979],[-1.75079,43.3317],[-1.75334,43.34107],[-1.77068,43.34396],[-1.78714,43.35476],[-1.78332,43.36399],[-1.79319,43.37497],[-1.77289,43.38957],[-1.81005,43.59738],[-10.14298,44.17365]],[[-5.40134,36.14896],[-5.38545,36.15481],[-5.36494,36.15496],[-5.34536,36.15501],[-5.33822,36.15272],[-5.27801,36.14942],[-5.28217,36.09907],[-5.3004,36.07439],[-5.32837,36.05935],[-5.36503,36.06205],[-5.39074,36.10278],[-5.40134,36.14896]]],[[[-5.38491,35.92591],[-5.37338,35.88417],[-5.35844,35.87375],[-5.34379,35.8711],[-5.27056,35.88794],[-5.27635,35.91222],[-5.38491,35.92591]]],[[[-4.30191,35.17419],[-4.30112,35.17058],[-4.29436,35.17149],[-4.30191,35.17419]]],[[[-3.90602,35.21494],[-3.88926,35.20841],[-3.88617,35.21406],[-3.90288,35.22024],[-3.90602,35.21494]]],[[[-2.97035,35.28852],[-2.96507,35.28801],[-2.96826,35.28296],[-2.96516,35.27967],[-2.95431,35.2728],[-2.95065,35.26576],[-2.93893,35.26737],[-2.92674,35.27313],[-2.92181,35.28599],[-2.92224,35.3401],[-2.96038,35.31609],[-2.96648,35.30475],[-2.96978,35.29459],[-2.97035,35.28852]]],[[[-2.44896,35.18777],[-2.44887,35.17075],[-2.41312,35.17111],[-2.41265,35.1877],[-2.44896,35.18777]]],[[[1.95606,42.45785],[1.96125,42.45364],[1.98378,42.44697],[1.99838,42.44682],[2.01564,42.45171],[1.99216,42.46208],[1.98579,42.47486],[1.99766,42.4858],[1.98916,42.49351],[1.98022,42.49569],[1.97697,42.48568],[1.97227,42.48487],[1.97003,42.48081],[1.96215,42.47854],[1.95606,42.45785]]]]}},{"type":"Feature","properties":{"id":"FJ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-180,-22.90585],[-176.74538,-22.89767],[-176.76826,-14.95183],[-178.60161,-14.95666],[-180,-14.96041],[-180,-22.90585]]],[[[174,-22.5],[179.99999,-22.5],[179.99999,-11.5],[174,-11.5],[174,-22.5]]]]}},{"type":"Feature","properties":{"id":"FR"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-178.60852,-12.49232],[-178.60161,-14.95666],[-176.76826,-14.95183],[-174.17905,-14.94502],[-174.18596,-12.48057],[-178.60852,-12.49232]]],[[[-156.4957,-12.32002],[-156.46451,-23.21255],[-156.44843,-28.52556],[-133.59543,-28.4709],[-133.61511,-21.93325],[-133.65593,-7.46952],[-149.6249,-7.51261],[-149.61166,-12.30171],[-156.4957,-12.32002]]],[[[-109.6462,9.84394],[-108.755,9.84085],[-108.75183,10.72712],[-109.64303,10.7302],[-109.6462,9.84394]]],[[[-63.35989,18.06012],[-63.33064,17.9615],[-63.13584,18.0541],[-63.11096,18.05368],[-63.09686,18.04608],[-63.07759,18.04943],[-63.0579,18.06614],[-63.04039,18.05619],[-63.02323,18.05757],[-62.93924,18.02904],[-63.07669,17.79659],[-62.76692,17.64353],[-62.54836,17.8636],[-62.75637,18.13489],[-62.86666,18.19278],[-63.35989,18.06012]]],[[[-62.17275,16.35721],[-61.81728,15.58058],[-61.44899,15.79571],[-60.95725,15.70997],[-60.71337,16.48911],[-61.44461,16.81958],[-61.83929,16.66647],[-62.17275,16.35721]]],[[[-61.51867,14.96709],[-61.26561,14.25664],[-60.5958,14.23076],[-60.69955,15.22234],[-61.51867,14.96709]]],[[[-56.70773,46.51478],[-55.8643,46.64935],[-56.25228,47.31192],[-56.67989,47.3339],[-56.70773,46.51478]]],[[[-54.6084,2.32856],[-54.16286,2.10779],[-53.78743,2.34412],[-52.96539,2.1881],[-52.6906,2.37298],[-52.31787,3.17896],[-51.85573,3.83427],[-51.82312,3.85825],[-51.79599,3.89336],[-51.61983,4.14596],[-51.63798,4.51394],[-51.35485,4.8383],[-53.7094,6.2264],[-54.01074,5.68785],[-54.01877,5.52789],[-54.26916,5.26909],[-54.4717,4.91964],[-54.38444,4.13222],[-54.19367,3.84387],[-54.05128,3.63557],[-53.98914,3.627],[-53.9849,3.58697],[-54.28534,2.67798],[-54.42864,2.42442],[-54.6084,2.32856]]],[[[-5.81385,48.52441],[-1.81005,43.59738],[-1.77289,43.38957],[-1.79319,43.37497],[-1.78332,43.36399],[-1.78714,43.35476],[-1.77068,43.34396],[-1.75334,43.34107],[-1.75079,43.3317],[-1.7397,43.32979],[-1.73074,43.29481],[-1.69407,43.31378],[-1.62481,43.30726],[-1.63052,43.28591],[-1.61341,43.25269],[-1.57674,43.25269],[-1.55963,43.28828],[-1.50992,43.29481],[-1.45289,43.27049],[-1.40942,43.27272],[-1.3758,43.24511],[-1.41562,43.12815],[-1.47555,43.08372],[-1.44067,43.047],[-1.35272,43.02658],[-1.34419,43.09665],[-1.32209,43.1127],[-1.27118,43.11961],[-1.30052,43.09581],[-1.30531,43.06859],[-1.25244,43.04164],[-1.22881,43.05534],[-1.10333,43.0059],[-1.00963,42.99279],[-0.97133,42.96239],[-0.81652,42.95166],[-0.75478,42.96916],[-0.72037,42.92541],[-0.73422,42.91228],[-0.72608,42.89318],[-0.69837,42.87945],[-0.67637,42.88303],[-0.55497,42.77846],[-0.50863,42.82713],[-0.44334,42.79939],[-0.41319,42.80776],[-0.38833,42.80132],[-0.3122,42.84788],[-0.17939,42.78974],[-0.16141,42.79535],[-0.10519,42.72761],[-0.02468,42.68513],[0.17569,42.73424],[0.25336,42.7174],[0.29407,42.67431],[0.36251,42.72282],[0.40214,42.69779],[0.67873,42.69458],[0.65421,42.75872],[0.66121,42.84021],[0.711,42.86372],[0.93089,42.79154],[0.96166,42.80629],[0.98292,42.78754],[1.0804,42.78569],[1.15928,42.71407],[1.35562,42.71944],[1.44197,42.60217],[1.47986,42.61346],[1.46718,42.63296],[1.48043,42.65203],[1.50867,42.64483],[1.55418,42.65669],[1.60085,42.62703],[1.63485,42.62957],[1.6625,42.61982],[1.68267,42.62533],[1.73452,42.61515],[1.72588,42.59098],[1.7858,42.57698],[1.73683,42.55492],[1.72515,42.50338],[1.76335,42.48863],[1.83037,42.48395],[1.88853,42.4501],[1.93663,42.45439],[1.94292,42.44316],[1.94061,42.43333],[1.94084,42.43039],[1.9574,42.42401],[1.96482,42.37787],[2.00488,42.35399],[2.06241,42.35906],[2.11621,42.38393],[2.12789,42.41291],[2.16599,42.42314],[2.20578,42.41633],[2.25551,42.43757],[2.38504,42.39977],[2.43299,42.39423],[2.43508,42.37568],[2.48457,42.33933],[2.54382,42.33406],[2.55516,42.35351],[2.57934,42.35808],[2.6747,42.33974],[2.65311,42.38771],[2.72056,42.42298],[2.75497,42.42578],[2.77464,42.41046],[2.84335,42.45724],[2.85675,42.45444],[2.86983,42.46843],[2.88413,42.45938],[2.92107,42.4573],[2.94283,42.48174],[2.96518,42.46692],[3.03734,42.47363],[3.08167,42.42748],[3.10027,42.42621],[3.11379,42.43646],[3.17156,42.43545],[3.4481,42.4358],[7.52234,41.54558],[8.80584,41.26173],[9.28609,41.32097],[9.62656,41.44198],[9.86526,42.21008],[9.56115,43.20816],[7.50102,43.51859],[7.41855,43.72479],[7.40903,43.7296],[7.41113,43.73156],[7.41291,43.73168],[7.41298,43.73311],[7.41233,43.73439],[7.42062,43.73977],[7.42299,43.74176],[7.42443,43.74087],[7.42809,43.74396],[7.43013,43.74895],[7.43624,43.75014],[7.43708,43.75197],[7.4389,43.75151],[7.4379,43.74963],[7.45448,43.7432],[7.53358,43.53609],[7.63035,43.57419],[7.5289,43.78887],[7.50423,43.84345],[7.49355,43.86551],[7.51162,43.88301],[7.56075,43.89932],[7.56858,43.94506],[7.60771,43.95772],[7.65266,43.9763],[7.66848,43.99943],[7.6597,44.03009],[7.72508,44.07578],[7.66878,44.12795],[7.68694,44.17487],[7.63245,44.17877],[7.62155,44.14881],[7.36364,44.11882],[7.34547,44.14359],[7.27827,44.1462],[7.16929,44.20352],[7.00764,44.23736],[6.98221,44.28289],[6.89171,44.36637],[6.88784,44.42043],[6.94504,44.43112],[6.86233,44.49834],[6.85507,44.53072],[6.96042,44.62129],[6.95133,44.66264],[7.00582,44.69364],[7.07484,44.68073],[7.00401,44.78782],[7.02217,44.82519],[6.93499,44.8664],[6.90774,44.84322],[6.75518,44.89915],[6.74519,44.93661],[6.74791,45.01939],[6.66981,45.02324],[6.62803,45.11175],[6.7697,45.16044],[6.85144,45.13226],[6.96706,45.20841],[7.07074,45.21228],[7.13115,45.25386],[7.10572,45.32924],[7.18019,45.40071],[7.00037,45.509],[6.98948,45.63869],[6.80785,45.71864],[6.80785,45.83265],[6.95315,45.85163],[7.04151,45.92435],[7.00946,45.9944],[6.93862,46.06502],[6.87868,46.03855],[6.89321,46.12548],[6.78968,46.14058],[6.86052,46.28512],[6.77152,46.34784],[6.8024,46.39171],[6.82312,46.42661],[6.53358,46.45431],[6.25432,46.3632],[6.21981,46.31304],[6.24826,46.30175],[6.25137,46.29014],[6.23775,46.27822],[6.24952,46.26255],[6.26749,46.24745],[6.29474,46.26221],[6.31041,46.24417],[6.29663,46.22688],[6.27694,46.21566],[6.26007,46.21165],[6.24821,46.20531],[6.23913,46.20511],[6.23544,46.20714],[6.22175,46.20045],[6.22222,46.19888],[6.21844,46.19837],[6.21603,46.19507],[6.21273,46.19409],[6.21114,46.1927],[6.20539,46.19163],[6.19807,46.18369],[6.19552,46.18401],[6.18707,46.17999],[6.18871,46.16644],[6.18116,46.16187],[6.15305,46.15194],[6.13397,46.1406],[6.09926,46.14373],[6.09199,46.15191],[6.07491,46.14879],[6.05203,46.15191],[6.04564,46.14031],[6.03614,46.13712],[6.01791,46.14228],[5.9871,46.14499],[5.97893,46.13303],[5.95781,46.12925],[5.9641,46.14412],[5.97508,46.15863],[5.98188,46.17392],[5.98846,46.17046],[5.99573,46.18587],[5.96515,46.19638],[5.97542,46.21525],[6.02461,46.23313],[6.03342,46.2383],[6.04602,46.23127],[6.05029,46.23518],[6.0633,46.24583],[6.07072,46.24085],[6.08563,46.24651],[6.10071,46.23772],[6.12446,46.25059],[6.11926,46.2634],[6.1013,46.28512],[6.11697,46.29547],[6.1198,46.31157],[6.13876,46.33844],[6.15738,46.3491],[6.16987,46.36759],[6.15985,46.37721],[6.15016,46.3778],[6.09926,46.40768],[6.06407,46.41676],[6.08427,46.44305],[6.07269,46.46244],[6.1567,46.54402],[6.11084,46.57649],[6.27135,46.68251],[6.38351,46.73171],[6.45209,46.77502],[6.43216,46.80336],[6.46456,46.88865],[6.43341,46.92703],[6.71531,47.0494],[6.68823,47.06616],[6.76788,47.1208],[6.8489,47.15933],[6.9508,47.24338],[6.95108,47.26428],[6.94316,47.28747],[7.05305,47.33304],[7.0564,47.35134],[7.03125,47.36996],[6.87959,47.35335],[6.88542,47.37262],[6.93744,47.40714],[6.93953,47.43388],[7.0024,47.45264],[6.98425,47.49432],[7.0231,47.50522],[7.07425,47.48863],[7.12781,47.50371],[7.16249,47.49025],[7.19583,47.49455],[7.17026,47.44312],[7.24669,47.4205],[7.33526,47.44186],[7.35603,47.43432],[7.40308,47.43638],[7.43088,47.45846],[7.4462,47.46264],[7.4583,47.47216],[7.42923,47.48628],[7.43356,47.49712],[7.47534,47.47932],[7.51076,47.49651],[7.49804,47.51798],[7.5229,47.51644],[7.53199,47.5284],[7.51904,47.53515],[7.50588,47.52856],[7.49691,47.53821],[7.50873,47.54546],[7.51723,47.54578],[7.52831,47.55347],[7.53634,47.55553],[7.55652,47.56779],[7.55689,47.57232],[7.56548,47.57617],[7.56684,47.57785],[7.58386,47.57536],[7.58945,47.59017],[7.59301,47.60058],[7.58851,47.60794],[7.57423,47.61628],[7.5591,47.63849],[7.53384,47.65115],[7.52067,47.66437],[7.51915,47.68335],[7.51266,47.70197],[7.53722,47.71635],[7.54761,47.72912],[7.52921,47.77747],[7.55673,47.87371],[7.62302,47.97898],[7.56966,48.03265],[7.57137,48.12292],[7.6648,48.22219],[7.69022,48.30018],[7.74562,48.32736],[7.73109,48.39192],[7.76833,48.48945],[7.80647,48.51239],[7.80167,48.54758],[7.80057,48.5857],[7.84098,48.64217],[7.89002,48.66317],[7.96812,48.72491],[7.96994,48.75606],[8.01534,48.76085],[8.0326,48.79017],[8.06802,48.78957],[8.10253,48.81829],[8.12813,48.87985],[8.19989,48.95825],[8.20031,48.95856],[8.22604,48.97352],[8.14189,48.97833],[7.97783,49.03161],[7.93641,49.05544],[7.86386,49.03499],[7.79557,49.06583],[7.75948,49.04562],[7.63618,49.05428],[7.62575,49.07654],[7.56416,49.08136],[7.53012,49.09818],[7.49172,49.13915],[7.49473,49.17],[7.44455,49.16765],[7.44052,49.18354],[7.3662,49.17308],[7.35995,49.14399],[7.3195,49.14231],[7.29514,49.11426],[7.23473,49.12971],[7.1593,49.1204],[7.1358,49.1282],[7.12504,49.14253],[7.10384,49.13787],[7.10715,49.15631],[7.07859,49.15031],[7.09007,49.13094],[7.07162,49.1255],[7.06642,49.11415],[7.05548,49.11185],[7.04843,49.11422],[7.04409,49.12123],[7.04662,49.13724],[7.03178,49.15734],[7.0274,49.17042],[7.03459,49.19096],[7.01318,49.19018],[6.97273,49.2099],[6.95963,49.203],[6.94028,49.21641],[6.93831,49.2223],[6.91875,49.22261],[6.89298,49.20863],[6.85939,49.22376],[6.83555,49.21249],[6.85119,49.20038],[6.85016,49.19354],[6.86225,49.18185],[6.84703,49.15734],[6.83385,49.15162],[6.78265,49.16793],[6.73765,49.16375],[6.71137,49.18808],[6.73256,49.20486],[6.71843,49.2208],[6.69274,49.21661],[6.66583,49.28065],[6.60186,49.31055],[6.572,49.35027],[6.58807,49.35358],[6.60091,49.36864],[6.533,49.40748],[6.55404,49.42464],[6.42432,49.47683],[6.40274,49.46546],[6.39168,49.4667],[6.38352,49.46463],[6.36778,49.46937],[6.3687,49.4593],[6.28818,49.48465],[6.27875,49.503],[6.25029,49.50609],[6.2409,49.51408],[6.19543,49.50536],[6.17386,49.50934],[6.15366,49.50226],[6.16115,49.49297],[6.14321,49.48796],[6.12814,49.49365],[6.12346,49.4735],[6.10325,49.4707],[6.09845,49.46351],[6.10072,49.45268],[6.08373,49.45594],[6.07887,49.46399],[6.05553,49.46663],[6.04176,49.44801],[6.02743,49.44845],[6.02648,49.45451],[5.97693,49.45513],[5.96876,49.49053],[5.94224,49.49608],[5.94128,49.50034],[5.86571,49.50015],[5.83389,49.52152],[5.83467,49.52717],[5.84466,49.53027],[5.83648,49.5425],[5.81664,49.53775],[5.80871,49.5425],[5.81838,49.54777],[5.79195,49.55228],[5.77435,49.56298],[5.7577,49.55915],[5.75649,49.54321],[5.64505,49.55146],[5.60909,49.51228],[5.55001,49.52729],[5.46541,49.49825],[5.46734,49.52648],[5.43713,49.5707],[5.3974,49.61596],[5.34837,49.62889],[5.33851,49.61599],[5.3137,49.61225],[5.30214,49.63055],[5.33039,49.6555],[5.31465,49.66846],[5.26232,49.69456],[5.14545,49.70287],[5.09249,49.76193],[4.96714,49.79872],[4.85464,49.78995],[4.86965,49.82271],[4.85134,49.86457],[4.88529,49.9236],[4.78827,49.95609],[4.8382,50.06738],[4.88602,50.15182],[4.83279,50.15331],[4.82438,50.16878],[4.75237,50.11314],[4.70064,50.09384],[4.68695,49.99685],[4.5414,49.96911],[4.51098,49.94659],[4.43488,49.94122],[4.35051,49.95315],[4.31963,49.97043],[4.20532,49.95803],[4.14239,49.98034],[4.13508,50.01976],[4.16294,50.04719],[4.23101,50.06945],[4.20147,50.13535],[4.13561,50.13078],[4.16014,50.19239],[4.15524,50.21103],[4.21945,50.25539],[4.20651,50.27333],[4.17861,50.27443],[4.17347,50.28838],[4.15524,50.2833],[4.16808,50.25786],[4.13665,50.25609],[4.11954,50.30425],[4.10957,50.30234],[4.10237,50.31247],[4.0689,50.3254],[4.0268,50.35793],[3.96771,50.34989],[3.90781,50.32814],[3.84314,50.35219],[3.73911,50.34809],[3.70987,50.3191],[3.71009,50.30305],[3.66976,50.34563],[3.65709,50.36873],[3.67262,50.38663],[3.67494,50.40239],[3.66153,50.45165],[3.64426,50.46275],[3.61014,50.49568],[3.58361,50.49049],[3.5683,50.50192],[3.49509,50.48885],[3.51564,50.5256],[3.47385,50.53397],[3.44629,50.51009],[3.37693,50.49538],[3.28575,50.52724],[3.2729,50.60718],[3.23951,50.6585],[3.264,50.67668],[3.2536,50.68977],[3.26141,50.69151],[3.26063,50.70086],[3.24593,50.71389],[3.22042,50.71019],[3.20845,50.71662],[3.19017,50.72569],[3.20064,50.73547],[3.18811,50.74025],[3.18339,50.74981],[3.16476,50.76843],[3.15017,50.79031],[3.1257,50.78603],[3.11987,50.79188],[3.11206,50.79416],[3.10614,50.78303],[3.09163,50.77717],[3.04314,50.77674],[3.00537,50.76588],[2.96778,50.75242],[2.95019,50.75138],[2.90873,50.702],[2.91036,50.6939],[2.90069,50.69263],[2.88504,50.70656],[2.87937,50.70298],[2.86985,50.7033],[2.8483,50.72276],[2.81056,50.71773],[2.71165,50.81295],[2.63331,50.81457],[2.59093,50.91751],[2.63074,50.94746],[2.57551,51.00326],[2.55904,51.07014],[2.18458,51.52087],[1.17405,50.74239],[-2.02963,49.91866],[-2.09454,49.46288],[-1.83944,49.23037],[-2.00491,48.86706],[-2.5234,48.91595],[-2.56423,49.22209],[-2.9511,49.31141],[-5.81385,48.52441]],[[1.95606,42.45785],[1.96215,42.47854],[1.97003,42.48081],[1.97227,42.48487],[1.97697,42.48568],[1.98022,42.49569],[1.98916,42.49351],[1.99766,42.4858],[1.98579,42.47486],[1.99216,42.46208],[2.01564,42.45171],[1.99838,42.44682],[1.98378,42.44697],[1.96125,42.45364],[1.95606,42.45785]]],[[[39.10324,-21.48967],[40.40841,-23.17181],[43.72277,-16.09877],[41.06663,-17.08802],[39.10324,-21.48967]]],[[[44.75722,-12.58368],[44.82644,-13.30845],[45.54824,-13.22353],[45.45962,-12.30345],[44.75722,-12.58368]]],[[[46.31615,-46.28749],[70.67507,-51.14192],[80.15867,-36.04977],[46.31615,-46.28749]]],[[[46.52682,-10.83678],[47.29063,-12.45583],[48.86266,-10.8109],[46.52682,-10.83678]]],[[[54.08717,-15.5001],[54.13761,-16.33002],[54.96649,-16.28353],[54.91606,-15.45342],[54.08717,-15.5001]]],[[[54.32269,-20.37973],[54.43368,-22.02482],[56.73473,-21.9174],[56.62373,-20.2711],[54.32269,-20.37973]]],[[[157.46481,-18.93777],[158.4748,-21.86428],[166.93331,-23.49588],[173.07304,-22.54607],[162.93363,-17.28904],[157.46481,-18.93777]]]]}},{"type":"Feature","properties":{"id":"GB"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-133.61511,-21.93325],[-133.59543,-28.4709],[-122.0366,-24.55017],[-133.61511,-21.93325]]],[[[-81.81969,19.51758],[-81.52417,18.7521],[-79.33932,19.50496],[-79.63484,20.26689],[-81.81969,19.51758]]],[[[-72.94479,20.79216],[-71.13087,20.98822],[-70.63262,21.53631],[-72.41726,22.40371],[-72.94479,20.79216]]],[[[-65.23529,32.66274],[-65.22652,31.98296],[-64.37503,31.99084],[-64.3838,32.67056],[-65.23529,32.66274]]],[[[-65.02435,18.73231],[-64.86049,18.39954],[-64.64067,18.36478],[-64.646,18.10286],[-63.95092,18.07976],[-63.35989,18.06012],[-62.86666,18.19278],[-62.75637,18.13489],[-62.64209,18.3662],[-63.3414,18.89029],[-63.90607,18.93262],[-64.62855,18.98678],[-65.02435,18.73231]]],[[[-62.78369,-53.1401],[-58.84651,-53.93403],[-55.76919,-51.15168],[-62.3754,-50.36819],[-62.78369,-53.1401]]],[[[-62.52079,16.69392],[-62.17275,16.35721],[-61.83929,16.66647],[-62.14123,17.02632],[-62.52079,16.69392]]],[[[-43.57991,-52.56305],[-40.68557,-57.40649],[-26.52505,-59.90465],[-23.50385,-54.792],[-43.57991,-52.56305]]],[[[-14.91926,-6.63386],[-14.82771,-8.70814],[-13.48367,-36.6746],[-13.41694,-37.88844],[-13.29655,-40.02846],[-9.34669,-41.00353],[-4.97086,-15.55882],[-13.33271,-8.07391],[-14.91926,-6.63386]]],[[[-14.78497,57.60709],[-7.93366,55.84142],[-6.79943,55.54107],[-6.71944,55.27952],[-6.9734,55.19878],[-7.2471,55.06933],[-7.34464,55.04688],[-7.4033,55.00391],[-7.40004,54.94498],[-7.44404,54.9403],[-7.4473,54.87003],[-7.47626,54.83084],[-7.54508,54.79401],[-7.54671,54.74606],[-7.64449,54.75265],[-7.75041,54.7103],[-7.83352,54.73854],[-7.93293,54.66603],[-7.70315,54.62077],[-7.8596,54.53671],[-7.99812,54.54427],[-8.04538,54.48941],[-8.179,54.46763],[-8.04555,54.36292],[-7.87101,54.29299],[-7.8596,54.21779],[-7.81397,54.20159],[-7.69501,54.20731],[-7.55812,54.12239],[-7.4799,54.12239],[-7.44567,54.1539],[-7.32834,54.11475],[-7.30553,54.11869],[-7.34005,54.14698],[-7.29157,54.17191],[-7.28017,54.16714],[-7.29687,54.1354],[-7.29493,54.12013],[-7.26316,54.13863],[-7.25012,54.20063],[-7.14908,54.22732],[-7.19145,54.31296],[-7.02034,54.4212],[-6.87775,54.34682],[-6.85179,54.29176],[-6.81583,54.22791],[-6.74575,54.18788],[-6.70175,54.20218],[-6.6382,54.17071],[-6.66264,54.0666],[-6.62842,54.03503],[-6.47849,54.06947],[-6.36605,54.07234],[-6.36279,54.11248],[-6.32694,54.09337],[-6.29003,54.11278],[-6.26218,54.09785],[-5.83481,53.87749],[-5.37267,53.63269],[-5.79914,52.03902],[-6.81839,49.7273],[-3.06097,49.47231],[-2.9511,49.31141],[-2.56423,49.22209],[-2.5234,48.91595],[-2.00491,48.86706],[-1.83944,49.23037],[-2.09454,49.46288],[-2.02963,49.91866],[1.17405,50.74239],[2.18458,51.52087],[2.56575,51.85301],[-0.3751,61.32236],[-14.78497,57.60709]]],[[[-5.40134,36.14896],[-5.39074,36.10278],[-5.36503,36.06205],[-5.32837,36.05935],[-5.3004,36.07439],[-5.28217,36.09907],[-5.27801,36.14942],[-5.33822,36.15272],[-5.34536,36.15501],[-5.36494,36.15496],[-5.38545,36.15481],[-5.40134,36.14896]]],[[[32.74412,34.43926],[33.26744,34.49942],[33.0138,34.64424],[32.96968,34.64046],[32.96718,34.63446],[32.95891,34.62919],[32.95323,34.64075],[32.95471,34.64528],[32.94976,34.65204],[32.94796,34.6587],[32.95325,34.66462],[32.97079,34.66112],[32.97736,34.65277],[32.99014,34.65518],[32.98668,34.67268],[32.99135,34.68061],[32.95539,34.68471],[32.94683,34.67907],[32.94379,34.67111],[32.93693,34.67027],[32.93449,34.66241],[32.92807,34.66736],[32.93043,34.67091],[32.91398,34.67343],[32.9068,34.66102],[32.86167,34.68734],[32.86014,34.70585],[32.82717,34.70622],[32.79433,34.67883],[32.76136,34.68318],[32.75515,34.64985],[32.74412,34.43926]]],[[[33.67678,35.03866],[33.69938,35.03123],[33.69731,35.01754],[33.71514,35.00294],[33.70639,34.99303],[33.70575,34.97947],[33.83531,34.73974],[33.98684,34.76642],[33.90075,34.96623],[33.86432,34.97592],[33.84811,34.97075],[33.83505,34.98108],[33.85621,34.98956],[33.85891,35.001],[33.85216,35.00579],[33.84045,35.00616],[33.82875,35.01685],[33.83055,35.02865],[33.81524,35.04192],[33.8012,35.04786],[33.82051,35.0667],[33.8355,35.05777],[33.85261,35.0574],[33.88367,35.07877],[33.89485,35.06873],[33.90247,35.07686],[33.91299,35.07579],[33.91789,35.08688],[33.89853,35.11377],[33.88737,35.11408],[33.88943,35.12007],[33.88561,35.12449],[33.87224,35.12293],[33.87622,35.10457],[33.87097,35.09389],[33.87479,35.08881],[33.8541,35.07201],[33.84168,35.06823],[33.82067,35.07826],[33.78581,35.05104],[33.76106,35.04253],[33.73824,35.05321],[33.71482,35.03722],[33.70209,35.04882],[33.7161,35.07279],[33.70861,35.07644],[33.69095,35.06237],[33.68474,35.06602],[33.67742,35.05963],[33.67678,35.03866]],[[33.7343,35.01178],[33.73781,35.02181],[33.74265,35.02329],[33.74983,35.02274],[33.7492,35.01319],[33.74144,35.01053],[33.7343,35.01178]],[[33.75682,34.99916],[33.75994,35.00113],[33.77312,34.9976],[33.77553,34.99518],[33.78516,34.99582],[33.79191,34.98914],[33.78917,34.98854],[33.78571,34.98951],[33.78318,34.98699],[33.78149,34.98854],[33.77843,34.988],[33.7778,34.98981],[33.76738,34.99188],[33.76605,34.99543],[33.75682,34.99916]]],[[[70.64391,-7.71751],[72.09053,-7.71938],[73.19616,-7.72081],[73.19718,-6.94577],[73.19979,-4.96078],[70.64754,-4.95745],[70.64391,-7.71751]]]]}},{"type":"Feature","properties":{"id":"IT"},"geometry":{"type":"MultiPolygon","coordinates":[[[[6.62803,45.11175],[6.66981,45.02324],[6.74791,45.01939],[6.74519,44.93661],[6.75518,44.89915],[6.90774,44.84322],[6.93499,44.8664],[7.02217,44.82519],[7.00401,44.78782],[7.07484,44.68073],[7.00582,44.69364],[6.95133,44.66264],[6.96042,44.62129],[6.85507,44.53072],[6.86233,44.49834],[6.94504,44.43112],[6.88784,44.42043],[6.89171,44.36637],[6.98221,44.28289],[7.00764,44.23736],[7.16929,44.20352],[7.27827,44.1462],[7.34547,44.14359],[7.36364,44.11882],[7.62155,44.14881],[7.63245,44.17877],[7.68694,44.17487],[7.66878,44.12795],[7.72508,44.07578],[7.6597,44.03009],[7.66848,43.99943],[7.65266,43.9763],[7.60771,43.95772],[7.56858,43.94506],[7.56075,43.89932],[7.51162,43.88301],[7.49355,43.86551],[7.50423,43.84345],[7.5289,43.78887],[7.63035,43.57419],[9.56115,43.20816],[9.86526,42.21008],[9.62656,41.44198],[9.28609,41.32097],[8.80584,41.26173],[7.52234,41.54558],[7.89009,38.19924],[11.2718,37.6713],[12.02012,35.25036],[12.80065,35.1178],[13.4634,35.88474],[14.02721,36.53141],[15.10171,36.26215],[18.75365,39.82496],[18.83516,40.36999],[16.15283,42.18525],[13.12821,44.48877],[13.05142,45.33128],[13.45644,45.59464],[13.6076,45.64761],[13.7198,45.59352],[13.74587,45.59811],[13.78445,45.5825],[13.84106,45.58185],[13.86771,45.59898],[13.8695,45.60835],[13.9191,45.6322],[13.87933,45.65207],[13.83422,45.68703],[13.83332,45.70855],[13.8235,45.7176],[13.66986,45.79955],[13.59784,45.8072],[13.58858,45.83503],[13.57563,45.8425],[13.58644,45.88173],[13.59565,45.89446],[13.60857,45.89907],[13.61931,45.91782],[13.63815,45.93607],[13.6329,45.94894],[13.64307,45.98326],[13.63458,45.98947],[13.62074,45.98388],[13.58903,45.99009],[13.56759,45.96991],[13.52963,45.96588],[13.50104,45.98078],[13.47474,46.00546],[13.49702,46.01832],[13.50998,46.04498],[13.49568,46.04839],[13.50104,46.05986],[13.57072,46.09022],[13.64053,46.13587],[13.66472,46.17392],[13.64451,46.18966],[13.56682,46.18703],[13.56114,46.2054],[13.47587,46.22725],[13.42218,46.20758],[13.37671,46.29668],[13.44808,46.33507],[13.43418,46.35992],[13.47019,46.3621],[13.5763,46.40915],[13.5763,46.42613],[13.59777,46.44137],[13.68684,46.43881],[13.7148,46.5222],[13.64088,46.53438],[13.27627,46.56059],[12.94445,46.60401],[12.59992,46.6595],[12.38708,46.71529],[12.27591,46.88651],[12.2006,46.88854],[12.11675,47.01241],[12.21781,47.03996],[12.19254,47.09331],[11.74789,46.98484],[11.50739,47.00644],[11.33355,46.99862],[11.10618,46.92966],[11.00764,46.76896],[10.72974,46.78972],[10.75753,46.82258],[10.66405,46.87614],[10.54783,46.84505],[10.47197,46.85698],[10.38659,46.67847],[10.40475,46.63671],[10.44686,46.64162],[10.49375,46.62049],[10.46136,46.53164],[10.25309,46.57432],[10.23674,46.63484],[10.10307,46.61003],[10.03715,46.44479],[10.165,46.41051],[10.10506,46.3372],[10.17862,46.25626],[10.14439,46.22992],[10.07055,46.21668],[9.95249,46.38045],[9.73086,46.35071],[9.71273,46.29266],[9.57015,46.2958],[9.46117,46.37481],[9.45936,46.50873],[9.40487,46.46621],[9.36128,46.5081],[9.28136,46.49685],[9.25502,46.43743],[9.29226,46.32717],[9.24503,46.23616],[9.01618,46.04928],[8.99257,45.9698],[9.09065,45.89906],[9.06642,45.8761],[9.04546,45.84968],[9.04059,45.8464],[9.03505,45.83976],[9.03793,45.83548],[9.03279,45.82865],[9.0298,45.82127],[9.00324,45.82055],[8.99663,45.83466],[8.9621,45.83707],[8.94737,45.84285],[8.91129,45.8388],[8.93504,45.86245],[8.94372,45.86587],[8.93649,45.86775],[8.88904,45.95465],[8.86688,45.96135],[8.85121,45.97239],[8.8319,45.9879],[8.79362,45.99207],[8.78585,45.98973],[8.79414,46.00913],[8.85617,46.0748],[8.80778,46.10085],[8.75697,46.10395],[8.62242,46.12112],[8.45032,46.26869],[8.46317,46.43712],[8.42464,46.46367],[8.30648,46.41587],[8.31162,46.38044],[8.08814,46.26692],[8.16866,46.17817],[8.11383,46.11577],[8.02906,46.10331],[7.98881,45.99867],[7.9049,45.99945],[7.85949,45.91485],[7.56343,45.97421],[7.10685,45.85653],[7.04151,45.92435],[6.95315,45.85163],[6.80785,45.83265],[6.80785,45.71864],[6.98948,45.63869],[7.00037,45.509],[7.18019,45.40071],[7.10572,45.32924],[7.13115,45.25386],[7.07074,45.21228],[6.96706,45.20841],[6.85144,45.13226],[6.7697,45.16044],[6.62803,45.11175]],[[12.40415,43.95485],[12.41414,43.95273],[12.42005,43.9578],[12.43662,43.95698],[12.44684,43.96597],[12.46205,43.97463],[12.47853,43.98052],[12.49406,43.98492],[12.50678,43.99113],[12.51463,43.99122],[12.5154,43.98508],[12.51064,43.98165],[12.51109,43.97201],[12.50622,43.97131],[12.50875,43.96198],[12.50655,43.95796],[12.51427,43.94897],[12.51553,43.94096],[12.50496,43.93017],[12.50269,43.92363],[12.49724,43.92248],[12.49247,43.91774],[12.49429,43.90973],[12.48771,43.89706],[12.45648,43.89369],[12.44184,43.90498],[12.41641,43.89991],[12.40935,43.9024],[12.41233,43.90956],[12.40733,43.92379],[12.41551,43.92984],[12.41165,43.93769],[12.40506,43.94325],[12.40415,43.95485]],[[12.44582,41.90194],[12.44815,41.90326],[12.44984,41.90545],[12.45091,41.90625],[12.45543,41.90738],[12.45561,41.90629],[12.45762,41.9058],[12.45755,41.9033],[12.45826,41.90281],[12.45834,41.90174],[12.4577,41.90115],[12.45691,41.90125],[12.45626,41.90172],[12.45435,41.90143],[12.45446,41.90028],[12.45181,41.90056],[12.44834,41.90095],[12.44582,41.90194]]],[[[8.95861,45.96485],[8.97604,45.96151],[8.97741,45.98317],[8.96668,45.98436],[8.95861,45.96485]]]]}},{"type":"Feature","properties":{"id":"KG"},"geometry":{"type":"Polygon","coordinates":[[[69.26938,39.8127],[69.3594,39.52516],[69.68677,39.59281],[69.87491,39.53882],[70.11111,39.58223],[70.2869,39.53141],[70.44757,39.60128],[70.64087,39.58792],[70.7854,39.38933],[71.06418,39.41586],[71.08752,39.50704],[71.49814,39.61397],[71.55856,39.57588],[71.5517,39.45722],[71.62688,39.44056],[71.76816,39.45456],[71.80164,39.40631],[71.7522,39.32031],[71.79202,39.27355],[71.90601,39.27674],[72.04059,39.36704],[72.09689,39.26823],[72.17242,39.2661],[72.23834,39.17248],[72.33173,39.33093],[72.62027,39.39696],[72.85934,39.35116],[73.18454,39.35536],[73.31912,39.38615],[73.45096,39.46677],[73.59831,39.46425],[73.87018,39.47879],[73.94683,39.60733],[73.92354,39.69565],[73.9051,39.75073],[73.83006,39.76136],[73.97049,40.04378],[74.25533,40.13191],[74.35063,40.09742],[74.69875,40.34668],[74.85996,40.32857],[74.78168,40.44886],[74.82013,40.52197],[75.08243,40.43945],[75.22834,40.45382],[75.5854,40.66874],[75.69663,40.28642],[75.91361,40.2948],[75.96168,40.38064],[76.33659,40.3482],[76.5261,40.46114],[76.75681,40.95354],[76.99302,41.0696],[77.28004,41.0033],[77.3693,41.0375],[77.52723,41.00227],[77.76206,41.01574],[77.81287,41.14307],[78.12873,41.23091],[78.15757,41.38565],[78.3732,41.39603],[79.92977,42.04113],[80.17842,42.03211],[80.17807,42.21166],[79.97364,42.42816],[79.52921,42.44778],[79.19763,42.804],[78.91502,42.76839],[78.48469,42.89649],[75.82823,42.94848],[75.72174,42.79672],[75.29966,42.86183],[75.22619,42.85528],[74.88756,42.98612],[74.75,42.99029],[74.70331,43.02519],[74.64615,43.05881],[74.57491,43.13702],[74.22489,43.24657],[73.55634,43.03071],[73.50992,42.82356],[73.44393,42.43098],[71.88792,42.83578],[71.62405,42.76613],[71.53272,42.8014],[71.2724,42.77853],[71.22785,42.69248],[71.17807,42.67381],[71.15232,42.60486],[70.97717,42.50147],[70.85973,42.30188],[70.94483,42.26238],[71.13263,42.28356],[71.28719,42.18033],[70.69777,41.92554],[70.17682,41.5455],[70.48909,41.40335],[70.67586,41.47953],[70.78572,41.36419],[70.77885,41.24813],[70.86263,41.23833],[70.9615,41.16393],[71.02193,41.19494],[71.11806,41.15359],[71.25813,41.18796],[71.27187,41.11015],[71.34877,41.16807],[71.40198,41.09436],[71.46148,41.13958],[71.43814,41.19644],[71.46688,41.31883],[71.57227,41.29175],[71.6787,41.42111],[71.65914,41.49599],[71.73054,41.54713],[71.71132,41.43012],[71.76625,41.4466],[71.83914,41.3546],[71.91457,41.2982],[71.85964,41.19081],[72.07249,41.11739],[72.10745,41.15483],[72.16433,41.16483],[72.17594,41.15522],[72.14864,41.13363],[72.1792,41.10621],[72.21061,41.05607],[72.17594,41.02377],[72.18339,40.99571],[72.324,41.03381],[72.34026,41.04539],[72.34757,41.06104],[72.36138,41.04384],[72.38511,41.02785],[72.45206,41.03018],[72.48742,40.97136],[72.55109,40.96046],[72.59136,40.86947],[72.68157,40.84942],[72.84291,40.85512],[72.94454,40.8094],[73.01869,40.84681],[73.13267,40.83512],[73.13412,40.79122],[73.0612,40.76678],[72.99133,40.76457],[72.93296,40.73089],[72.8722,40.71111],[72.85372,40.7116],[72.84754,40.67229],[72.80137,40.67856],[72.74866,40.60873],[72.74894,40.59592],[72.75982,40.57273],[72.74862,40.57131],[72.74768,40.58051],[72.73995,40.58409],[72.69579,40.59778],[72.66713,40.59076],[72.66713,40.5219],[72.47795,40.5532],[72.40517,40.61917],[72.34406,40.60144],[72.41714,40.55736],[72.38384,40.51535],[72.41513,40.50856],[72.44191,40.48222],[72.40346,40.4007],[72.24368,40.46091],[72.18648,40.49893],[71.96401,40.31907],[72.05464,40.27586],[71.85002,40.25647],[71.82646,40.21872],[71.73054,40.14818],[71.71719,40.17886],[71.69621,40.18492],[71.70569,40.20391],[71.68386,40.26984],[71.61931,40.26775],[71.61725,40.20615],[71.51549,40.22986],[71.51215,40.26943],[71.4246,40.28619],[71.36663,40.31593],[71.13042,40.34106],[71.05901,40.28765],[70.95789,40.28761],[70.9818,40.22392],[70.80495,40.16813],[70.7928,40.12797],[70.65827,40.0981],[70.65946,39.9878],[70.58912,39.95211],[70.55033,39.96619],[70.47557,39.93216],[70.57384,39.99394],[70.58297,40.00891],[70.01283,40.23288],[69.67001,40.10639],[69.64704,40.12165],[69.57615,40.10524],[69.55555,40.12296],[69.53794,40.11833],[69.53855,40.0887],[69.5057,40.03277],[69.53615,39.93991],[69.43557,39.92877],[69.43134,39.98431],[69.35649,40.01994],[69.26938,39.8127]],[[70.52631,39.86989],[70.53651,39.89155],[70.74189,39.86319],[70.63105,39.77923],[70.59667,39.83542],[70.54998,39.85137],[70.52631,39.86989]],[[71.00236,40.18154],[71.06305,40.1771],[71.12218,40.03052],[71.21139,40.03369],[71.1427,39.95026],[71.23067,39.93581],[71.16101,39.88423],[71.10531,39.91354],[71.04979,39.89808],[71.10501,39.95568],[71.09063,39.99],[71.11668,39.99291],[71.11037,40.01984],[71.01035,40.05481],[71.00236,40.18154]],[[71.71511,39.96348],[71.78838,40.01404],[71.86463,39.98598],[71.84316,39.95582],[71.7504,39.93701],[71.71511,39.96348]]]}},{"type":"Feature","properties":{"id":"KI"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-175.33482,-1.40631],[-175.31804,-7.54825],[-174.18707,-7.54408],[-167.75329,-7.52784],[-156.50903,-7.4975],[-156.4957,-12.32002],[-149.61166,-12.30171],[-149.6249,-7.51261],[-149.65979,5.27712],[-161.06795,5.2462],[-161.05669,1.11722],[-158.62734,1.1296],[-158.62058,-1.35506],[-161.04969,-1.36251],[-175.33482,-1.40631]]],[[[169,-3.5],[178,-3.5],[178,3.9],[169,3.9],[169,-3.5]]]]}},{"type":"Feature","properties":{"id":"NL"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-70.34259,12.92535],[-70.24399,12.38063],[-69.4514,12.18025],[-68.99639,11.79035],[-68.33524,11.78151],[-68.01417,11.77722],[-67.89186,12.4116],[-68.90012,12.62309],[-69.5195,12.75292],[-70.34259,12.92535]]],[[[-63.58819,17.61311],[-63.22932,17.32592],[-63.11114,17.23125],[-62.76692,17.64353],[-63.07669,17.79659],[-62.93924,18.02904],[-63.02323,18.05757],[-63.04039,18.05619],[-63.0579,18.06614],[-63.07759,18.04943],[-63.09686,18.04608],[-63.11096,18.05368],[-63.13584,18.0541],[-63.33064,17.9615],[-63.29212,17.90532],[-63.58819,17.61311]]],[[[2.56575,51.85301],[3.36263,51.37112],[3.38696,51.33436],[3.35847,51.31572],[3.38289,51.27331],[3.41704,51.25933],[3.43488,51.24135],[3.52698,51.2458],[3.51502,51.28697],[3.58939,51.30064],[3.78999,51.25766],[3.78783,51.2151],[3.90125,51.20371],[3.97889,51.22537],[4.01957,51.24504],[4.05165,51.24171],[4.16721,51.29348],[4.24024,51.35371],[4.21923,51.37443],[4.33265,51.37687],[4.34086,51.35738],[4.39292,51.35547],[4.43777,51.36989],[4.38064,51.41965],[4.39747,51.43316],[4.38122,51.44905],[4.47736,51.4778],[4.5388,51.48184],[4.54675,51.47265],[4.52846,51.45002],[4.53521,51.4243],[4.57489,51.4324],[4.65442,51.42352],[4.72935,51.48424],[4.74578,51.48937],[4.77321,51.50529],[4.78803,51.50284],[4.84139,51.4799],[4.82409,51.44736],[4.82946,51.4213],[4.78314,51.43319],[4.76577,51.43046],[4.77229,51.41337],[4.78941,51.41102],[4.84988,51.41502],[4.90016,51.41404],[4.92152,51.39487],[5.00393,51.44406],[5.0106,51.47167],[5.03281,51.48679],[5.04774,51.47022],[5.07891,51.4715],[5.10456,51.43163],[5.07102,51.39469],[5.13105,51.34791],[5.13377,51.31592],[5.16222,51.31035],[5.2002,51.32243],[5.24244,51.30495],[5.22542,51.26888],[5.23814,51.26064],[5.26461,51.26693],[5.29716,51.26104],[5.33886,51.26314],[5.347,51.27502],[5.41672,51.26248],[5.4407,51.28169],[5.46519,51.2849],[5.48476,51.30053],[5.515,51.29462],[5.5569,51.26544],[5.5603,51.22249],[5.65145,51.19788],[5.65528,51.18736],[5.70344,51.1829],[5.74617,51.18928],[5.77735,51.17845],[5.77697,51.1522],[5.82564,51.16753],[5.85508,51.14445],[5.80798,51.11661],[5.8109,51.10861],[5.83226,51.10585],[5.82921,51.09328],[5.79903,51.09371],[5.79835,51.05834],[5.77258,51.06196],[5.75961,51.03113],[5.77688,51.02483],[5.76242,50.99703],[5.71864,50.96092],[5.72875,50.95428],[5.74752,50.96202],[5.75927,50.95601],[5.74644,50.94723],[5.72545,50.92312],[5.72644,50.91167],[5.71626,50.90796],[5.69858,50.91046],[5.67886,50.88142],[5.64504,50.87107],[5.64009,50.84742],[5.65259,50.82309],[5.70118,50.80764],[5.68995,50.79641],[5.70107,50.7827],[5.68091,50.75804],[5.69469,50.75529],[5.72216,50.76398],[5.73904,50.75674],[5.74356,50.7691],[5.76533,50.78159],[5.77513,50.78308],[5.80673,50.7558],[5.84548,50.76542],[5.84888,50.75448],[5.88734,50.77092],[5.89129,50.75125],[5.89132,50.75124],[5.95942,50.7622],[5.97545,50.75441],[6.01976,50.75398],[6.02624,50.77453],[5.97497,50.79992],[5.98404,50.80988],[6.00462,50.80065],[6.02328,50.81694],[6.01921,50.84435],[6.05623,50.8572],[6.05702,50.85179],[6.07431,50.84674],[6.07693,50.86025],[6.08805,50.87223],[6.07486,50.89307],[6.09297,50.92066],[6.01615,50.93367],[6.02697,50.98303],[5.95282,50.98728],[5.90296,50.97356],[5.90493,51.00198],[5.87849,51.01969],[5.86735,51.05182],[5.9134,51.06736],[5.9541,51.03496],[5.98292,51.07469],[6.16706,51.15677],[6.17384,51.19589],[6.07889,51.17038],[6.07889,51.24432],[6.16977,51.33169],[6.22674,51.36135],[6.22641,51.39948],[6.20654,51.40049],[6.21724,51.48568],[6.18017,51.54096],[6.09055,51.60564],[6.11759,51.65609],[6.02767,51.6742],[6.04091,51.71821],[5.95003,51.7493],[5.98665,51.76944],[5.94568,51.82786],[5.99848,51.83195],[6.06705,51.86136],[6.10337,51.84829],[6.16902,51.84094],[6.11551,51.89769],[6.15349,51.90439],[6.21443,51.86801],[6.29872,51.86801],[6.30593,51.84998],[6.40704,51.82771],[6.38815,51.87257],[6.47179,51.85395],[6.50231,51.86313],[6.58556,51.89386],[6.68386,51.91861],[6.72319,51.89518],[6.82357,51.96711],[6.83035,51.9905],[6.68128,52.05052],[6.76117,52.11895],[6.83984,52.11728],[6.97189,52.20329],[6.9897,52.2271],[7.03729,52.22695],[7.06365,52.23789],[7.02703,52.27941],[7.07044,52.37805],[7.03417,52.40237],[6.99041,52.47235],[6.94293,52.43597],[6.69507,52.488],[6.71641,52.62905],[6.77307,52.65375],[7.04557,52.63318],[7.07253,52.81083],[7.21694,53.00742],[7.17898,53.13817],[7.22681,53.18165],[7.21679,53.20058],[7.19052,53.31866],[7.00198,53.32672],[6.91025,53.44221],[5.45168,54.20039],[2.56575,51.85301]],[[4.91493,51.4353],[4.91935,51.43634],[4.92227,51.44252],[4.91811,51.44621],[4.92287,51.44741],[4.92811,51.4437],[4.92566,51.44273],[4.92815,51.43856],[4.92879,51.44161],[4.93544,51.44634],[4.94025,51.44193],[4.93416,51.44185],[4.93471,51.43861],[4.94265,51.44003],[4.93986,51.43064],[4.92952,51.42984],[4.92652,51.43329],[4.91493,51.4353]],[[4.93295,51.44945],[4.95244,51.45207],[4.9524,51.45014],[4.93909,51.44632],[4.93295,51.44945]]]]}},{"type":"Feature","properties":{"id":"NO"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-10.71459,70.09565],[-5.93364,70.76368],[-9.68082,72.73731],[-10.71459,70.09565]]],[[[-3.52068,82.6752],[16.4353,73.61229],[33.12005,75.46568],[35.22046,80.57056],[-3.52068,82.6752]]],[[[-0.3751,61.32236],[7.28637,57.35913],[10.40861,58.38489],[10.64958,58.89391],[11.08911,58.98745],[11.15367,59.07862],[11.34459,59.11672],[11.4601,58.99022],[11.45199,58.89604],[11.65732,58.90177],[11.8213,59.24985],[11.69297,59.59442],[11.92112,59.69531],[11.87121,59.86039],[12.15641,59.8926],[12.36317,59.99259],[12.52003,60.13846],[12.59133,60.50559],[12.2277,61.02442],[12.69115,61.06584],[12.86939,61.35427],[12.57707,61.56547],[12.40595,61.57226],[12.14746,61.7147],[12.29187,62.25699],[12.07085,62.6297],[12.19919,63.00104],[11.98529,63.27487],[12.19919,63.47935],[12.14928,63.59373],[12.74105,64.02171],[13.23411,64.09087],[13.98222,64.00953],[14.16051,64.18725],[14.11117,64.46674],[13.64276,64.58402],[14.50926,65.31786],[14.53778,66.12399],[15.05113,66.15572],[15.49318,66.28509],[15.37197,66.48217],[16.35589,67.06419],[16.39154,67.21653],[16.09922,67.4364],[16.12774,67.52106],[16.38441,67.52923],[16.7409,67.91037],[17.30416,68.11591],[17.90787,67.96537],[18.13836,68.20874],[18.1241,68.53721],[18.39503,68.58672],[18.63032,68.50849],[18.97255,68.52416],[19.93508,68.35911],[20.22027,68.48759],[19.95647,68.55546],[20.22027,68.67246],[20.33435,68.80174],[20.28444,68.93283],[20.0695,69.04469],[20.55258,69.06069],[20.72171,69.11874],[21.05775,69.0356],[21.11099,69.10291],[20.98641,69.18809],[21.00732,69.22755],[21.27827,69.31281],[21.63833,69.27485],[22.27276,68.89514],[22.38367,68.71561],[22.53321,68.74393],[23.13064,68.64684],[23.68017,68.70276],[23.781,68.84514],[24.02299,68.81601],[24.18432,68.73936],[24.74898,68.65143],[24.90023,68.55579],[24.93048,68.61102],[25.10189,68.63307],[25.12206,68.78684],[25.42455,68.90328],[25.61613,68.89602],[25.75729,68.99383],[25.69679,69.27039],[25.96904,69.68397],[26.40261,69.91377],[26.64461,69.96565],[27.05802,69.92069],[27.57226,70.06215],[27.95542,70.0965],[27.97558,69.99671],[28.32849,69.88605],[28.36883,69.81658],[29.12697,69.69193],[29.31664,69.47994],[28.8629,69.22395],[28.81248,69.11997],[28.91738,69.04774],[29.0444,69.0119],[29.26623,69.13794],[29.27631,69.2811],[29.97205,69.41623],[30.16363,69.65244],[30.52662,69.54699],[30.95011,69.54699],[30.84095,69.80584],[31.59909,70.16571],[32.07813,72.01005],[18.46509,71.28681],[-0.3751,61.32236]]],[[[2.85578,-54.70531],[3.87126,-54.71036],[3.87947,-54.15611],[2.86398,-54.15099],[2.85578,-54.70531]]]]}},{"type":"Feature","properties":{"id":"NZ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-180,-24.21376],[-179.93224,-45.18423],[-173.00283,-45.20102],[-173.10761,-24.19665],[-180,-24.21376]]],[[[-174.18707,-7.54408],[-174.17993,-10.13616],[-167.75195,-10.12005],[-167.73854,-14.92809],[-171.14262,-14.93704],[-173.13438,-14.94228],[-173.11048,-23.23027],[-167.73129,-23.22266],[-156.46451,-23.21255],[-156.4957,-12.32002],[-156.50903,-7.4975],[-167.75329,-7.52784],[-174.18707,-7.54408]]],[[[164.49803,-50.68404],[169.00308,-53.19756],[179.49541,-50.04657],[179.49541,-36.79303],[169.6687,-29.09191],[169.35326,-30.60259],[164.49803,-50.68404]]]]}},{"type":"Feature","properties":{"id":"OM"},"geometry":{"type":"MultiPolygon","coordinates":[[[[52.00311,19.00083],[52.78009,17.35124],[52.74267,17.29519],[52.81185,17.28568],[53.09917,16.67084],[53.32998,16.16312],[56.66759,17.24021],[61.45114,22.55394],[56.86325,25.03856],[56.3227,24.97284],[56.34873,24.93205],[56.30269,24.88334],[56.20568,24.85063],[56.20062,24.78565],[56.13684,24.73699],[56.06128,24.74457],[56.03535,24.81161],[55.97836,24.87673],[55.97467,24.89639],[56.05106,24.87461],[56.05715,24.95727],[55.96316,25.00857],[55.90849,24.96771],[55.85094,24.96858],[55.81116,24.9116],[55.81348,24.80102],[55.83408,24.77858],[55.83271,24.68567],[55.76461,24.5287],[55.83271,24.41521],[55.83395,24.32776],[55.80747,24.31069],[55.79145,24.27914],[55.76781,24.26209],[55.75939,24.26114],[55.75382,24.2466],[55.75257,24.23466],[55.76558,24.23227],[55.77658,24.23476],[55.83367,24.20193],[55.95472,24.2172],[56.01799,24.07426],[55.8308,24.01633],[55.73301,24.05994],[55.48677,23.94946],[55.57358,23.669],[55.22634,23.10378],[55.2137,22.71065],[55.66469,21.99658],[54.99756,20.00083],[52.00311,19.00083]]],[[[55.81777,26.18798],[56.08666,26.05038],[56.15498,26.06828],[56.19334,25.9795],[56.13963,25.82765],[56.17416,25.77239],[56.13579,25.73524],[56.14826,25.66351],[56.18363,25.65508],[56.20473,25.61119],[56.25365,25.60211],[56.26636,25.60643],[56.25341,25.61443],[56.26534,25.62825],[56.82555,25.7713],[56.79239,26.41236],[56.68954,26.76645],[56.2644,26.58649],[55.81777,26.18798]]],[[[56.20838,25.25668],[56.20872,25.24104],[56.24341,25.22867],[56.27628,25.23404],[56.34438,25.26653],[56.35172,25.30681],[56.3111,25.30107],[56.3005,25.31815],[56.26062,25.33108],[56.23362,25.31253],[56.25008,25.28843],[56.24465,25.27505],[56.20838,25.25668]],[[56.27086,25.26128],[56.2716,25.27916],[56.28102,25.28486],[56.29379,25.2754],[56.28423,25.26344],[56.27086,25.26128]]]]}},{"type":"Feature","properties":{"id":"RU"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-179.99933,64.74703],[-172.76104,63.77445],[-169.03888,65.48473],[-168.95635,65.98512],[-168.25765,71.99091],[-179.9843,71.90735],[-179.99933,64.74703]]],[[[18.57853,55.25302],[19.64312,54.45423],[19.8038,54.44203],[20.63871,54.3706],[21.41123,54.32395],[22.79705,54.36264],[22.7253,54.41732],[22.70208,54.45312],[22.67788,54.532],[22.71293,54.56454],[22.68021,54.58486],[22.7522,54.63525],[22.74225,54.64339],[22.75467,54.6483],[22.73397,54.66604],[22.73631,54.72952],[22.87317,54.79492],[22.85083,54.88711],[22.76422,54.92521],[22.68723,54.9811],[22.65451,54.97037],[22.60075,55.01863],[22.58907,55.07085],[22.47688,55.04408],[22.31562,55.0655],[22.14267,55.05345],[22.11697,55.02131],[22.06087,55.02935],[22.02582,55.05078],[22.03984,55.07888],[21.99543,55.08691],[21.96505,55.07353],[21.85521,55.09493],[21.64954,55.1791],[21.55605,55.20311],[21.51095,55.18507],[21.46766,55.21115],[21.38446,55.29348],[21.35465,55.28427],[21.26425,55.24456],[20.95181,55.27994],[20.60454,55.40986],[18.57853,55.25302]]],[[[26.32936,60.00121],[26.90044,59.63819],[27.85643,59.58538],[28.04187,59.47017],[28.19061,59.39962],[28.21137,59.38058],[28.20537,59.36491],[28.19284,59.35791],[28.14215,59.28934],[28.00689,59.28351],[27.90911,59.24353],[27.87978,59.18097],[27.80482,59.1116],[27.74429,58.98351],[27.36366,58.78381],[27.55489,58.39525],[27.48541,58.22615],[27.62393,58.09462],[27.67282,57.92627],[27.81841,57.89244],[27.78526,57.83963],[27.56689,57.83356],[27.50171,57.78842],[27.52615,57.72843],[27.3746,57.66834],[27.40393,57.62125],[27.31919,57.57672],[27.34698,57.52242],[27.56832,57.53728],[27.52453,57.42826],[27.86101,57.29402],[27.66511,56.83921],[27.86101,56.88204],[28.04768,56.59004],[28.13526,56.57989],[28.10069,56.524],[28.19057,56.44637],[28.16599,56.37806],[28.23716,56.27588],[28.15217,56.16964],[28.30571,56.06035],[28.36888,56.05805],[28.37987,56.11399],[28.43068,56.09407],[28.5529,56.11705],[28.68337,56.10173],[28.63668,56.07262],[28.73418,55.97131],[29.08299,56.03427],[29.21717,55.98971],[29.44692,55.95978],[29.3604,55.75862],[29.51283,55.70294],[29.61446,55.77716],[29.80672,55.79569],[29.97975,55.87281],[30.12136,55.8358],[30.27776,55.86819],[30.30987,55.83592],[30.48257,55.81066],[30.51346,55.78982],[30.51037,55.76568],[30.63344,55.73079],[30.67464,55.64176],[30.72957,55.66268],[30.7845,55.58514],[30.86003,55.63169],[30.93419,55.6185],[30.95204,55.50667],[30.90123,55.46621],[30.93144,55.3914],[30.8257,55.3313],[30.81946,55.27931],[30.87944,55.28223],[30.97369,55.17134],[31.02071,55.06167],[31.00972,55.02783],[30.94243,55.03964],[30.9081,55.02232],[30.95754,54.98609],[30.93144,54.9585],[30.81759,54.94064],[30.8264,54.90062],[30.75165,54.80699],[30.95479,54.74346],[30.97127,54.71967],[31.0262,54.70698],[30.98226,54.68872],[30.99187,54.67046],[31.19339,54.66947],[31.21399,54.63113],[31.08543,54.50361],[31.22945,54.46585],[31.3177,54.34067],[31.30791,54.25315],[31.57002,54.14535],[31.89599,54.0837],[31.88744,54.03653],[31.85019,53.91801],[31.77028,53.80015],[31.89137,53.78099],[32.12621,53.81586],[32.36663,53.7166],[32.45717,53.74039],[32.50112,53.68594],[32.40499,53.6656],[32.47777,53.5548],[32.74968,53.45597],[32.73257,53.33494],[32.51725,53.28431],[32.40773,53.18856],[32.15368,53.07594],[31.82373,53.10042],[31.787,53.18033],[31.62496,53.22886],[31.56316,53.19432],[31.40523,53.21406],[31.36403,53.13504],[31.3915,53.09712],[31.33519,53.08805],[31.32283,53.04101],[31.24147,53.031],[31.35667,52.97854],[31.592,52.79011],[31.57277,52.71613],[31.50406,52.69707],[31.63869,52.55361],[31.56316,52.51518],[31.61397,52.48843],[31.62084,52.33849],[31.57971,52.32146],[31.70735,52.26711],[31.6895,52.1973],[31.77877,52.18636],[31.7822,52.11406],[31.81722,52.09955],[31.85018,52.11305],[31.96141,52.08015],[31.92159,52.05144],[32.08813,52.03319],[32.23331,52.08085],[32.2777,52.10266],[32.34044,52.1434],[32.33083,52.23685],[32.38988,52.24946],[32.3528,52.32842],[32.54781,52.32423],[32.69475,52.25535],[32.85405,52.27888],[32.89937,52.2461],[33.18913,52.3754],[33.51323,52.35779],[33.48027,52.31499],[33.55718,52.30324],[33.78789,52.37204],[34.05239,52.20132],[34.11199,52.14087],[34.09413,52.00835],[34.41136,51.82793],[34.42922,51.72852],[34.07765,51.67065],[34.17599,51.63253],[34.30562,51.5205],[34.22048,51.4187],[34.33446,51.363],[34.23009,51.26429],[34.31661,51.23936],[34.38802,51.2746],[34.6613,51.25053],[34.6874,51.18],[34.82472,51.17483],[34.97304,51.2342],[35.14058,51.23162],[35.12685,51.16191],[35.20375,51.04723],[35.31774,51.08434],[35.40837,51.04119],[35.32598,50.94524],[35.39307,50.92145],[35.41367,50.80227],[35.47704,50.77274],[35.48116,50.66405],[35.39464,50.64751],[35.47463,50.49247],[35.58003,50.45117],[35.61711,50.35707],[35.73659,50.35489],[35.80388,50.41356],[35.8926,50.43829],[36.06893,50.45205],[36.20763,50.3943],[36.30101,50.29088],[36.47817,50.31457],[36.58371,50.28563],[36.56655,50.2413],[36.64571,50.218],[36.69377,50.26982],[36.91762,50.34963],[37.08468,50.34935],[37.48204,50.46079],[37.47243,50.36277],[37.62486,50.29966],[37.62879,50.24481],[37.61113,50.21976],[37.75807,50.07896],[37.79515,50.08425],[37.90776,50.04194],[38.02999,49.94482],[38.02999,49.90592],[38.21675,49.98104],[38.18517,50.08161],[38.32524,50.08866],[38.35408,50.00664],[38.65688,49.97176],[38.68677,50.00904],[38.73311,49.90238],[38.90477,49.86787],[38.9391,49.79524],[39.1808,49.88911],[39.27968,49.75976],[39.44496,49.76067],[39.59142,49.73758],[39.65047,49.61761],[39.84548,49.56064],[40.13249,49.61672],[40.16683,49.56865],[40.03636,49.52321],[40.03087,49.45452],[40.1141,49.38798],[40.14912,49.37681],[40.18331,49.34996],[40.22176,49.25683],[40.01988,49.1761],[39.93437,49.05709],[39.6836,49.05121],[39.6683,48.99454],[39.71353,48.98959],[39.72649,48.9754],[39.74874,48.98675],[39.78368,48.91596],[39.98967,48.86901],[40.03636,48.91957],[40.08168,48.87443],[39.97182,48.79398],[39.79466,48.83739],[39.73104,48.7325],[39.71765,48.68673],[39.67226,48.59368],[39.79764,48.58668],[39.84548,48.57821],[39.86196,48.46633],[39.88794,48.44226],[39.94847,48.35055],[39.84136,48.33321],[39.84273,48.30947],[39.90041,48.3049],[39.91465,48.26743],[39.95248,48.29972],[39.9693,48.29904],[39.97325,48.31399],[39.99241,48.31768],[40.00752,48.22445],[39.94847,48.22811],[39.83724,48.06501],[39.88256,48.04482],[39.77544,48.04206],[39.82213,47.96396],[39.73935,47.82876],[38.87979,47.87719],[38.79628,47.81109],[38.76379,47.69346],[38.35062,47.61631],[38.28679,47.53552],[38.28954,47.39255],[38.22225,47.30788],[38.33074,47.30508],[38.32112,47.2585],[38.23049,47.2324],[38.22955,47.12069],[38.3384,46.98085],[38.12112,46.86078],[37.62608,46.82615],[35.23066,45.79231],[34.96015,45.75634],[34.79905,45.81009],[34.80153,45.90047],[34.75479,45.90705],[34.66679,45.97136],[34.60861,45.99347],[34.55889,45.99347],[34.52011,45.95097],[34.48729,45.94267],[34.44155,45.95995],[34.41221,46.00245],[34.33912,46.06114],[34.25111,46.0532],[34.181,46.06804],[34.12929,46.10494],[34.07311,46.11769],[34.05272,46.10838],[33.91549,46.15938],[33.85234,46.19863],[33.79715,46.20482],[33.74047,46.18555],[33.646,46.23028],[33.61517,46.22615],[33.63854,46.14147],[33.61467,46.13561],[33.57318,46.10317],[33.59087,46.06013],[33.54017,46.0123],[31.62627,45.50633],[32.99857,44.48323],[33.66142,43.9825],[36.61884,44.89556],[39.81147,43.06294],[40.0078,43.38551],[40.00853,43.40578],[40.01552,43.42025],[40.01007,43.42411],[40.03312,43.44262],[40.04445,43.47776],[40.10657,43.57344],[40.65957,43.56212],[41.64935,43.22331],[42.40563,43.23226],[42.66667,43.13917],[42.75889,43.19651],[43.03322,43.08883],[43.0419,43.02413],[43.81453,42.74297],[43.73119,42.62043],[43.95517,42.55396],[44.54202,42.75699],[44.70002,42.74679],[44.80941,42.61277],[44.88754,42.74934],[45.15318,42.70598],[45.36501,42.55268],[45.78692,42.48358],[45.61676,42.20768],[46.42738,41.91323],[46.5332,41.87389],[46.58924,41.80547],[46.75269,41.8623],[46.8134,41.76252],[47.00955,41.63583],[46.99554,41.59743],[47.03757,41.55434],[47.10762,41.59044],[47.34579,41.27884],[47.49004,41.26366],[47.54504,41.20275],[47.62288,41.22969],[47.75831,41.19455],[47.87973,41.21798],[48.07587,41.49957],[48.22064,41.51472],[48.2878,41.56221],[48.40277,41.60441],[48.42301,41.65444],[48.55078,41.77917],[48.5867,41.84306],[48.80971,41.95365],[49.2134,44.84989],[49.88945,46.04554],[49.32259,46.26944],[49.16518,46.38542],[48.54988,46.56267],[48.51142,46.69268],[49.01136,46.72716],[48.52326,47.4102],[48.45173,47.40818],[48.15348,47.74545],[47.64973,47.76559],[47.41689,47.83687],[47.38731,47.68176],[47.12107,47.83687],[47.11516,48.27188],[46.49011,48.43019],[46.78392,48.95352],[46.91104,48.99715],[47.01458,49.07085],[47.04416,49.17152],[46.98795,49.23531],[46.78398,49.34026],[46.9078,49.86707],[47.18319,49.93721],[47.34589,50.09308],[47.30448,50.30894],[47.58551,50.47867],[48.10044,50.09242],[48.24519,49.86099],[48.42564,49.82283],[48.68352,49.89546],[48.90782,50.02281],[48.57946,50.63278],[48.86936,50.61589],[49.12673,50.78639],[49.41959,50.85927],[49.39001,51.09396],[49.76866,51.11067],[49.97277,51.2405],[50.26859,51.28677],[50.59695,51.61859],[51.26254,51.68466],[51.301,51.48799],[51.77431,51.49536],[51.8246,51.67916],[52.36119,51.74161],[52.54329,51.48444],[53.46165,51.49445],[53.69299,51.23466],[54.12248,51.11542],[54.46331,50.85554],[54.41894,50.61214],[54.55797,50.52006],[54.71476,50.61214],[54.56685,51.01958],[54.72067,51.03261],[55.67774,50.54508],[56.11398,50.7471],[56.17906,50.93204],[57.17302,51.11253],[57.44221,50.88354],[57.74986,50.93017],[57.75578,51.13852],[58.3208,51.15151],[58.87974,50.70852],[59.48928,50.64216],[59.51886,50.49937],[59.81172,50.54451],[60.01288,50.8163],[60.17262,50.83312],[60.31914,50.67705],[60.81833,50.6629],[61.4431,50.80679],[61.56889,51.23679],[61.6813,51.25716],[61.55114,51.32746],[61.50677,51.40687],[60.95655,51.48615],[60.92401,51.61124],[60.5424,51.61675],[60.36787,51.66815],[60.50986,51.7964],[60.09867,51.87135],[59.99809,51.98263],[60.19925,51.99173],[60.48915,52.15175],[60.72581,52.15538],[60.78201,52.22067],[61.05417,52.35096],[60.98021,52.50068],[60.84709,52.52228],[60.84118,52.63912],[60.71693,52.66245],[60.71989,52.75923],[61.05842,52.92217],[61.23462,53.03227],[62.0422,52.96105],[62.12799,52.99133],[62.14574,53.09626],[61.19024,53.30536],[61.14291,53.41481],[61.29082,53.50992],[61.37957,53.45887],[61.57185,53.50112],[61.55706,53.57144],[60.90626,53.62937],[61.22574,53.80268],[61.14283,53.90063],[60.99796,53.93699],[61.26863,53.92797],[61.3706,54.08464],[61.47603,54.08048],[61.56941,53.95703],[61.65318,54.02445],[62.03913,53.94768],[62.00966,54.04134],[62.38535,54.03961],[62.45931,53.90737],[62.56876,53.94047],[62.58651,54.05871],[63.80604,54.27079],[63.91224,54.20013],[64.02715,54.22679],[63.97686,54.29763],[64.97216,54.4212],[65.11033,54.33028],[65.24663,54.35721],[65.20174,54.55216],[68.21308,54.98645],[68.26661,55.09226],[68.19206,55.18823],[68.90865,55.38148],[69.34224,55.36344],[69.74917,55.35545],[70.19179,55.1476],[70.76493,55.3027],[70.96009,55.10558],[71.08288,54.71253],[71.24185,54.64965],[71.08706,54.33376],[71.10379,54.13326],[71.96141,54.17736],[72.17477,54.36303],[72.43415,53.92685],[72.71026,54.1161],[73.37963,53.96132],[73.74778,54.07194],[73.68921,53.86522],[73.25412,53.61532],[73.39218,53.44623],[75.07405,53.80831],[75.43398,53.98652],[75.3668,54.07439],[76.91052,54.4677],[76.82266,54.1798],[76.44076,54.16017],[76.54243,53.99329],[77.90383,53.29807],[79.11255,52.01171],[80.08138,50.77658],[80.4127,50.95581],[80.44819,51.20855],[80.80318,51.28262],[81.16999,51.15662],[81.06091,50.94833],[81.41248,50.97524],[81.46581,50.77658],[81.94999,50.79307],[82.55443,50.75412],[83.14607,51.00796],[83.8442,50.87375],[84.29385,50.27257],[84.99198,50.06793],[85.24047,49.60239],[86.18709,49.50259],[86.63674,49.80136],[86.79056,49.74787],[86.61307,49.60239],[86.82606,49.51796],[87.03071,49.25142],[87.31465,49.23603],[87.28386,49.11626],[87.478,49.07403],[87.48983,49.13794],[87.81333,49.17354],[87.98977,49.18147],[88.15543,49.30314],[88.17223,49.46934],[88.42449,49.48821],[88.82499,49.44808],[89.70687,49.72535],[89.59711,49.90851],[91.86048,50.73734],[92.07173,50.69585],[92.44714,50.78762],[93.01109,50.79001],[92.99595,50.63183],[94.30823,50.57498],[94.39258,50.22193],[94.49477,50.17832],[94.6121,50.04239],[94.97166,50.04725],[95.02465,49.96941],[95.74757,49.97915],[95.80056,50.04239],[96.97388,49.88413],[97.24639,49.74737],[97.56811,49.84265],[97.56432,49.92801],[97.76871,49.99861],[97.85197,49.91339],[98.29481,50.33561],[98.31373,50.4996],[98.06393,50.61262],[97.9693,50.78044],[98.01472,50.86652],[97.83305,51.00248],[98.05257,51.46696],[98.22053,51.46579],[98.33222,51.71832],[98.74142,51.8637],[98.87768,52.14563],[99.27888,51.96876],[99.75578,51.90108],[99.89203,51.74903],[100.61116,51.73028],[101.39085,51.45753],[101.5044,51.50467],[102.14032,51.35566],[102.32194,50.67982],[102.71178,50.38873],[103.70343,50.13952],[105.32528,50.4648],[106.05562,50.40582],[106.07865,50.33474],[106.47156,50.31909],[106.49628,50.32436],[106.51122,50.34408],[106.58373,50.34044],[106.80326,50.30177],[107.00007,50.1977],[107.1174,50.04239],[107.36407,49.97612],[107.96116,49.93191],[107.95387,49.66659],[108.27937,49.53167],[108.53969,49.32325],[109.18017,49.34709],[109.51325,49.22859],[110.24373,49.16676],[110.39891,49.25083],[110.64493,49.1816],[113.02647,49.60772],[113.20216,49.83356],[114.325,50.28098],[114.9703,50.19254],[115.26068,49.97367],[115.73602,49.87688],[116.22402,50.04477],[116.62502,49.92919],[116.71193,49.83813],[117.07142,49.68482],[117.27597,49.62544],[117.48208,49.62324],[117.82343,49.52696],[118.61623,49.93809],[119.11003,50.00276],[119.27996,50.13348],[119.38598,50.35162],[119.13553,50.37412],[120.10963,51.671],[120.65907,51.93544],[120.77337,52.20805],[120.61346,52.32447],[120.71673,52.54099],[120.46454,52.63811],[120.04049,52.58773],[120.0451,52.7359],[120.85633,53.28499],[121.39213,53.31888],[122.35063,53.49565],[122.85966,53.47395],[123.26989,53.54843],[123.86158,53.49391],[124.46078,53.21881],[125.17522,53.20225],[125.6131,53.07229],[126.558,52.13738],[126.44606,51.98254],[126.68349,51.70607],[126.90369,51.3238],[126.93135,51.0841],[127.14586,50.91152],[127.28165,50.72075],[127.36335,50.58306],[127.28765,50.46585],[127.36009,50.43787],[127.37384,50.28393],[127.60515,50.23503],[127.49299,50.01251],[127.53516,49.84306],[127.83476,49.5748],[128.72896,49.58676],[129.11153,49.36813],[129.23232,49.40353],[129.35317,49.3481],[129.40398,49.44194],[129.50685,49.42398],[129.67598,49.29596],[129.85416,49.11067],[130.2355,48.86741],[130.43232,48.90844],[130.66946,48.88251],[130.52147,48.61745],[130.84462,48.30942],[130.65103,48.10052],[130.90915,47.90623],[130.95985,47.6957],[131.09871,47.6852],[131.2635,47.73325],[131.90448,47.68011],[132.57309,47.71741],[132.66989,47.96491],[134.49516,48.42884],[134.75328,48.36763],[134.67098,48.1564],[134.55508,47.98651],[134.7671,47.72051],[134.50898,47.4812],[134.20016,47.33458],[134.03538,46.75668],[133.84104,46.46681],[133.91496,46.4274],[133.88097,46.25066],[133.68047,46.14697],[133.72695,46.05576],[133.67569,45.9759],[133.60442,45.90053],[133.48457,45.86203],[133.41083,45.57723],[133.19419,45.51913],[133.09279,45.25693],[133.12293,45.1332],[132.96373,45.0212],[132.83978,45.05916],[131.99417,45.2567],[131.86903,45.33636],[131.76532,45.22609],[131.66852,45.2196],[131.68466,45.12374],[131.48415,44.99513],[130.95639,44.85154],[131.1108,44.70266],[131.30365,44.04262],[131.25484,44.03131],[131.23583,43.96085],[131.26176,43.94011],[131.21105,43.82383],[131.19492,43.53047],[131.29402,43.46695],[131.30324,43.39498],[131.19031,43.21385],[131.20414,43.13654],[131.10274,43.04734],[131.135,42.94114],[131.02668,42.91246],[131.02438,42.86518],[130.66524,42.84753],[130.44361,42.76205],[130.40213,42.70788],[130.56576,42.68925],[130.62107,42.58413],[130.55143,42.52158],[130.56835,42.43281],[130.60805,42.4317],[130.64181,42.41422],[130.66367,42.38024],[130.65022,42.32281],[131.95041,41.5445],[140.9182,45.92937],[145.82343,44.571],[145.23667,43.76813],[145.76215,43.50342],[145.97944,43.07828],[169.19658,53.9242],[180,62.52334],[180,71.53642],[157.93051,77.7025],[94.09128,81.82849],[56.59649,82.16972],[35.22046,80.57056],[33.12005,75.46568],[32.07813,72.01005],[31.59909,70.16571],[30.84095,69.80584],[30.95011,69.54699],[30.52662,69.54699],[30.16363,69.65244],[29.97205,69.41623],[29.27631,69.2811],[29.26623,69.13794],[29.0444,69.0119],[28.91738,69.04774],[28.45957,68.91417],[28.78224,68.86696],[28.43941,68.53366],[28.62982,68.19816],[29.34179,68.06655],[29.66955,67.79872],[30.02041,67.67523],[29.91155,67.51507],[28.9839,66.94139],[29.91155,66.13863],[30.16363,65.66935],[29.97205,65.70256],[29.74013,65.64025],[29.84096,65.56945],[29.68972,65.31803],[29.61914,65.23791],[29.8813,65.22101],[29.84096,65.1109],[29.61914,65.05993],[29.68972,64.80789],[30.05271,64.79072],[30.12329,64.64862],[30.01238,64.57513],[30.06279,64.35782],[30.4762,64.25728],[30.55687,64.09036],[30.25437,63.83364],[29.98213,63.75795],[30.49637,63.46666],[31.23244,63.22239],[31.29294,63.09035],[31.58535,62.91642],[31.38369,62.66284],[31.10136,62.43042],[29.01829,61.17448],[28.82816,61.1233],[28.47974,60.93365],[27.77352,60.52722],[27.71177,60.3893],[27.44953,60.22766],[26.32936,60.00121]]]]}}]} \ No newline at end of file diff --git a/core/src/main/resources/com/graphhopper/util/ar.txt b/core/src/main/resources/com/graphhopper/util/ar.txt index 9c6e4740904..5ecb90cb344 100644 --- a/core/src/main/resources/com/graphhopper/util/ar.txt +++ b/core/src/main/resources/com/graphhopper/util/ar.txt @@ -13,6 +13,9 @@ turn_slight_right=إستدر لليمين قليلا turn_sharp_left=اتجه قليلاً لليسار turn_sharp_right=اتجه قليلاً لليمين u_turn=الدوران للإتجاه المعاكس +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=علامة غير معرفة '%1$s' via=من خلال hour_abbr=ساعة @@ -33,14 +36,16 @@ stopover=توقف %1$s roundabout_enter=أدخل الدوران roundabout_exit=في الدوران ، أتخذ مخرج %1$s roundabout_exit_onto=في الدوران ، أتخذ مخرج %1$s من خلال %2$s -total_ascend=%1$s اجمالى صعود -total_descend=%1$s اجمالى نزول -way_contains_ford=انتبه ، هناك مخاضة على الطريق -way_contains_ferry=استخدم العبارة -way_contains_private=طريق خاص -way_contains_toll=طريق برسم عبور -pt_start_trip=ادخل %1$s -pt_end_trip=مغادرة %1$s +web.total_ascend=%1$s اجمالى صعود +web.total_descend=%1$s اجمالى نزول +web.way_contains_ford=انتبه ، هناك مخاضة على الطريق +web.way_contains_ferry=استخدم العبارة +web.way_contains_private=طريق خاص +web.way_contains_toll=طريق برسم عبور +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=انتقل الى %1$s web.start_label=البداية web.intermediate_label=نقطة عبور @@ -50,8 +55,18 @@ web.set_intermediate=ادخل نقطة عبور web.set_end=ادخل نقطة وصول web.center_map=اجعل وسط الخريطة هنا web.show_coords=اظهر الاحداثيات +web.query_osm= web.route=المسار +web.add_to_route= web.delete_from_route=احذف من المسار +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=رمز او علامة web.gh_offline_info= واجهة برمجة التطبيقات (API) بلا اتصال؟ web.refresh_button=اعادة تنشيط @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=خلال web.from_hint=من web.gpx_export_button=GPX التصدير +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=إلي web.route_info=%1$s ستاخذ %2$s web.search_button=ابحث @@ -74,6 +92,8 @@ web.more_button=المزيد web.pt_route_info=وصول سعت %1$s مع %2$s انتقال (%3$s) web.pt_route_info_walking=الوصول عند %1$s مشياً (%2$s) web.locations_not_found=لا يوجد طريق من هذه المنطقة +web.search_with_nominatim= +web.powered_by= web.bike=دراجة web.racingbike=دراجة سباق web.mtb=دراجة جبلية @@ -85,6 +105,9 @@ web.bus=باص web.truck=شاحنة web.staticlink=رابط ثابت web.motorcycle=دراجة نارية +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/ast.txt b/core/src/main/resources/com/graphhopper/util/ast.txt index 212085a831e..0230de4984a 100644 --- a/core/src/main/resources/com/graphhopper/util/ast.txt +++ b/core/src/main/resources/com/graphhopper/util/ast.txt @@ -13,6 +13,9 @@ turn_slight_right=xira llixeramente a la drecha turn_sharp_left=xira fuerte a la izquierda turn_sharp_right=xira fuerte a la drecha u_turn=da vuelta atrás +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=signu indicador desconocíu '%1$s' via=pasando per hour_abbr=h @@ -33,14 +36,16 @@ stopover=pasando per %1$s roundabout_enter=Entra na rotonda roundabout_exit=Na rotonda, toma la salida %1$s roundabout_exit_onto=Na rotonda, toma la salida %1$s haza %2$s -total_ascend=%1$s d'ascensu total -total_descend=%1$s de descensu total -way_contains_ford=hai un vau nel camín -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=xubi a %1$s -pt_end_trip=baxa de %1$s +web.total_ascend=%1$s d'ascensu total +web.total_descend=%1$s de descensu total +web.way_contains_ford=hai un vau nel camín +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=cambia a %1$s web.start_label=Principiu web.intermediate_label=Intermediu @@ -50,8 +55,18 @@ web.set_intermediate=Poner como intermediu web.set_end=Poner como final web.center_map=Centrar el mapa equí web.show_coords=Ver coordenaes +web.query_osm= web.route=Ruta +web.add_to_route= web.delete_from_route=Quitar de la ruta +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Marcador web.gh_offline_info=¿Cayó la API de GraphHopper? web.refresh_button=Refrescar la páxina @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Pasando per web.from_hint=Dende web.gpx_export_button=Esportar GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Fasta web.route_info=%1$s tardaràs %2$s web.search_button=Buscar @@ -74,6 +92,8 @@ web.more_button=más web.pt_route_info=llegada a les %1$s con %2$s tresbordos (%3$s) web.pt_route_info_walking=llegada a les %1$s sólo caminando (%2$s) web.locations_not_found=Nun se puede trazar una ruta. Dalgún llugar nun s'alcuentra nel área +web.search_with_nominatim= +web.powered_by= web.bike=Bicicleta web.racingbike=Bici de carreres web.mtb=Bici de montaña @@ -85,6 +105,9 @@ web.bus=Autobús web.truck=Camión web.staticlink=enllaz estáticu web.motorcycle=Motocicleta +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/az.txt b/core/src/main/resources/com/graphhopper/util/az.txt new file mode 100644 index 00000000000..42634fa45c4 --- /dev/null +++ b/core/src/main/resources/com/graphhopper/util/az.txt @@ -0,0 +1,122 @@ +# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh + +continue=Davam edin +continue_onto=Davam edin - %1$s +finish=Məntəqəyə çatdınız +keep_left=Sol tərəfi tutaraq hərəkət edin +keep_right=Sağ tərəfi tutaraq hərəkət edin +turn_onto=%1$s - %2$s +turn_left=Sola dönün +turn_right=Sağa dönün +turn_slight_left=Yavaşca sola dönün +turn_slight_right=Yavaşca sağa dönün +turn_sharp_left=İti sola dönün +turn_sharp_right=İti sağa dönün +u_turn=Geriyə dönün +toward_destination=%1$s dönün və %2$s istiqamətində hərəkət edin +toward_destination_ref_only=%1$s istiqamətindən %2$s dönün +toward_destination_with_ref=%1$s dönün və %2$s istiqamətində hərəkət edin +unknown=təyin edilməmiş təlimat '%1$s' +via=- +hour_abbr=saat +day_abbr=gün +min_abbr=dəq. +km_abbr=km +m_abbr=m +mi_abbr=mil +ft_abbr=fut +road=yol +off_bike=velosipeddən düşün +cycleway=velosiped yolu +way=yol +small_way=dar yol +paved=örtüklü +unpaved=örtüksüz +stopover=dayanacaq %1$s +roundabout_enter=dairəvi yola dönün +roundabout_exit=%1$s çıxışdan çıxın +roundabout_exit_onto=%1$s çıxışdan %2$s çıxın +web.total_ascend=%1$s yüksəliş +web.total_descend=%1$s eniş +web.way_contains_ford=Yolda keçid var +web.way_contains_ferry=Bərəyə minin +web.way_contains_private=özəl yol +web.way_contains_toll=ödənişli yol +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= +pt_transfer_to=%1$s keçin +web.start_label=Başlanğıc +web.intermediate_label=Ara nöqtə +web.end_label=Son +web.set_start=Başlanğıcını burada təyin edin +web.set_intermediate=Ara nöqtə olaraq təyin edin +web.set_end=Bitmə məntəqəsi olaraq təyin edin +web.center_map=Xəritənin mərkəzini bura təyin edin +web.show_coords=Koordinatları göstər +web.query_osm= +web.route=Marşrut +web.add_to_route= +web.delete_from_route=Marşrutdan sil +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= +web.marker=İşarələyici +web.gh_offline_info=GraphHopper APİ ilə bağlantı qurulmadı? +web.refresh_button=Səhifəni yeniləyin +web.server_status=Server statusu +web.zoom_in=Yaxınlaşdır +web.zoom_out=Uzaqlaşdır +web.drag_to_reorder=Yol nöqtəsini köçürün +web.route_timed_out=Marşrutun hesablanma vaxtı keçdi +web.route_request_failed=Marşrut qurulması baş tutmadı +web.current_location=Hazırki yer +web.searching_location=Məkan axtarışı +web.searching_location_failed=Məkan axtarış baş tutmadı +web.via_hint=vasitəsilə +web.from_hint=-dan +web.gpx_export_button=GPX ixrac +web.gpx_button= +web.hide_button= +web.details_button= +web.to_hint=-dək +web.route_info=məsafə - %1$s vaxt - %2$s +web.search_button=Axtarış +web.more_button=daha +web.pt_route_info=Təxmini çatma vaxtı %1$s transfer - %2$s (%3$s) +web.pt_route_info_walking=Piyada olaraq təxmini çatma vaxtı %1$s ( %2$s) +web.locations_not_found=Marşrut qurulmağı mümkün deyil. Yer müəyyən edilməyib. +web.search_with_nominatim= +web.powered_by= +web.bike=Velosiped +web.racingbike=Yarış velosipedi +web.mtb=Dağ velosipedi +web.car=Avtomobil +web.foot=Piyada +web.hike=Gəzinti +web.small_truck=yüngül yük maşını +web.bus=Avtobus +web.truck=Yük maşını +web.staticlink=Daimi keçid +web.motorcycle=Motosiklet +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= +navigate.in_km_singular=1 kilometrdə +navigate.in_km=%1$s kilometrdə +navigate.in_m=%1$s metrdə +navigate.for_km=%1$s kilometrdə +navigate.then=sonra +navigate.in_mi_singular=1 mildə +navigate.in_mi=%1$s mildə +navigate.in_ft=%1$s futda +navigate.for_mi=%1$s mildə +navigate.warning= +navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/bg.txt b/core/src/main/resources/com/graphhopper/util/bg.txt index e63c3b7561f..d6e4a45e6a7 100644 --- a/core/src/main/resources/com/graphhopper/util/bg.txt +++ b/core/src/main/resources/com/graphhopper/util/bg.txt @@ -1,98 +1,122 @@ # do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh -continue=продължи -continue_onto=продължи по %1$s -finish=Крайна цел! -keep_left= -keep_right= +continue=продължете +continue_onto=продължете по %1$s +finish=Пристигнахте в крайната точка +keep_left=дръжте ляво +keep_right=дръжте дясно turn_onto=%1$s по %2$s -turn_left=завий наляво -turn_right=завий надясно -turn_slight_left=завий леко наляво -turn_slight_right=завий леко надясно -turn_sharp_left=завий рязко наляво -turn_sharp_right=завий рязко надясно -u_turn= -unknown= +turn_left=завийте наляво +turn_right=завийте надясно +turn_slight_left=завийте леко наляво +turn_slight_right=завийте леко надясно +turn_sharp_left=завийте плътно наляво +turn_sharp_right=завийте плътно надясно +u_turn=направете обратен завой +toward_destination=%1$s и карайте към %2$s +toward_destination_ref_only=%1$s към %2$s +toward_destination_with_ref=%1$s и вземете %2$s към %3$s +unknown=знак с неизвестна инструкция „%1$s“ via=през -hour_abbr=ч -day_abbr=д -min_abbr=мин +hour_abbr=ч. +day_abbr=д. +min_abbr=мин. km_abbr=км m_abbr=m mi_abbr=mi ft_abbr=ft road=улица -off_bike=в този участък трябва да слезеш от колелото! +off_bike=в този участък трябва да слезте от велосипеда! cycleway=велоалея way=път -small_way= +small_way=тесен път paved=асфалтиран unpaved=черен -stopover=прехвърляне %1$s -roundabout_enter=Влез в кръговото кръстовище -roundabout_exit=На кръговото кръстовище използвай изход %1$s -roundabout_exit_onto=На кръговото кръстовище използвай изход %1$s по %2$s -total_ascend= -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= -pt_transfer_to= -web.start_label= -web.intermediate_label= -web.end_label= -web.set_start= -web.set_intermediate= -web.set_end= -web.center_map= -web.show_coords= -web.route= -web.delete_from_route= -web.marker= -web.gh_offline_info= -web.refresh_button= -web.server_status= -web.zoom_in= -web.zoom_out= -web.drag_to_reorder= -web.route_timed_out= -web.route_request_failed= -web.current_location= +stopover=отправна точка %1$s +roundabout_enter=Влезте в кръговото кръстовище +roundabout_exit=На кръговото кръстовище вземете изход %1$s +roundabout_exit_onto=На кръговото кръстовище вземете изход %1$s по %2$s +web.total_ascend=%1$s общо изкачване +web.total_descend=%1$s общо слизане +web.way_contains_ford=Внимание, на пътя има брод +web.way_contains_ferry=вземете ферибота +web.way_contains_private=частен път +web.way_contains_toll=път с такса +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= +pt_transfer_to=сменете на %1$s +web.start_label=Начална точка +web.intermediate_label=Междинна точка +web.end_label=Крайна точка +web.set_start=Избиране като начална точка +web.set_intermediate=Избиране като междинна точка +web.set_end=Избиране като крайна точка +web.center_map=Центриране на картата тук +web.show_coords=Показване на координатите +web.query_osm= +web.route=Маршрут +web.add_to_route= +web.delete_from_route=Премахване от маршрута +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= +web.marker=Значка +web.gh_offline_info=ППИ на GraphHopper е без мрежа? +web.refresh_button=Презареждане на страницата +web.server_status=Състояние +web.zoom_in=Увеличаване +web.zoom_out=Намаляване +web.drag_to_reorder=Влачете за преподреждане +web.route_timed_out=Времето за изчисляване на маршрут изтече +web.route_request_failed=Заявката за маршрут е неуспешна +web.current_location=Текущо местоположение web.searching_location= web.searching_location_failed= -web.via_hint=през +web.via_hint=През web.from_hint=От -web.gpx_export_button=GPX експорт +web.gpx_export_button=Изнасяне като GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=До web.route_info=%1$s ще отнемат %2$s web.search_button=Търсене -web.more_button=още -web.pt_route_info= -web.pt_route_info_walking= +web.more_button=повече +web.pt_route_info=пристига в %1$s с %2$s прекачвания (%3$s) +web.pt_route_info_walking=пристига в %1$s само с ходене (%2$s) web.locations_not_found=Не са изтеглени упътвания. Търсеното местоположение не е открито. +web.search_with_nominatim= +web.powered_by= web.bike=Велосипед web.racingbike=Състезателно колело web.mtb=Планинско колело web.car=Автомобил -web.foot=Пеш -web.hike= -web.small_truck= -web.bus= -web.truck= -web.staticlink=статична връзка +web.foot=Пеша +web.hike=Планински преход +web.small_truck=Малък камион +web.bus=Автобус +web.truck=Камион +web.staticlink=Постоянна препратка web.motorcycle=Мотоциклет -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= -navigate.in_mi_singular= -navigate.in_mi= -navigate.in_ft= -navigate.for_mi= -navigate.warning= -navigate.accept_risks_after_warning= +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= +navigate.in_km_singular=След 1 километър +navigate.in_km=След %1$s километра +navigate.in_m=След %1$s метра +navigate.for_km=за %1$s километра +navigate.then=след това +navigate.in_mi_singular=След 1 миля +navigate.in_mi=След %1$s мили +navigate.in_ft=След %1$s фута +navigate.for_mi=за %1$s мили +navigate.warning=Внимание: Приложението е експериментално! Използвайте на свой риск! +navigate.accept_risks_after_warning=Разбирам и се съгласявам +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/bn_BN.txt b/core/src/main/resources/com/graphhopper/util/bn_BN.txt index d914c370757..0a32bfd0828 100644 --- a/core/src/main/resources/com/graphhopper/util/bn_BN.txt +++ b/core/src/main/resources/com/graphhopper/util/bn_BN.txt @@ -1,18 +1,21 @@ # do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh -continue=soja jete thakun -continue_onto=%1$s road a soja jete thakun +continue=সোজা যেতে থাকুন +continue_onto=%1$s সড়কে সোজা যেতে থাকুন finish= -keep_left=bame thakun -keep_right=dane thakun -turn_onto=%1$s %2$s er dike -turn_left=bame jan -turn_right=dane jan -turn_slight_left=ektu bame jan -turn_slight_right=ektu dane jan -turn_sharp_left=bame jan -turn_sharp_right=dane jan -u_turn=u turn nin +keep_left=বামে থাকুন +keep_right=ডানে থাকুন +turn_onto=%1$s %2$s এর দিকে +turn_left=বামে যান +turn_right=ডানে যান +turn_slight_left=একটু বামে যান +turn_slight_right=একটু ডানে যান +turn_sharp_left=বামে যান +turn_sharp_right=ডানে যান +u_turn=ইউটার্ন নিন +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via= hour_abbr= @@ -22,7 +25,7 @@ km_abbr= m_abbr= mi_abbr= ft_abbr= -road=road +road=সড়ক off_bike= cycleway= way= @@ -30,17 +33,19 @@ small_way= paved= unpaved= stopover= -roundabout_enter=golchakkar -roundabout_exit=golchakkar hote %1$s dike jan -roundabout_exit_onto=golchakkar hote %1$s dike jan %2$s -total_ascend= -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +roundabout_enter=গোলচক্কর +roundabout_exit=গোলচক্কর হতে %1$s এর দিকে যান +roundabout_exit_onto=গোলচক্কর হতে %2$s এ যেতে %1$s এর দিকে যান +web.total_ascend= +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll=রুটে টোল আছে +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label= web.intermediate_label= @@ -50,8 +55,18 @@ web.set_intermediate= web.set_end= web.center_map= web.show_coords= +web.query_osm= web.route= +web.add_to_route= web.delete_from_route= +web.open_custom_model_box= +web.help_custom_model=সাহায্য +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model=উদাহরণ web.marker= web.gh_offline_info= web.refresh_button= @@ -67,13 +82,18 @@ web.searching_location_failed= web.via_hint= web.from_hint= web.gpx_export_button= +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint= web.route_info= -web.search_button= -web.more_button= +web.search_button=অনুসন্ধান করুন +web.more_button=আরও web.pt_route_info= web.pt_route_info_walking= web.locations_not_found= +web.search_with_nominatim= +web.powered_by= web.bike= web.racingbike= web.mtb= @@ -85,6 +105,9 @@ web.bus= web.truck= web.staticlink= web.motorcycle= +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/ca.txt b/core/src/main/resources/com/graphhopper/util/ca.txt index 413e0f031ef..779afae4a29 100644 --- a/core/src/main/resources/com/graphhopper/util/ca.txt +++ b/core/src/main/resources/com/graphhopper/util/ca.txt @@ -13,6 +13,9 @@ turn_slight_right=gira lleugerament a la dreta turn_sharp_left=gira just a l'esquerra turn_sharp_right=gira just a la dreta u_turn=fes la volta +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=instrucció desconeguda «%1$s» via=passant per hour_abbr=h @@ -33,14 +36,16 @@ stopover=passant per %1$s roundabout_enter=Entra a la rotonda roundabout_exit=A la rotonda, agafa la %1$sa sortida roundabout_exit_onto=A la rotonda, agafa la %1$sa sortida cap a %2$s -total_ascend=%1$s de pujada total -total_descend=%1$s de baixada total -way_contains_ford=hi ha un gual en el camí -way_contains_ferry=Agafa el ferri -way_contains_private=camí privat -way_contains_toll=via de peatge -pt_start_trip=entra a %1$s -pt_end_trip=surt de %1$s +web.total_ascend=%1$s de pujada total +web.total_descend=%1$s de baixada total +web.way_contains_ford=hi ha un gual en el camí +web.way_contains_ferry=Agafa el ferri +web.way_contains_private=camí privat +web.way_contains_toll=via de peatge +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=canvia a %1$s web.start_label=Punt d'inici web.intermediate_label=Punt intermig @@ -50,8 +55,18 @@ web.set_intermediate=Defineix com a punt intermig web.set_end=Defineix com a punt final web.center_map=Centra el mapa web.show_coords=Mostra les coordenades +web.query_osm= web.route=Ruta +web.add_to_route= web.delete_from_route=Suprimeix el punt de la ruta +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Marcador web.gh_offline_info=L'API GraphHopper no té connexió ? web.refresh_button=Actualitza la pàgina @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Passant per web.from_hint=Des de web.gpx_export_button=Exporta a GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Cap a web.route_info=%1$s trigaràs %2$s web.search_button=Cerca @@ -74,6 +92,8 @@ web.more_button=més web.pt_route_info=arriba a les %1$s amb %2$s transbordaments (%3$s) web.pt_route_info_walking=arriba caminant a les %1$s (%2$s) web.locations_not_found=No hi ha cap ruta. El destí no es troba dins l'àrea. +web.search_with_nominatim= +web.powered_by= web.bike=Bicicleta web.racingbike=Bicicleta de curses web.mtb=Bicicleta de muntanya @@ -85,6 +105,9 @@ web.bus=Autobús web.truck=Camió web.staticlink=Enllaç web.motorcycle=Motocicleta +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular=En un quilòmetre navigate.in_km=En %1$s quilòmetres navigate.in_m=En %1$s metres @@ -96,3 +119,4 @@ navigate.in_ft=En %1$s peus navigate.for_mi=per %1$s milles navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/cs_CZ.txt b/core/src/main/resources/com/graphhopper/util/cs_CZ.txt index 98ece1c2df1..aaf0f046e43 100644 --- a/core/src/main/resources/com/graphhopper/util/cs_CZ.txt +++ b/core/src/main/resources/com/graphhopper/util/cs_CZ.txt @@ -13,6 +13,9 @@ turn_slight_right=odbočte mírně vpravo turn_sharp_left=odbočte ostře vlevo turn_sharp_right=odbočte ostře vpravo u_turn=otočte se +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=neznámý pokyn „%1$s“ via=přes hour_abbr=h @@ -33,14 +36,16 @@ stopover=zastávka %1$s roundabout_enter=Vjeďte na kruhový objezd roundabout_exit=Na kruhovém objezdu použijte %1$s. výjezd roundabout_exit_onto=Na kruhovém objezdu použijte %1$s. výjezd, směrem na %2$s -total_ascend=Celkové stoupání %1$s -total_descend=Celkové klesání %1$s -way_contains_ford=na cestě je brod -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=nastupte do %1$s -pt_end_trip=vystupte z %1$s +web.total_ascend=Celkové stoupání %1$s +web.total_descend=Celkové klesání %1$s +web.way_contains_ford=na cestě je brod +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=přestupte na %1$s web.start_label=Start web.intermediate_label=Zastávka @@ -50,8 +55,18 @@ web.set_intermediate=Nastavit jako zastávku web.set_end=Nastavit jako cíl web.center_map=Vycentrovat sem mapu web.show_coords=Zobrazit souřadnice +web.query_osm= web.route=Trasa +web.add_to_route= web.delete_from_route=Odstranit z trasy +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Značka web.gh_offline_info=API GraphHopper je offline? web.refresh_button=Obnovit stránku @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Přes web.from_hint=Z web.gpx_export_button=Export do GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Do web.route_info=%1$s bude trvat %2$s web.search_button=Vyhledat @@ -74,6 +92,8 @@ web.more_button=více web.pt_route_info=dorazí v %1$s, %2$s přestup(y) (%3$s) web.pt_route_info_walking=dorazí v %1$s (%2$s) web.locations_not_found=Navigování není dostupné. Pozice nenalezena v této oblasti. +web.search_with_nominatim= +web.powered_by= web.bike=Kolo web.racingbike=Závodní kolo web.mtb=Horské kolo @@ -85,6 +105,9 @@ web.bus=Autobus web.truck=Nákladní automobil web.staticlink=neměnný odkaz web.motorcycle=Motocykl +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/da_DK.txt b/core/src/main/resources/com/graphhopper/util/da_DK.txt index dd281c67d0c..ac275e6a4a3 100644 --- a/core/src/main/resources/com/graphhopper/util/da_DK.txt +++ b/core/src/main/resources/com/graphhopper/util/da_DK.txt @@ -13,6 +13,9 @@ turn_slight_right=drej lidt til højre turn_sharp_left=drej skarpt til venstre turn_sharp_right=drej skarpt til højre u_turn=foretag en U-vending +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=ukendt instruktionsskilt '%1$s' via=via hour_abbr=t @@ -33,14 +36,16 @@ stopover=delmål %1$s roundabout_enter=Kør ind i rundskørslen roundabout_exit=I rundkørslen, tag udkørsel %1$s roundabout_exit_onto=I rundskørslen, tag udkørsel %1$s ind på %2$s -total_ascend=%1$s samlet stigning -total_descend=%1$s samlet fald -way_contains_ford=der er et vadested undervejs -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=stig på %1$s -pt_end_trip=stig af %1$s +web.total_ascend=%1$s samlet stigning +web.total_descend=%1$s samlet fald +web.way_contains_ford=der er et vadested undervejs +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=omstigning til %1$s web.start_label=Start web.intermediate_label=Via @@ -50,8 +55,18 @@ web.set_intermediate=Sæt via web.set_end=Sæt som mål web.center_map=Centrer kort her web.show_coords=Vis koordinater +web.query_osm= web.route=Rute +web.add_to_route= web.delete_from_route=Fjern fra rute +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Markør web.gh_offline_info=Er GraphHopper API offline? web.refresh_button=Genindlæs siden @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Via web.from_hint=Fra web.gpx_export_button=Eksportér GPX-fil +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Til web.route_info=%1$s med køretid %2$s web.search_button=Søg @@ -74,6 +92,8 @@ web.more_button=mere web.pt_route_info=ankommer %1$s med %2$s omstigninger (%3$s) web.pt_route_info_walking=ankommer til fods %1$s (%2$s) web.locations_not_found=Kan ikke beregne ruten. Lokation(erne) kan ikke findes i området. +web.search_with_nominatim= +web.powered_by= web.bike=Cykel web.racingbike=Racercykel web.mtb=Mountain bike @@ -85,6 +105,9 @@ web.bus=Bus web.truck=Lastbil web.staticlink=statisk link web.motorcycle=Motorcykel +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/de_DE.txt b/core/src/main/resources/com/graphhopper/util/de_DE.txt index 0935c4b42ce..b1e3663f21e 100644 --- a/core/src/main/resources/com/graphhopper/util/de_DE.txt +++ b/core/src/main/resources/com/graphhopper/util/de_DE.txt @@ -13,6 +13,9 @@ turn_slight_right=leicht rechts abbiegen turn_sharp_left=scharf links abbiegen turn_sharp_right=scharf rechts abbiegen u_turn=wenden +toward_destination=%1$s und Richtung %2$s fahren +toward_destination_ref_only=%1$s zur %2$s +toward_destination_with_ref=%1$s und fahre zur %2$s in Richtung %3$s unknown=unbekanntes Richtungszeichen '%1$s' via=über hour_abbr=h @@ -33,25 +36,37 @@ stopover=Wegpunkt %1$s roundabout_enter=In den Kreisverkehr einfahren roundabout_exit=Im Kreisverkehr Ausfahrt %1$s nehmen roundabout_exit_onto=Im Kreisverkehr Ausfahrt %1$s auf %2$s nehmen -total_ascend=%1$s Gesamtaufstieg -total_descend=%1$s Gesamtabstieg -way_contains_ford=Achtung, es liegt eine Furt auf dem Weg -way_contains_ferry=die Fähre nehmen -way_contains_private=Privatweg -way_contains_toll=Mautpflichtige Straße -pt_start_trip=einsteigen in %1$s -pt_end_trip=aus %1$s aussteigen +web.total_ascend=%1$s Gesamtaufstieg +web.total_descend=%1$s Gesamtabstieg +web.way_contains_ford=Eine Furt muss überquert werden +web.way_contains_ferry=Route erfordert Fährennutzung +web.way_contains_private=Route mit Privatwegen +web.way_contains_toll=Route mit mautpflichtigen Straßen +web.way_crosses_border=Route überquert Landesgrenzen +web.way_contains=Auf der Route sind %1$s +web.tracks=unbefestigte Feldwege +web.steps=Treppen pt_transfer_to=umsteigen auf %1$s web.start_label=Start web.intermediate_label=Zwischenziel web.end_label=Ziel -web.set_start=Setze als Start -web.set_intermediate=Setze als Zwischenziel -web.set_end=Setze als Ziel -web.center_map=Zentriere Karte hier +web.set_start=Von hier +web.set_intermediate=Zwischenziel +web.set_end=Bis hier +web.center_map=Zentriere Karte web.show_coords=Zeige Koordinaten +web.query_osm=OSM Anfragen web.route=Route +web.add_to_route=Ziel hinzufügen web.delete_from_route=Lösche aus Route +web.open_custom_model_box=Öffne custom model Kasten +web.help_custom_model=Hilfe +web.apply_custom_model=Anwenden +web.exclude_motorway_example=Autobahn vermeiden +web.limit_speed_example=Max. Geschwindigkeit +web.exclude_area_example=Gebiet umfahren +web.combined_example=Kombiniertes Bsp +web.examples_custom_model=Beispiele web.marker=Marker web.gh_offline_info=Ist die GraphHopper API offline? web.refresh_button=Lade Seite neu @@ -66,7 +81,10 @@ web.searching_location=Suche Standort web.searching_location_failed=Standortsuche fehlgeschlagen web.via_hint=Über web.from_hint=Von -web.gpx_export_button=GPX export +web.gpx_export_button=GPX Export +web.gpx_button= +web.hide_button=Weniger +web.details_button=Details web.to_hint=Nach web.route_info=%1$s werden %2$s brauchen web.search_button=Suche @@ -74,6 +92,8 @@ web.more_button=mehr web.pt_route_info=Ankunft %1$s mit %2$s Umstiegen (%3$s) web.pt_route_info_walking=Ankunft zu Fuß %1$s (%2$s) web.locations_not_found=Routing war nicht möglich. Ort(e) nicht gefunden in diesem Gebiet. +web.search_with_nominatim=Suche mit Nominatim +web.powered_by=Powered by web.bike=Fahrrad web.racingbike=Rennrad web.mtb=Mountainbike @@ -85,6 +105,9 @@ web.bus=Bus web.truck=LKW web.staticlink=Link web.motorcycle=Motorrad +web.back_to_map=Zurück +web.distance_unit=Distanzen sind in %1$s +web.waiting_for_gps=Warten auf das GPS Signal... navigate.in_km_singular=In 1 Kilometer navigate.in_km=In %1$s Kilometern navigate.in_m=In %1$s Metern @@ -94,5 +117,6 @@ navigate.in_mi_singular=In 1 Meile navigate.in_mi=In %1$s Meilen navigate.in_ft=In %1$s Fuß navigate.for_mi=für %1$s Meilen -navigate.warning= -navigate.accept_risks_after_warning= +navigate.warning=Achtung: diese Applikation ist experimentell! Benutzung auf eigene Gefahr! +navigate.accept_risks_after_warning=I verstehe und akzeptiere das Risiko +navigate.start_navigation=Navi diff --git a/core/src/main/resources/com/graphhopper/util/el.txt b/core/src/main/resources/com/graphhopper/util/el.txt index ece15bde5c9..d0cfb378c9b 100644 --- a/core/src/main/resources/com/graphhopper/util/el.txt +++ b/core/src/main/resources/com/graphhopper/util/el.txt @@ -13,6 +13,9 @@ turn_slight_right=στρίψτε λοξά δεξιά turn_sharp_left=στρίψτε κλειστά αριστερά turn_sharp_right=στρίψτε κλειστά δεξιά u_turn=κάνετε αναστροφή +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=άγνωστη οδηγία '%1$s' via=μέσω hour_abbr=h @@ -33,14 +36,16 @@ stopover=σημείο διαδρομής %1$s roundabout_enter=Μπείτε στον κυκλικό κόμβο roundabout_exit=Στον κυκλικό κόμβο βγείτε στην έξοδο %1$s roundabout_exit_onto=Στον κυκλικό κόμβο βγείτε στην έξοδο %1$s στην %2$s -total_ascend=%1$s συνολική ανάβαση -total_descend=%1$s συνολική κατάβαση -way_contains_ford=υπάρχει πέρασμα στο δρόμο -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=μπείτε στο %1$s -pt_end_trip=αφήστε το %1$s +web.total_ascend=%1$s συνολική ανάβαση +web.total_descend=%1$s συνολική κατάβαση +web.way_contains_ford=υπάρχει πέρασμα στο δρόμο +web.way_contains_ferry=χρησιμοποιήστε το ferry +web.way_contains_private=ιδιωτικός δρόμος +web.way_contains_toll=δρόμος με διόδια +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=αλλάξτε στο %1$s web.start_label=Αφετηρία web.intermediate_label=Ενδιάμεσο σημείο @@ -50,8 +55,18 @@ web.set_intermediate=Ορισμός ενδιάμεσου σημείου web.set_end=Ορισμός προορισμού web.center_map=Κεντράρισμα χάρτη εδώ web.show_coords=Εμφάνιση συντεταγμένων +web.query_osm= web.route=Διαδρομή +web.add_to_route= web.delete_from_route=Αφαίρεση από διαδρομή +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Σημάδι web.gh_offline_info=GraphHopper API offline? web.refresh_button=Ανανέωση σελίδας @@ -59,14 +74,17 @@ web.server_status=Κατάσταση web.zoom_in=Μεγέθυνση web.zoom_out=Σμίκρυνση web.drag_to_reorder=Σύρετε για αναδιάταξη -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.route_timed_out=Ο υπολογισμός διαδρομής απέτυχε λόγω εξάντλησης χρόνου +web.route_request_failed=Ο υπολογισμός διαδρομής απέτυχε +web.current_location=Τωρινή τοποθεσία +web.searching_location=Αναζήτηση τοποθεσίας +web.searching_location_failed=Η αναζήτηση τοποθεσίας απέτυχε web.via_hint=Μέσω web.from_hint=Αφετηρία web.gpx_export_button=Εξαγωγή GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Προορισμός web.route_info=%1$s σε %2$s web.search_button=Αναζήτηση @@ -74,6 +92,8 @@ web.more_button=περισσότερα web.pt_route_info=φτάνει στις %1$s με %2$s μεταβιβάσεις (%3$s) web.pt_route_info_walking=φτάνει στις %1$s απλά περπατώντας (%2$s) web.locations_not_found=Η δρομολόγηση δεν είναι δυνατή. Οι τοποθεσίες δεν βρέθηκαν στην περιοχή. +web.search_with_nominatim= +web.powered_by= web.bike=Ποδήλατο web.racingbike=Αγωνιστικό ποδήλατο web.mtb=Ποδήλατο βουνού @@ -85,14 +105,18 @@ web.bus=Λεωφορείο web.truck=Φορτηγό web.staticlink=στατική διεύθυνση web.motorcycle=Μοτοσυκλέτα -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= -navigate.in_mi_singular= -navigate.in_mi= -navigate.in_ft= -navigate.for_mi= -navigate.warning= -navigate.accept_risks_after_warning= +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= +navigate.in_km_singular=Σε 1 χιλιόμετρο +navigate.in_km=Σε %1$s χιλιόμετρα +navigate.in_m=Σε %1$s μέτρα +navigate.for_km=Για %1$s χιλιόμετρα +navigate.then=τότε +navigate.in_mi_singular=Σε 1 μίλη +navigate.in_mi=Σε %1$s μίλια +navigate.in_ft=Σε %1$s πόδια +navigate.for_mi=για %1$s μίλια +navigate.warning=ΠΡΟΣΟΧΗ: Αυτή η εφαρμογή είναι πειραματική! Χρησιμοποιήστε την με δική σας ευθύνη! +navigate.accept_risks_after_warning=Καταλαβαίνω και συμφωνώ +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/en_US.txt b/core/src/main/resources/com/graphhopper/util/en_US.txt index e3b04f1d36a..254935c2824 100644 --- a/core/src/main/resources/com/graphhopper/util/en_US.txt +++ b/core/src/main/resources/com/graphhopper/util/en_US.txt @@ -13,6 +13,9 @@ turn_slight_right=turn slight right turn_sharp_left=turn sharp left turn_sharp_right=turn sharp right u_turn=make a U-turn +toward_destination=%1$s and drive toward %2$s +toward_destination_ref_only=%1$s toward %2$s +toward_destination_with_ref=%1$s and take %2$s toward %3$s unknown=unknown instruction sign '%1$s' via=via hour_abbr=h @@ -33,25 +36,37 @@ stopover=waypoint %1$s roundabout_enter=Enter roundabout roundabout_exit=At roundabout, take exit %1$s roundabout_exit_onto=At roundabout, take exit %1$s onto %2$s -total_ascend=%1$s total ascent -total_descend=%1$s total descent -way_contains_ford=Attention, there is a ford on the way -way_contains_ferry=take the ferry -way_contains_private=private road -way_contains_toll=toll road -pt_start_trip=enter %1$s -pt_end_trip=leave %1$s +web.total_ascend=%1$s total ascent +web.total_descend=%1$s total descent +web.way_contains_ford=Route includes fords +web.way_contains_ferry=Route includes ferries +web.way_contains_private=Route with private roads +web.way_contains_toll=Route has tolls +web.way_crosses_border=Route crosses a country border +web.way_contains=Route includes %1$s +web.tracks=unpaved dirt roads +web.steps=steps pt_transfer_to=change to %1$s web.start_label=Start web.intermediate_label=Intermediate web.end_label=End -web.set_start=Set as start -web.set_intermediate=Set intermediate -web.set_end=Set as end -web.center_map=Center map here +web.set_start=From here +web.set_intermediate=Via point +web.set_end=To here +web.center_map=Center map web.show_coords=Show coordinates +web.query_osm=Query OSM web.route=Route +web.add_to_route=Add Location web.delete_from_route=Delete from Route +web.open_custom_model_box=Open custom model box +web.help_custom_model=Help +web.apply_custom_model=Apply +web.exclude_motorway_example=Exclude Motorway +web.limit_speed_example=Limit Speed +web.exclude_area_example=Exclude Area +web.combined_example=Combined Example +web.examples_custom_model=Examples web.marker=Marker web.gh_offline_info=GraphHopper API offline? web.refresh_button=Refresh page @@ -66,7 +81,10 @@ web.searching_location=Searching location web.searching_location_failed=Searching location failed web.via_hint=Via web.from_hint=From -web.gpx_export_button=GPX export +web.gpx_export_button=GPX Export +web.gpx_button=GPX +web.hide_button=Hide +web.details_button=Details web.to_hint=To web.route_info=%1$s will take %2$s web.search_button=Search @@ -74,6 +92,8 @@ web.more_button=more web.pt_route_info=arrives at %1$s with %2$s transfers (%3$s) web.pt_route_info_walking=arrives at %1$s with just walking (%2$s) web.locations_not_found=Routing not possible. Location(s) not found in the area. +web.search_with_nominatim=Search with Nominatim +web.powered_by=Powered by web.bike=Bike web.racingbike=Racing Bike web.mtb=MTB @@ -85,6 +105,9 @@ web.bus=Bus web.truck=Truck web.staticlink=static link web.motorcycle=Motorcycle +web.back_to_map=Back +web.distance_unit=Distances are in %1$s +web.waiting_for_gps=Waiting for the GPS signal... navigate.in_km_singular=In 1 kilometer navigate.in_km=In %1$s kilometers navigate.in_m=In %1$s meters @@ -96,3 +119,4 @@ navigate.in_ft=In %1$s feet navigate.for_mi=for %1$s miles navigate.warning=WARNING: This application is highly experimental! Use at your own risk! navigate.accept_risks_after_warning=I understand and agree +navigate.start_navigation=Navigate diff --git a/core/src/main/resources/com/graphhopper/util/eo.txt b/core/src/main/resources/com/graphhopper/util/eo.txt index 7acaf08c4da..b13b378ed1f 100644 --- a/core/src/main/resources/com/graphhopper/util/eo.txt +++ b/core/src/main/resources/com/graphhopper/util/eo.txt @@ -13,6 +13,9 @@ turn_slight_right=turniĝetu dekstren turn_sharp_left=turniĝegu maldekstren turn_sharp_right=turniĝegu dekstren u_turn=turniĝu malantaŭen +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=nekonata instrukcio “%1$s” via=tra hour_abbr=h @@ -33,14 +36,16 @@ stopover=%1$s-a haltejo roundabout_enter=Enveturu trafikcirklon roundabout_exit=Ĉe trafikcirklo, elveturu al %1$s roundabout_exit_onto=Ĉe trafikcirklo, elveturu al %1$s‑a vojo al %2$s -total_ascend=%1$s supreniro tute -total_descend=%1$s malsupreniro tute -way_contains_ford=travadejo sur la kurso -way_contains_ferry=enpramiĝu -way_contains_private=privata vojo -way_contains_toll=pegenda vojo -pt_start_trip=eniru al %1$s -pt_end_trip=eliru el %1$s +web.total_ascend=%1$s supreniro tute +web.total_descend=%1$s malsupreniro tute +web.way_contains_ford=travadejo sur la kurso +web.way_contains_ferry=enpramiĝu +web.way_contains_private=privata vojo +web.way_contains_toll=pegenda vojo +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=transveturiĝu al %1$s web.start_label=Komenco web.intermediate_label=Intercelo @@ -50,8 +55,18 @@ web.set_intermediate=Agordi kiel intercelon web.set_end=Agordi kiel celon web.center_map=Centrigi mapon ĉi tien web.show_coords=Montri koordinatojn +web.query_osm= web.route=Kurso +web.add_to_route= web.delete_from_route=Forigi el la kurso +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Marko web.gh_offline_info=Eble GraphHopper-API malfunkcias? web.refresh_button=Aktualigi paĝon @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Tra web.from_hint=El web.gpx_export_button=Elporti GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Al web.route_info=%1$s daŭros %2$s web.search_button=Serĉi @@ -74,6 +92,8 @@ web.more_button=pli web.pt_route_info=alveno je %1$s kun %2$s transveturiliĝoj (%3$s) web.pt_route_info_walking=alveno je %1$s nur perpiede (%2$s) web.locations_not_found=Vojdifinado ne eblas. Ne trovis loko(j)n en la ĉirkaŭaĵo. +web.search_with_nominatim= +web.powered_by= web.bike=Biciklo web.racingbike=Sporta biciklo web.mtb=Montara biciklo @@ -85,6 +105,9 @@ web.bus=Aŭtobuso web.truck=Kamiono web.staticlink=konstanta ligilo web.motorcycle=Motorciklo +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/es.txt b/core/src/main/resources/com/graphhopper/util/es.txt index 4f8cc792553..ef61beac667 100644 --- a/core/src/main/resources/com/graphhopper/util/es.txt +++ b/core/src/main/resources/com/graphhopper/util/es.txt @@ -13,6 +13,9 @@ turn_slight_right=gira leve a la derecha turn_sharp_left=gira fuerte a la izquierda turn_sharp_right=gira fuerte a la derecha u_turn=da la vuelta +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=señal de instrucción «%1$s» desconocida via=pasando por hour_abbr=h @@ -33,14 +36,16 @@ stopover=pasando por %1$s roundabout_enter=Entra en la rotonda roundabout_exit=En la rotonda, toma la %1$sª salida roundabout_exit_onto=En la rotonda, toma la %1$sª salida hacia %2$s -total_ascend=Ascender %1$s en total -total_descend=Descender %1$s en total -way_contains_ford=hay un vado en el camino -way_contains_ferry=toma el ferry -way_contains_private=carretera privada -way_contains_toll=carretera con peaje -pt_start_trip=entra en %1$s -pt_end_trip=sal de %1$s +web.total_ascend=Ascender %1$s en total +web.total_descend=Descender %1$s en total +web.way_contains_ford=hay un vado en el camino +web.way_contains_ferry=toma el ferry +web.way_contains_private=carretera privada +web.way_contains_toll=carretera con peaje +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=cambia a %1$s web.start_label=Punto de partida web.intermediate_label=Punto intermedio @@ -50,8 +55,18 @@ web.set_intermediate=Fijar como punto intermedio web.set_end=Fijar como finalización web.center_map=Centrar mapa aquí web.show_coords=Mostrar coordenadas +web.query_osm= web.route=Ruta +web.add_to_route= web.delete_from_route=Borrar punto de ruta +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Marcador web.gh_offline_info=¿API de GraphHopper sin conexión? web.refresh_button=Actualizar página @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Pasando por web.from_hint=Desde web.gpx_export_button=Exportar GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Hasta web.route_info=%1$s tardará %2$s web.search_button=Buscar @@ -74,6 +92,8 @@ web.more_button=más web.pt_route_info=llega a las %1$s con %2$s transferencias (%3$s) web.pt_route_info_walking=llega sólo caminando a las %1$s (%2$s) web.locations_not_found=No se ha encontrado la ruta. El destino no se encuentra en el área. +web.search_with_nominatim= +web.powered_by= web.bike=Bicicleta web.racingbike=Bicicleta de carrera web.mtb=Bicicleta de montaña @@ -85,6 +105,9 @@ web.bus=Autobús web.truck=Camión web.staticlink=enlace estático web.motorcycle=Motocicleta +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular=En un kilómetro navigate.in_km=En %1$s kilómetros navigate.in_m=En %1$s metros @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/fa.txt b/core/src/main/resources/com/graphhopper/util/fa.txt index 41dddce3431..9e608b2809a 100644 --- a/core/src/main/resources/com/graphhopper/util/fa.txt +++ b/core/src/main/resources/com/graphhopper/util/fa.txt @@ -13,6 +13,9 @@ turn_slight_right=کمی به راست بپیچید turn_sharp_left=در پیچ تند به چپ بپیچید turn_sharp_right=در پیچ تند به راست بپیچید u_turn=دور بزنید +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=علامت راهنمایی ناشناخته: '%1$s' via=با گذر از hour_abbr=ساعت @@ -33,14 +36,16 @@ stopover=نقطهٔ بین‌راهی %1$s roundabout_enter=وارد فلکه شوید roundabout_exit=در فلکه، به خروجی %1$s بروید roundabout_exit_onto=در فلکه، از خروجی %1$s به %2$s بروید -total_ascend=مجموع صعود %1$s -total_descend=مجموع نزول %1$s -way_contains_ford=در طول مسیر گُدار وجود دارد -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=وارد %1$s شوید -pt_end_trip=از %1$s خارج شوید +web.total_ascend=مجموع صعود %1$s +web.total_descend=مجموع نزول %1$s +web.way_contains_ford=در طول مسیر گُدار وجود دارد +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=مسیر را به %1$s تغییر دهید web.start_label=آغاز web.intermediate_label=نقطهٔ میانی @@ -50,8 +55,18 @@ web.set_intermediate=انتخاب نقطهٔ میانی web.set_end=انتخاب نقطهٔ پایان web.center_map=اینجا مرکز نقشه شود web.show_coords=مختصات اینجا +web.query_osm= web.route=مسیر +web.add_to_route= web.delete_from_route=از مسیر حذف شود +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=نشانه web.gh_offline_info=گراف‌هاپر در دسترس نیست؟ web.refresh_button=تازه‌سازی صفحه @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=با گذر از web.from_hint=از web.gpx_export_button=دریافت در قالب GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=به web.route_info=%1$s %2$s طول می‌کشد web.search_button=جست‌وجو @@ -74,6 +92,8 @@ web.more_button=بیشتر web.pt_route_info=با %2$s جابه‌جایی در ساعت %1$s می‌رسید (%3$s) web.pt_route_info_walking=ساعت %1$s می‌رسید، فقط پیاده (%2$s) web.locations_not_found=مسیریابی ممکن نیست. مکان(ها) در ناحیهٔ موردنظر پیدا نشد. +web.search_with_nominatim= +web.powered_by= web.bike=دوچرخه web.racingbike=دوچرخه کورسی web.mtb=دوچرخه کوهستان @@ -85,6 +105,9 @@ web.bus=اتوبوس web.truck=کامیون web.staticlink=پیوند ثابت web.motorcycle=موتورسیکلت +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/fi.txt b/core/src/main/resources/com/graphhopper/util/fi.txt index e93121a1b80..984035daf6d 100644 --- a/core/src/main/resources/com/graphhopper/util/fi.txt +++ b/core/src/main/resources/com/graphhopper/util/fi.txt @@ -13,6 +13,9 @@ turn_slight_right=käänny loivasti oikealle turn_sharp_left=käänny jyrkästi vasemmalle turn_sharp_right=käänny jyrkästi oikealle u_turn=tee U-käännös +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=tuntematon ohje '%1$s' via=kautta hour_abbr=h @@ -33,14 +36,16 @@ stopover=%1$s. pysähdys roundabout_enter=Aja liikenneympyrään roundabout_exit=Liikenneympyrästä poistu %1$s. liittymästä roundabout_exit_onto=Liikenneympyrästä poistu %1$s. liittymästä suuntaan %2$s -total_ascend=nousu yhteensä %1$s -total_descend=lasku yhteensä %1$s -way_contains_ford=Huomio, reitillä on kahlaamo -way_contains_ferry=käytä lauttaa -way_contains_private=yksityinen tie -way_contains_toll=maksullinen tie -pt_start_trip=mene %1$s -pt_end_trip=poistu %1$s +web.total_ascend=nousu yhteensä %1$s +web.total_descend=lasku yhteensä %1$s +web.way_contains_ford=Huomio, reitillä on kahlaamo +web.way_contains_ferry=käytä lauttaa +web.way_contains_private=yksityinen tie +web.way_contains_toll=maksullinen tie +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=vaihda %1$s web.start_label=Lähtöpaikka web.intermediate_label=Välikohde @@ -50,8 +55,18 @@ web.set_intermediate=Merkitse välikohteeksi web.set_end=Merkitse päätepisteeksi web.center_map=Keskitä kartta tähän web.show_coords=Näytä koordinaatit +web.query_osm= web.route=Reitti +web.add_to_route= web.delete_from_route=Poista reitistä +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Merkki web.gh_offline_info=Ei yhteyttä GraphHopper-palvelimeen web.refresh_button=Päivitä sivu @@ -67,6 +82,9 @@ web.searching_location_failed=Paikan haku epäonnistui web.via_hint=Reittipiste web.from_hint=Lähtöpaikka web.gpx_export_button=GPX-tuonti +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Määränpää web.route_info=%1$s kestää %2$s web.search_button=Etsi @@ -74,6 +92,8 @@ web.more_button=lisää web.pt_route_info=saapuu kello %1$s, %2$s vaihtoa (%3$s) web.pt_route_info_walking=saapuu kello %1$s, vain kävely (%3$s) web.locations_not_found=Reittiohjeiden luonti epäonnistui. Paikkaa ei löydy tältä alueelta. +web.search_with_nominatim= +web.powered_by= web.bike=Pyörä web.racingbike=Kilpapyörä web.mtb=Maastopyörä @@ -85,6 +105,9 @@ web.bus=Linja-auto web.truck=Kuorma-auto web.staticlink=yhteys web.motorcycle=Moottoripyörällä +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular=Kilometrin päässä navigate.in_km=%1$s kilometrin päässä navigate.in_m=%1$s metrin päässä @@ -96,3 +119,4 @@ navigate.in_ft=%1$s jalan päässä navigate.for_mi=kulje %1$s mailia navigate.warning=VAROITUS: Tämä sovellus on erittäin kokeellinen! Käytä omalla vastuullasi! navigate.accept_risks_after_warning=Ymmärrän ja hyväksyn +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/fil.txt b/core/src/main/resources/com/graphhopper/util/fil.txt index 9692aa6d946..25622231e7f 100644 --- a/core/src/main/resources/com/graphhopper/util/fil.txt +++ b/core/src/main/resources/com/graphhopper/util/fil.txt @@ -13,6 +13,9 @@ turn_slight_right=pagliko bahagyang kanan turn_sharp_left=pagliko matalim kaliwa turn_sharp_right=pagliko matalim karapatan u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=sa pamamagitan ng hour_abbr=h @@ -33,14 +36,16 @@ stopover=pamahingahan %1$s roundabout_enter=Lpasok Rotonda roundabout_exit=Sa rotonda, lumabas sa exit %1$s roundabout_exit_onto=Sa rotonda, lumabas sa exit papunta %1$s %2$s -total_ascend= -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend= +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label= web.intermediate_label= @@ -50,8 +55,18 @@ web.set_intermediate= web.set_end= web.center_map= web.show_coords= +web.query_osm= web.route= +web.add_to_route= web.delete_from_route= +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker= web.gh_offline_info= web.refresh_button= @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint= web.from_hint=mula sa web.gpx_export_button=GPX Export +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=upang sa web.route_info=%1$s ay magdadala sa %2$s web.search_button=Paghahanap @@ -74,6 +92,8 @@ web.more_button=mas web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=Routing hindi maaari, hindi nahanap Lokasyon sa lugar. +web.search_with_nominatim= +web.powered_by= web.bike=Bike web.racingbike=Racing Bike web.mtb=Mountain Bike @@ -85,6 +105,9 @@ web.bus= web.truck= web.staticlink=static link web.motorcycle=motorsiklo +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/fr_CH.txt b/core/src/main/resources/com/graphhopper/util/fr_CH.txt index f68d973a0a4..ba16f8eb136 100644 --- a/core/src/main/resources/com/graphhopper/util/fr_CH.txt +++ b/core/src/main/resources/com/graphhopper/util/fr_CH.txt @@ -13,6 +13,9 @@ turn_slight_right=tournez légèrement à droite turn_sharp_left=tournez fort à gauche turn_sharp_right=tournez fort à droite u_turn=faites demi-tour +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref=%1$s et prendre %2$s vers %3$s unknown=indication inconnue '%1$s' via=via hour_abbr=h @@ -31,16 +34,18 @@ paved=revêtu unpaved=non revêtu stopover=escale %1$s roundabout_enter=Empruntez le giratoire -roundabout_exit=Au giratoire, prenez la %1$s sortie -roundabout_exit_onto=Au giratoire, prenez la %1$s sortie vers %2$s -total_ascend=%1$s de dénivelé positif -total_descend=%1$s de dénivelé négatif -way_contains_ford=cet itinéraire comprend un passage à gué -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=montez dans %1$s -pt_end_trip=descendez de %1$s +roundabout_exit=Au giratoire, prenez la %1$se sortie +roundabout_exit_onto=Au giratoire, prenez la %1$se sortie vers %2$s +web.total_ascend=%1$s de dénivelé positif +web.total_descend=%1$s de dénivelé négatif +web.way_contains_ford=cet itinéraire comprend un passage à gué +web.way_contains_ferry=prenez le bateau +web.way_contains_private=chemin privé +web.way_contains_toll=route à péage +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=changez vers %1$s web.start_label=Départ web.intermediate_label=Point intermédiaire @@ -50,8 +55,18 @@ web.set_intermediate=Ajouter un point intermédiaire web.set_end=Arrivée de l'itinéraire ici web.center_map=Centrer la carte ici web.show_coords=Afficher les coordonnées +web.query_osm= web.route=Itinéraire +web.add_to_route=Ajouter un Emplacement web.delete_from_route=Supprimer ce point de l'itinéraire +web.open_custom_model_box=Ouvrir la boîte du modèle personnalisé +web.help_custom_model=Aide +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Marqueur web.gh_offline_info=GraphHopper API hors connexion? web.refresh_button=Rafraîchir @@ -59,14 +74,17 @@ web.server_status=Statut web.zoom_in=Zoom avant web.zoom_out=Zoom arrière web.drag_to_reorder=Faire glisser pour réorganiser -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.route_timed_out=Le calcul de l'itinéraire a expiré +web.route_request_failed=Échec du calcul de l'itinéraire +web.current_location=Localisation actuelle +web.searching_location=Chercher localisation +web.searching_location_failed=Recherche de localisation échouée web.via_hint=Via web.from_hint=De web.gpx_export_button=Export GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=À web.route_info=%1$s durera %2$s web.search_button=Rechercher @@ -74,6 +92,8 @@ web.more_button=plus web.pt_route_info=arrivée à %1$s avec %2$s changements (%3$s) web.pt_route_info_walking=arrivée à pied à %1$s (%2$s) web.locations_not_found=Calcul d'itinéraire impossible. Position(s) non trouvée(s) dans la zone. +web.search_with_nominatim= +web.powered_by= web.bike=Vélo web.racingbike=Vélo de course web.mtb=VTT @@ -85,14 +105,18 @@ web.bus=Bus web.truck=Camion web.staticlink=Lien web.motorcycle=Moto -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= +navigate.in_km_singular=à 1 kilomètre +navigate.in_km=à %1$s kilomètres +navigate.in_m=à %1$s mètres +navigate.for_km=pendant %1$s kilomètres +navigate.then=ensuite navigate.in_mi_singular= navigate.in_mi= navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/fr_FR.txt b/core/src/main/resources/com/graphhopper/util/fr_FR.txt index 04c39d54dd5..f76c8f30a44 100644 --- a/core/src/main/resources/com/graphhopper/util/fr_FR.txt +++ b/core/src/main/resources/com/graphhopper/util/fr_FR.txt @@ -13,6 +13,9 @@ turn_slight_right=tournez légèrement à droite turn_sharp_left=tournez fort à gauche turn_sharp_right=tournez fort à droite u_turn=faites demi-tour +toward_destination=%1$s et conduisez vers %2$s +toward_destination_ref_only=%1$s vers %2$s +toward_destination_with_ref=%1$s et prendre %2$s vers %3$s unknown=indication inconnue '%1$s' via=via hour_abbr=h @@ -31,16 +34,18 @@ paved=revêtu unpaved=non revêtu stopover=étape %1$s roundabout_enter=Empruntez le rond-point -roundabout_exit=Au rond-point, prenez la %1$s sortie -roundabout_exit_onto=Au rond-point, prenez la %1$s sortie vers %2$s -total_ascend=%1$s de dénivelé positif -total_descend=%1$s de dénivelé négatif -way_contains_ford=cet itinéraire comprend un passage à gué -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=montez dans %1$s -pt_end_trip=descendez de %1$s +roundabout_exit=Au rond-point, prenez la %1$se sortie +roundabout_exit_onto=Au rond-point, prenez la %1$se sortie vers %2$s +web.total_ascend=%1$s de dénivelé positif +web.total_descend=%1$s de dénivelé négatif +web.way_contains_ford=cet itinéraire comprend un passage à gué +web.way_contains_ferry=prenez le bateau +web.way_contains_private=chemin privé +web.way_contains_toll=route à péage +web.way_crosses_border=L'itinéraire traverse une frontière nationale +web.way_contains=L'itinéraire inclut %1$s +web.tracks=chemins de terre non pavés +web.steps=pas pt_transfer_to=changez vers %1$s web.start_label=Départ web.intermediate_label=Point intermédiaire @@ -50,8 +55,18 @@ web.set_intermediate=Ajouter un point intermédiaire web.set_end=Arrivée de l'itinéraire ici web.center_map=Centrer la carte ici web.show_coords=Afficher les coordonnées +web.query_osm=Interroger OSM web.route=Itinéraire +web.add_to_route=Ajouter un Emplacement web.delete_from_route=Supprimer ce point de l'itinéraire +web.open_custom_model_box=Ouvrir la boîte du modèle personnalisé +web.help_custom_model=Aide +web.apply_custom_model=Appliquer +web.exclude_motorway_example=Eviter les Autoroutes +web.limit_speed_example=Vitesse Limite +web.exclude_area_example=Exclure Zone +web.combined_example=Exemple Combiné +web.examples_custom_model=Examples web.marker=Marqueur web.gh_offline_info=GraphHopper API hors connexion? web.refresh_button=Rafraîchir @@ -59,14 +74,17 @@ web.server_status=Statut web.zoom_in=Zoom avant web.zoom_out=Zoom arrière web.drag_to_reorder=Faire glisser pour réorganiser -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.route_timed_out=Le calcul de l'itinéraire a expiré +web.route_request_failed=Échec du calcul de l'itinéraire +web.current_location=Localisation actuelle +web.searching_location=Chercher localisation +web.searching_location_failed=Recherche de localisation échouée web.via_hint=Via web.from_hint=De web.gpx_export_button=Export GPX +web.gpx_button=GPX +web.hide_button=Cacher +web.details_button=Détails web.to_hint=À web.route_info=%1$s durera %2$s web.search_button=Rechercher @@ -74,6 +92,8 @@ web.more_button=plus web.pt_route_info=arrivée à %1$s avec %2$s changements (%3$s) web.pt_route_info_walking=arrivée à pied à %1$s (%2$s) web.locations_not_found=Calcul d'itinéraire impossible. Position(s) non trouvée(s) dans la zone. +web.search_with_nominatim=Rechercher avec Nominatim +web.powered_by=Alimenté par web.bike=Vélo web.racingbike=Vélo de course web.mtb=VTT @@ -85,14 +105,18 @@ web.bus=Bus web.truck=Camion web.staticlink=Lien web.motorcycle=Moto -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= -navigate.in_mi_singular= -navigate.in_mi= -navigate.in_ft= -navigate.for_mi= -navigate.warning= -navigate.accept_risks_after_warning= +web.back_to_map=Retour +web.distance_unit=Les distances sont en %1$s +web.waiting_for_gps=En attente du signal GPS... +navigate.in_km_singular=à 1 kilomètre +navigate.in_km=à %1$s kilomètres +navigate.in_m=à %1$s mètres +navigate.for_km=pendant %1$s kilomètres +navigate.then=ensuite +navigate.in_mi_singular=Dans 1 mile +navigate.in_mi=Dans %1$s miles +navigate.in_ft=Dans %1$s pieds +navigate.for_mi=Pendant %1$s miles +navigate.warning=ATTENTION : Cette application est hautement expérimentale ! À utiliser à vos risques et périls ! +navigate.accept_risks_after_warning=Je comprends et j'accepte +navigate.start_navigation=Naviguer diff --git a/core/src/main/resources/com/graphhopper/util/gl.txt b/core/src/main/resources/com/graphhopper/util/gl.txt index 0b308b69b73..950fb8eb90d 100644 --- a/core/src/main/resources/com/graphhopper/util/gl.txt +++ b/core/src/main/resources/com/graphhopper/util/gl.txt @@ -13,6 +13,9 @@ turn_slight_right=vire á dereita turn_sharp_left=vire por xusto á esquerda turn_sharp_right=vire por xusto á dereita u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=vía hour_abbr=h @@ -33,14 +36,16 @@ stopover=escala%1$s roundabout_enter=Entre na rotonda roundabout_exit=Na rotonda tome a saída %1$s roundabout_exit_onto=Na rotonda, tome a saída %1$s cara %2$s -total_ascend= -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend= +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label= web.intermediate_label= @@ -50,8 +55,18 @@ web.set_intermediate= web.set_end= web.center_map= web.show_coords= +web.query_osm= web.route= +web.add_to_route= web.delete_from_route= +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker= web.gh_offline_info= web.refresh_button= @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Vía web.from_hint=dende web.gpx_export_button=GPX Exportación +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=ata web.route_info=%1$s tardará %2$s web.search_button=buscar @@ -74,6 +92,8 @@ web.more_button=máis web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=Non se atopou a ruta. O destino non se atopa na área +web.search_with_nominatim= +web.powered_by= web.bike=Bicicleta web.racingbike=Bicleta de carreiras web.mtb=Bicicleta de montaña @@ -85,6 +105,9 @@ web.bus= web.truck= web.staticlink=Enlace web.motorcycle=Motocicleta +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/he.txt b/core/src/main/resources/com/graphhopper/util/he.txt index 791e63c5d61..f344e0741ab 100644 --- a/core/src/main/resources/com/graphhopper/util/he.txt +++ b/core/src/main/resources/com/graphhopper/util/he.txt @@ -13,6 +13,9 @@ turn_slight_right=מעט ימינה turn_sharp_left=שמאלה בחדות turn_sharp_right=ימינה בחדות u_turn=לבצע פניית פרסה +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=שלט הנחייה לא מוכר '%1$s' via=דרך hour_abbr=שע׳ @@ -33,14 +36,16 @@ stopover=נקודת עצירה מס׳ %1$s roundabout_enter=יש להיכנס לכיכר roundabout_exit=בכיכר, יש לצאת ביציאה %1$s roundabout_exit_onto=בכיכר, יש לצאת ביציאה %1$s לתוך %2$s -total_ascend=עלייה כוללת של %1$s -total_descend=ירידה כוללת של %1$s -way_contains_ford=יש מעבר מים לאורך הדרך -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=יש להיכנס אל %1$s -pt_end_trip=יש לצאת מ%1$s +web.total_ascend=עלייה כוללת של %1$s +web.total_descend=ירידה כוללת של %1$s +web.way_contains_ford=יש מעבר מים לאורך הדרך +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=להחליף ל%1$s web.start_label=התחלה web.intermediate_label=אמצע @@ -50,8 +55,18 @@ web.set_intermediate=הגדרה כאמצע web.set_end=הגדרה כסוף web.center_map=למרכז את המפה לכאן web.show_coords=הצגת נקודות ציון +web.query_osm= web.route=דרך +web.add_to_route= web.delete_from_route=מחיקה מהדרך +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=הדגשה web.gh_offline_info=‏ GraphHopper API לא מקוון? web.refresh_button=רענון הדף @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=דרך web.from_hint=מוצא web.gpx_export_button=ייצוא GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=יעד web.route_info=%1$s ייקחו %2$s web.search_button=חיפוש @@ -74,6 +92,8 @@ web.more_button=עוד web.pt_route_info=מגיע ב־%1$s עם %2$s החלפות (%3$s) web.pt_route_info_walking=מגיע ב־%1$s עם הליכה (%3$s) web.locations_not_found=אין נתיב ישיר אל היעד. המיקום לא נמצא באזור. +web.search_with_nominatim= +web.powered_by= web.bike=אופניים web.racingbike=מסלולי אופניים web.mtb=אופני הרים @@ -85,6 +105,9 @@ web.bus=אוטובוס web.truck=משאית web.staticlink=קישור קבוע web.motorcycle=אופנוע +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/hr_HR.txt b/core/src/main/resources/com/graphhopper/util/hr_HR.txt index 33115c494cc..ea66ce48da1 100644 --- a/core/src/main/resources/com/graphhopper/util/hr_HR.txt +++ b/core/src/main/resources/com/graphhopper/util/hr_HR.txt @@ -13,6 +13,9 @@ turn_slight_right=skrenite blago desno turn_sharp_left=skrenite oštro lijevo turn_sharp_right=skrenite oštro desno u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=preko hour_abbr=h @@ -33,14 +36,16 @@ stopover=zaustavite se za %1$s roundabout_enter=Uđite na kružni tok roundabout_exit=Sa kružnog toka izađite na izlaz %1$s roundabout_exit_onto=Sa kružnog toka izađite na izlaz %1$s na %2$s -total_ascend=%1$s ukupni uspon -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend=%1$s ukupni uspon +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label=Početak web.intermediate_label=Posrednik @@ -50,8 +55,18 @@ web.set_intermediate=Postavi kao posrednik web.set_end=Postavi kao kraj web.center_map=Postavi kao centar karte web.show_coords=Prikaži koordinate +web.query_osm= web.route=Ruta +web.add_to_route= web.delete_from_route=Izbriši iz rute +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Oznaka web.gh_offline_info=GraphHopper API nedostupan web.refresh_button=Osvježi stranicu @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Preko web.from_hint=Od web.gpx_export_button=Izvezi kao GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Do web.route_info=%1$s trajat će %2$s web.search_button=Pretraži @@ -74,6 +92,8 @@ web.more_button=više web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=Rutiranje nije moguće. Lokacije nisu pronađene na ovome području. +web.search_with_nominatim= +web.powered_by= web.bike=Bicikl web.racingbike=Trkaći motocikl web.mtb=Brdski bicikl @@ -85,6 +105,9 @@ web.bus=Autobus web.truck=Kamion web.staticlink=statička poveznica web.motorcycle=Motocikl +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/hsb.txt b/core/src/main/resources/com/graphhopper/util/hsb.txt index 14657c985a1..6a5656fa7df 100644 --- a/core/src/main/resources/com/graphhopper/util/hsb.txt +++ b/core/src/main/resources/com/graphhopper/util/hsb.txt @@ -13,6 +13,9 @@ turn_slight_right=zlochka naprawo wotbočić turn_sharp_left=wótrje nalěwo wotbočić turn_sharp_right=wótrje naprawo wotbočić u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=via hour_abbr=hodź. @@ -33,14 +36,16 @@ stopover=mjezycil %1$s roundabout_enter=do kružneho wobchada zajěć roundabout_exit=we kružnym wobchadźe %1$s. wujězd wzać roundabout_exit_onto=we kružnym wobchadźe %1$s. wujězd na %2$s wzać -total_ascend= -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend= +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label= web.intermediate_label= @@ -50,8 +55,18 @@ web.set_intermediate= web.set_end= web.center_map= web.show_coords= +web.query_osm= web.route= +web.add_to_route= web.delete_from_route= +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker= web.gh_offline_info= web.refresh_button= @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=přez web.from_hint=wot web.gpx_export_button=eksport do GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=do web.route_info=za %1$s so trjeba %2$s web.search_button=pytaj @@ -74,6 +92,8 @@ web.more_button=wjac web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=Čara njeje móžna. Městno so w tutej kónčinje njenamaka. +web.search_with_nominatim= +web.powered_by= web.bike=koleso web.racingbike=wubědźowanske koleso web.mtb=mountainbike @@ -85,6 +105,9 @@ web.bus= web.truck= web.staticlink=link web.motorcycle=motorske +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/hu_HU.txt b/core/src/main/resources/com/graphhopper/util/hu_HU.txt index 0f03b8c4edf..d281ebcc45f 100644 --- a/core/src/main/resources/com/graphhopper/util/hu_HU.txt +++ b/core/src/main/resources/com/graphhopper/util/hu_HU.txt @@ -13,6 +13,9 @@ turn_slight_right=forduljon enyhén jobbra turn_sharp_left=forduljon élesen balra turn_sharp_right=forduljon élesen jobbra u_turn=forduljon meg +toward_destination=%1$s, és haladjon tovább erre: %2$s +toward_destination_ref_only=%1$s erre: %2$s +toward_destination_with_ref=%1$s, és haladjon tovább %2$s úton erre: %3$s unknown=ismeretlen jelzőtábla: %1$s via=ezen át: hour_abbr=óra @@ -33,14 +36,16 @@ stopover=%1$s. útpont roundabout_enter=Hajtson be a körforgalomba roundabout_exit=Hajtson ki a körforgalomból itt: %1$s. kijárat roundabout_exit_onto=Hajtson ki a körforgalomból itt: %1$s. kijárat, majd hajtson rá erre: %2$s -total_ascend=Összes szintemelkedés: %1$s -total_descend=Összes szintcsökkenés: %1$s -way_contains_ford=gázló van az útvonalon -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=szálljon fel erre: %1$s -pt_end_trip=szálljon le erről: %1$s +web.total_ascend=Összes szintemelkedés: %1$s +web.total_descend=Összes szintcsökkenés: %1$s +web.way_contains_ford=Az útvonalon gázló található +web.way_contains_ferry=Az útvonalon kompátkelés található +web.way_contains_private=Az útvonalon magánút is található +web.way_contains_toll=Az útvonalon útdíjat kell fizetni +web.way_crosses_border=Az útvonal országhatárt keresztez +web.way_contains=Az útvonalon előfordul %1$s +web.tracks=burkolatlan földút +web.steps=lépcső pt_transfer_to=szálljon át erre: %1$s web.start_label=Indulás web.intermediate_label=Köztes célpont @@ -50,8 +55,18 @@ web.set_intermediate=Beállítás köztes célpontnak web.set_end=Beállítás célpontnak web.center_map=Beállítás a térkép középpontjának web.show_coords=Pozíció megjelenítése +web.query_osm=OSM-lekérdezés web.route=Útvonal +web.add_to_route=Hely hozzáadása web.delete_from_route=Eltávolítás az útvonalról +web.open_custom_model_box=Egyedi modelldoboz megnyitása +web.help_custom_model=Súgó +web.apply_custom_model=Alkalmazás +web.exclude_motorway_example=Autópálya nélkül +web.limit_speed_example=Sebességkorlátozás +web.exclude_area_example=Terület elkerülése +web.combined_example=Kombinált példa +web.examples_custom_model=Példák web.marker=Jelölő web.gh_offline_info=Lehet, hogy a GraphHopper API nem érhető el? web.refresh_button=Oldal frissítése @@ -59,14 +74,17 @@ web.server_status=Állapot web.zoom_in=Nagyítás web.zoom_out=Kicsinyítés web.drag_to_reorder=Húzza el az átrendezéshez -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.route_timed_out=Időtúllépés az útvonaltervezéskor +web.route_request_failed=Útvonaltervezés sikertelen +web.current_location=Jelenlegi helyzet +web.searching_location=Helyzet keresése +web.searching_location_failed=A jelenlegi helyzet megtalálása sikertelen web.via_hint=Ezen keresztül web.from_hint=Innen web.gpx_export_button=GPX export +web.gpx_button=GPX +web.hide_button=Elrejtés +web.details_button=Részletek web.to_hint=Ide web.route_info=%1$s ennyi ideig tart: %2$s web.search_button=Keresés @@ -74,6 +92,8 @@ web.more_button=tovább web.pt_route_info=érkezés %1$s órakor, %2$s átszállással (%3$s) web.pt_route_info_walking=érkezés %1$s órakor, csak gyalog (%2$s) web.locations_not_found=Útvonaltervezés nem lehetséges. A megadott hely(ek) nem található(k) meg a területen. +web.search_with_nominatim=Keresés a Nominatim segítségével +web.powered_by=Motor: web.bike=Kerékpár web.racingbike=Versenykerékpár web.mtb=Hegyi kerékpár @@ -85,6 +105,9 @@ web.bus=Busz web.truck=Teherautó web.staticlink=Statikus hivatkozás web.motorcycle=Motorkerékpár +web.back_to_map=Vissza +web.distance_unit=Távolság mértékegysége: %1$s +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -94,5 +117,6 @@ navigate.in_mi_singular= navigate.in_mi= navigate.in_ft= navigate.for_mi= -navigate.warning= -navigate.accept_risks_after_warning= +navigate.warning=FIGYELEM! Ez az alkalmazás erősen kísérleti jellegű. Használat csak saját felelősségre! +navigate.accept_risks_after_warning=Megértettem és elfogadom +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/in_ID.txt b/core/src/main/resources/com/graphhopper/util/in_ID.txt index f936924cacf..4458152889b 100644 --- a/core/src/main/resources/com/graphhopper/util/in_ID.txt +++ b/core/src/main/resources/com/graphhopper/util/in_ID.txt @@ -13,6 +13,9 @@ turn_slight_right=belok kanan sedikit turn_sharp_left=belok kiri tajam turn_sharp_right=belok kanan tajam u_turn=putar balik +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=petunjuk baru %1$s via=melalui hour_abbr=jam @@ -33,14 +36,16 @@ stopover=titik hubung %1$s roundabout_enter=Masuk bundaran roundabout_exit=Pada bundaran, keluar melalui %1$s roundabout_exit_onto=At roundabout, take exit %1$s onto %2$s -total_ascend=naik dengan jarak %1$s -total_descend=turun dengan jarak %1$s -way_contains_ford=terdapat jalan untuk dilewati -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=masuk dalam %1$s -pt_end_trip=keluar dalam %1$s +web.total_ascend=naik dengan jarak %1$s +web.total_descend=turun dengan jarak %1$s +web.way_contains_ford=terdapat jalan untuk dilewati +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=berpindah ke jalur %1$s web.start_label=Mulai web.intermediate_label=Antara @@ -50,8 +55,18 @@ web.set_intermediate=Atur sebagai titik antara web.set_end=Atur sebagai titik akhir web.center_map=Tengahkan Peta web.show_coords=Tampilkan koordinat +web.query_osm= web.route=Rute +web.add_to_route= web.delete_from_route=Hapus dari rute +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Titik web.gh_offline_info=Pelayanan API Graphhopper dalam kondisi offline web.refresh_button=Perbarui Halaman @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=melalui web.from_hint=dari web.gpx_export_button=Ekspor GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=ke web.route_info=%1$s berada dalam waktu %2$s web.search_button=pencarian @@ -74,6 +92,8 @@ web.more_button=lebih lanjut web.pt_route_info=sampai pada %1$s dengan %2$s jarak (%3$s) web.pt_route_info_walking=sampai pada %1$s dengan berjalan kaki (%2$s) web.locations_not_found=Penentuan rute tidak dapat dilakukan. Lokasi tidak ditemukan +web.search_with_nominatim= +web.powered_by= web.bike=Sepeda web.racingbike=Sepeda Balap web.mtb=Sepeda Gunung @@ -85,6 +105,9 @@ web.bus=Bus web.truck=Truk web.staticlink=Jalur tetap web.motorcycle=Motor +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/it.txt b/core/src/main/resources/com/graphhopper/util/it.txt index 6805e183261..3f146fe6b37 100644 --- a/core/src/main/resources/com/graphhopper/util/it.txt +++ b/core/src/main/resources/com/graphhopper/util/it.txt @@ -2,7 +2,7 @@ continue=continua continue_onto=continua su %1$s -finish=Arrivato a destinazione +finish=arrivato a destinazione keep_left=tieni la sinistra keep_right=tieni la destra turn_onto=%1$s su %2$s @@ -10,11 +10,14 @@ turn_left=gira a sinistra turn_right=gira a destra turn_slight_left=gira leggermente a sinistra turn_slight_right=gira leggermente a destra -turn_sharp_left=gira nettamente a sinistra -turn_sharp_right=gira nettamente a destra -u_turn=fai una inversione a U +turn_sharp_left=gira a sinistra curva stretta +turn_sharp_right=gira a destra curva stretta +u_turn=fai inversione a U +toward_destination=%1$s e vai verso %2$s +toward_destination_ref_only=%1$s verso %2$s +toward_destination_with_ref=%1$s e prendi %2$s verso %3$s unknown=sconosciuto %1$s -via=via +via=attraverso hour_abbr=hh day_abbr=gg min_abbr=mm @@ -33,14 +36,16 @@ stopover=sosta %1$s roundabout_enter=Entrare nella rotatoria roundabout_exit=Nella rotatoria, prendere l'uscita %1$s roundabout_exit_onto=Nella rotatoria, prendere l'uscita %1$s su %2$s -total_ascend=%1$s di dislivello positivo -total_descend=%1$s di dislivello negativo -way_contains_ford=c'é un guado sulla strada -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=prendi %1$s -pt_end_trip=lascia %1$s +web.total_ascend=%1$s di dislivello positivo +web.total_descend=%1$s di dislivello negativo +web.way_contains_ford=Il percorso include un guado +web.way_contains_ferry=Il percorso include traghetti +web.way_contains_private=Il percorso include strade private +web.way_contains_toll=Il percorso include pedaggi +web.way_crosses_border=Il percorso attraversa un confine di stato +web.way_contains=Il percorso include %1$s +web.tracks=strade sterrate +web.steps=Passi pt_transfer_to=cambia con %1$s web.start_label=Partenza web.intermediate_label=Punto intermedio @@ -50,8 +55,18 @@ web.set_intermediate=Imposta punto intermedio web.set_end=Imposta arrivo web.center_map=Centra la mappa qui web.show_coords=Mostra coordinate +web.query_osm= web.route=Itinerario +web.add_to_route=Aggiungi destinazione web.delete_from_route=Elimina dall'itinerario +web.open_custom_model_box= +web.help_custom_model=Aiuto +web.apply_custom_model=Applica +web.exclude_motorway_example=Escludi autostrada +web.limit_speed_example=Limita velocità +web.exclude_area_example=Escludi area +web.combined_example=Esempio combinato +web.examples_custom_model=Esempi web.marker=Marker web.gh_offline_info=Servizio GraphHopper non disponibile? web.refresh_button=Ricarica pagina @@ -59,21 +74,26 @@ web.server_status=Stato web.zoom_in=Zoom avanti web.zoom_out=Zoom indietro web.drag_to_reorder=Trascina per riordinare -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= -web.via_hint=attraverso +web.route_timed_out=Calcolo del percorso fallito +web.route_request_failed=Richiesta della posizione fallita +web.current_location=Posizione attuale +web.searching_location=Ricerca posizione +web.searching_location_failed=Ricerca posizione fallita +web.via_hint=Attraverso web.from_hint=Da web.gpx_export_button=Esporta GPX +web.gpx_button= +web.hide_button=Nascondi +web.details_button=Dettagli web.to_hint=A web.route_info=%1$s in %2$s web.search_button=Ricerca web.more_button=altro web.pt_route_info=arrivato alle %1$s con %2$s trasferimenti (%3$s) -web.pt_route_info_walking=arrivato alle %1$s solocamminando (%2$s) +web.pt_route_info_walking=arrivato alle %1$s solo camminando (%2$s) web.locations_not_found=Percorso non calcolabile. Località non trovata(e) nell'area. +web.search_with_nominatim=Cerca con Nominatim +web.powered_by=Offerto da web.bike=Bicicletta web.racingbike=Bici da corsa web.mtb=Mountainbike @@ -85,14 +105,18 @@ web.bus=Tram web.truck=Camion web.staticlink=permalink web.motorcycle=Moto -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= -navigate.in_mi_singular= -navigate.in_mi= -navigate.in_ft= -navigate.for_mi= -navigate.warning= -navigate.accept_risks_after_warning= +web.back_to_map=Indietro +web.distance_unit=Le distanze sono in %1$s +web.waiting_for_gps=Aspettando il segnale GPS... +navigate.in_km_singular=Tra 1 chilometro +navigate.in_km=Tra %1$s chilometri +navigate.in_m=Tra %1$s metri +navigate.for_km=per %1$s chilometri +navigate.then=poi +navigate.in_mi_singular=Tra 1 miglio +navigate.in_mi=Tra %1$s miglia +navigate.in_ft=Tra %1$s piedi +navigate.for_mi=per %1$s miglia +navigate.warning=ATTENZIONE: Questa applicazione è ancora sperimentale! Utilizzala a tuo rischio e pericolo! +navigate.accept_risks_after_warning=Ho capito e accetto +navigate.start_navigation=Naviga diff --git a/core/src/main/resources/com/graphhopper/util/ja.txt b/core/src/main/resources/com/graphhopper/util/ja.txt index 9c38de4c2c5..edd0d020515 100644 --- a/core/src/main/resources/com/graphhopper/util/ja.txt +++ b/core/src/main/resources/com/graphhopper/util/ja.txt @@ -13,6 +13,9 @@ turn_slight_right=右に曲がる turn_sharp_left=左に曲がる turn_sharp_right=右に曲がる u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=経由 hour_abbr=時間 @@ -33,14 +36,16 @@ stopover=%1$sで降りる roundabout_enter=円形交差点に入る roundabout_exit=円形交差点の出口%1$sへ roundabout_exit_onto=円形交差点の出口%1$sから%2$sへ -total_ascend= -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend= +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label= web.intermediate_label= @@ -50,8 +55,18 @@ web.set_intermediate= web.set_end= web.center_map= web.show_coords=座標を表示 +web.query_osm= web.route=経路 +web.add_to_route= web.delete_from_route=経路から取り除く +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=ピン web.gh_offline_info=サーバに接続できませんでした web.refresh_button=更新 @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=経由 web.from_hint=出発地点 web.gpx_export_button=GPX形式でエクスポート +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=目的地点 web.route_info=%1$s 所要時間 %2$s web.search_button=検索 @@ -74,6 +92,8 @@ web.more_button=詳細 web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=経路を検索できませんでした.指定の地点が存在しません. +web.search_with_nominatim= +web.powered_by= web.bike=自転車 web.racingbike=レースバイク web.mtb=マウンテンバイク @@ -85,6 +105,9 @@ web.bus= web.truck= web.staticlink=パーマリンク web.motorcycle=オートバイ +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/ko.txt b/core/src/main/resources/com/graphhopper/util/ko.txt index 9d018f63df1..539676d3359 100644 --- a/core/src/main/resources/com/graphhopper/util/ko.txt +++ b/core/src/main/resources/com/graphhopper/util/ko.txt @@ -6,13 +6,16 @@ finish=도착 keep_left=좌측 유지 keep_right=우측 유지 turn_onto=%2$s로 %1$s -turn_left=TranslationMap +turn_left=좌회전 turn_right=우회전 turn_slight_left=왼쪽 방향 turn_slight_right=오른쪽 방향 turn_sharp_left=왼쪽 방향 turn_sharp_right=오른쪽 방향 u_turn=유턴 +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=알 수 없는 안내 '%1$s' via=경유 hour_abbr=시간 @@ -33,14 +36,16 @@ stopover=경유지 %1$s roundabout_enter=회전교차로 진입 roundabout_exit=회전교차로에서 %1$s 진출 roundabout_exit_onto=회전교차로에서 %1$s 진출 후 %2$s 이동 -total_ascend=오르막길 총 %1$s -total_descend=내리막길 총 %1$s -way_contains_ford=경로에 여울이 있습니다 -way_contains_ferry=페리 승선 -way_contains_private=사유 도로 -way_contains_toll=유료 도로 -pt_start_trip=%1$s 진입 -pt_end_trip=%1$s 진출 +web.total_ascend=오르막길 총 %1$s +web.total_descend=내리막길 총 %1$s +web.way_contains_ford=경로에 여울이 있습니다 +web.way_contains_ferry=페리 승선 +web.way_contains_private=사유 도로 +web.way_contains_toll=유료 도로 +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=%1$s(으)로 환승 web.start_label=출발지 web.intermediate_label=경유지 @@ -50,8 +55,18 @@ web.set_intermediate=경유지로 설정 web.set_end=도착지로 설정 web.center_map=여기를 지도의 중심으로 설정 web.show_coords=좌표 보기 +web.query_osm= web.route=경로 +web.add_to_route= web.delete_from_route=경로에서 삭제 +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=핀 web.gh_offline_info=GraphHopper API에 연결할 수 없습니다. web.refresh_button=새로고침 @@ -59,14 +74,17 @@ web.server_status=상태 web.zoom_in=확대 web.zoom_out=축소 web.drag_to_reorder=드래그하여 재정렬 -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.route_timed_out=경로 탐색 시간 초과 +web.route_request_failed=경로 탐색 요청 실패 +web.current_location=현재 위치 +web.searching_location=위치 탐색 +web.searching_location_failed=위티 탐색 실패 web.via_hint=경유 web.from_hint=출발 web.gpx_export_button=GPX 내보내기 +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=도착 web.route_info=%1$s %2$s 소요 web.search_button=탐색 @@ -74,6 +92,8 @@ web.more_button=더보기 web.pt_route_info=%1$s회 환승, %2$s에 도착 (%3$s) web.pt_route_info_walking=도보 이용, %1$s에 도착 (%2$s) web.locations_not_found=경로를 탐색할 수 없습니다. 이 지역에서 위치를 찾을 수 없습니다. +web.search_with_nominatim= +web.powered_by= web.bike=자전거 web.racingbike=경주용 자전거 web.mtb=산악자전거 @@ -85,6 +105,9 @@ web.bus=버스 web.truck=트럭 web.staticlink=정적 링크 web.motorcycle=모터사이클 +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/lt_LT.txt b/core/src/main/resources/com/graphhopper/util/lt_LT.txt index a9d221afd28..81ebf6bf4a1 100644 --- a/core/src/main/resources/com/graphhopper/util/lt_LT.txt +++ b/core/src/main/resources/com/graphhopper/util/lt_LT.txt @@ -13,6 +13,9 @@ turn_slight_right=laikykite dešiniau turn_sharp_left=staigiai sukite kairėn turn_sharp_right=staigiai sukite dešinėn u_turn=apsisukite +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=nežinomos instrukcijos žymėjimas '%1$s' via=per hour_abbr=h @@ -33,14 +36,16 @@ stopover=sustojimas %1$s roundabout_enter=Įvažiuokite į žiedą roundabout_exit=Žiede išvažiuokite %1$s išvažiavime roundabout_exit_onto=Žiede išvažiuokite %1$s išvažiavime į %2$s -total_ascend= -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend= +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=persėskite į %1$s web.start_label=Pradėti web.intermediate_label= @@ -50,8 +55,18 @@ web.set_intermediate= web.set_end=Pasirinkti pabaigą čia web.center_map=Centruoti žemėlapį čia web.show_coords=Rodyti koordinates +web.query_osm= web.route=Maršrutas +web.add_to_route= web.delete_from_route=Ištrinti iš maršruto +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Žymeklis web.gh_offline_info=GraphHopper API nepasiekiamas? web.refresh_button=Atnaujinti puslapį @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Per web.from_hint=Nuo web.gpx_export_button=GPX eksportas +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Iki web.route_info=%1$s užtruksite %2$s web.search_button=Ieškoti @@ -74,6 +92,8 @@ web.more_button=dar web.pt_route_info=Atvyksta %1$s su %2$s persėdimais (%3$s) web.pt_route_info_walking=Atvyksta %1$s, su ėjimu pėščiomis (%2$s) web.locations_not_found=Neįmanoma sukurti maršruto. Nurodyti taškai nerasti šioje zonoje. +web.search_with_nominatim= +web.powered_by= web.bike=Dviratis web.racingbike=Plentinis dviratis web.mtb=MTB dviratis @@ -85,6 +105,9 @@ web.bus=Autobusas web.truck=Sunkvežimis web.staticlink=statinė nuoroda web.motorcycle=Motociklas +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular=Už 1 kilometro navigate.in_km=Už %1$s kilometrų navigate.in_m=Už %1$s metrų @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/nb_NO.txt b/core/src/main/resources/com/graphhopper/util/nb_NO.txt new file mode 100644 index 00000000000..9750a20cc3f --- /dev/null +++ b/core/src/main/resources/com/graphhopper/util/nb_NO.txt @@ -0,0 +1,123 @@ +# do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh + +continue=fortsett +continue_onto=fortsett på %1$s +finish=fremme +keep_left=hold til venstre +keep_right=hold til høyre +turn_onto=%1$s på %2$s +turn_left=sving til venstre +turn_right=sving til høyre +turn_slight_left=svak venstre +turn_slight_right=svak høyre +turn_sharp_left=brå venstre +turn_sharp_right=brå høyre +u_turn=snu +toward_destination=%1$s mot %2$s +toward_destination_ref_only=%1$s mot %2$s +toward_destination_with_ref=%1$s og følg %2$s mot %3$s +unknown=ukjent skilting '%1$s' +via=via +hour_abbr=t +day_abbr=d +min_abbr=min +km_abbr=km +m_abbr=min +mi_abbr=mi +ft_abbr=ft +road=vei +off_bike=trill sykkelen +cycleway=sykkelfelt +way=vei +small_way=sti +paved=asfaltert +unpaved=grus +stopover=delmål %1$s +roundabout_enter=kjør inn i rundkjøringen +roundabout_exit=ta den %1$s avkjøringen i rundkjøringen +roundabout_exit_onto=I rundkjøringen, ta avkjørsel %1$s til %2$s +web.total_ascend=%1$s totale høydemeter +web.total_descend=%1$s total nedkjøring +web.way_contains_ford=ruten inneholder vadesteder +web.way_contains_ferry=ruten inneholder ferge +web.way_contains_private=ruten inneholder privat vei +web.way_contains_toll=ruten er avgiftsbelagt +web.way_crosses_border=ruten krysser landegrense +web.way_contains=ruten inneholder %1$s +web.tracks=grusvei +web.steps=trapper +pt_transfer_to=bytt til %1$s +web.start_label=Start +web.intermediate_label=delmål +web.end_label=Mål +web.set_start=Angi som start +web.set_intermediate=Angi delmål +web.set_end=Angi som mål +web.center_map=Sentrer kart +web.show_coords=Via koordinater +web.query_osm= +web.route=Rute +web.add_to_route=Legg til +web.delete_from_route=Fjern fra ruten +web.open_custom_model_box=Åpne tilpasser modellboks +web.help_custom_model=Hjelp +web.apply_custom_model=Bekreft +web.exclude_motorway_example=Unngå motorvei +web.limit_speed_example=Begrens hastighet +web.cargo_bike_example= +web.exclude_area_example=Unngå område +web.combined_example= +web.examples_custom_model=Eksempler +web.marker=Nål +web.gh_offline_info=GraphHopper API offline? +web.refresh_button=Oppdater side +web.server_status=Status +web.zoom_in=Zoom inn +web.zoom_out=Zoom ut +web.drag_to_reorder=Dra for å endre rekkefølge +web.route_timed_out= +web.route_request_failed= +web.current_location=Nåværende plassering +web.searching_location=Søker… +web.searching_location_failed=Søk feilet +web.via_hint=Via +web.from_hint=Fra +web.gpx_export_button=Eksporter GPX +web.gpx_button=GPX +web.hide_button=Skjul +web.details_button=Detaljer +web.to_hint=Til +web.route_info=%1$s vil ta %2$s +web.search_button=Søk +web.more_button=flere +web.pt_route_info=ankommer %1$s med %2$s bytter (%3$s) +web.pt_route_info_walking=ankommer %1$s ved å gå (%2$s) +web.locations_not_found=Ruteplanlegging mislykket. Destinasjon(ene) ikke funnet i valgt område +web.search_with_nominatim=Søk med Nominatim +web.powered_by=Powered by +web.bike=Sykkel +web.racingbike=Landeveisykkel +web.mtb=Mountain bike +web.car=Bil +web.foot=Gange +web.hike=Vandring +web.small_truck=Lett lastebil +web.bus=Buss +web.truck=Lastebil +web.staticlink=lenke +web.motorcycle=Motorsykkel +web.back_to_map=Tilbake +web.distance_unit=Avstand måles i %1$s +web.waiting_for_gps=Venter på GPS-signal +navigate.in_km_singular=Om 1 kilometer +navigate.in_km=Om %1$s kilometer +navigate.in_m=Om %1$s meter +navigate.for_km=I %1$s kilometer +navigate.then=deretter +navigate.in_mi_singular= +navigate.in_mi= +navigate.in_ft= +navigate.for_mi= +navigate.warning=VARSEL: Denne appen er svært eksperimentell. Brukes på eget ansvar! +navigate.accept_risks_after_warning=Jeg forstår og samtykker +navigate.start_navigation=Start navigasjon diff --git a/core/src/main/resources/com/graphhopper/util/ne.txt b/core/src/main/resources/com/graphhopper/util/ne.txt index 4716d331ab3..a95acb66488 100644 --- a/core/src/main/resources/com/graphhopper/util/ne.txt +++ b/core/src/main/resources/com/graphhopper/util/ne.txt @@ -13,6 +13,9 @@ turn_slight_right=थोरै दाया मोड्नुहोस turn_sharp_left=धेरै बाया मोड्नुहोस turn_sharp_right=धेरै दाया मोड्नुहोस u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=बाट hour_abbr=घण्टा @@ -33,14 +36,16 @@ stopover=%1$s रोकिने ठाउँ roundabout_enter=घुम्ती मा छिर्नुहोस roundabout_exit=घुम्तीमा %1$s नम्बर को मोडबाट निस्कनुहोस roundabout_exit_onto=घुम्तीमा %1$s नम्बर को मोडबाट निस्केर %2$s मा जानुहोस -total_ascend= -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend= +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label= web.intermediate_label= @@ -50,8 +55,18 @@ web.set_intermediate= web.set_end= web.center_map= web.show_coords= +web.query_osm= web.route= +web.add_to_route= web.delete_from_route= +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker= web.gh_offline_info= web.refresh_button= @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=बाट web.from_hint=सुरु web.gpx_export_button=GPX मा परिबर्तन गर्नुहोस +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=अन्त्य web.route_info=%1$s को लागि %2$s लाग्नेछ web.search_button=खोज @@ -74,6 +92,8 @@ web.more_button=अझै web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=ठाउँ नभेटीनाले बाटो पत्ता लगाउन सकिएन +web.search_with_nominatim= +web.powered_by= web.bike=बाईक web.racingbike=छिटो गतिका बाईक web.mtb=माउन्टेन बाईक @@ -85,6 +105,9 @@ web.bus= web.truck= web.staticlink=ईस्ट्यातिक लिंक web.motorcycle=मोटरसाइकल +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/nl.txt b/core/src/main/resources/com/graphhopper/util/nl.txt index 5ff275d39c6..94cd3034efc 100644 --- a/core/src/main/resources/com/graphhopper/util/nl.txt +++ b/core/src/main/resources/com/graphhopper/util/nl.txt @@ -13,6 +13,9 @@ turn_slight_right=houd rechts aan turn_sharp_left=ga linksaf turn_sharp_right=ga rechtsaf u_turn=keer om +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=onbekende richtingaanwijzer '%1$s' via=via hour_abbr=u @@ -33,14 +36,16 @@ stopover=marker %1$s roundabout_enter=ga de rotonde op roundabout_exit=neem afslag %1$s op de rotonde roundabout_exit_onto=neem afslag %1$s naar %2$s op de rotonde -total_ascend=%1$s totale klim -total_descend=%1$s totale daling -way_contains_ford=Er is een doorwaadbare plaats -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=Neem de %1$s -pt_end_trip=Stap uit de %1$s uit +web.total_ascend=%1$s totale klim +web.total_descend=%1$s totale daling +web.way_contains_ford=Er is een doorwaadbare plaats +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=Stap over op de %1$s web.start_label=Start web.intermediate_label=Tussenstop @@ -50,8 +55,18 @@ web.set_intermediate=Stel in als tussenstop web.set_end=Stel in als einde web.center_map=Centreer de kaart hier web.show_coords=Laat coordinaten zien +web.query_osm= web.route=Route +web.add_to_route= web.delete_from_route=Van route verwijderen +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Pin web.gh_offline_info=GrabHopper API offline? web.refresh_button=Ververs pagina @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=via web.from_hint=van web.gpx_export_button=GPX export +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=naar web.route_info=%1$s duurt %2$s web.search_button=zoek @@ -74,6 +92,8 @@ web.more_button=meer web.pt_route_info=aankomst om %1$s met %2$s overstappen (%3$s) web.pt_route_info_walking=aankomst om %1$s te voet (%2$s) web.locations_not_found=Route niet mogelijk. Locatie(s) niet gevonden. +web.search_with_nominatim= +web.powered_by= web.bike=fiets web.racingbike=racefiets web.mtb=mountainbike @@ -85,6 +105,9 @@ web.bus=bus web.truck=vrachtwagen web.staticlink=statische link web.motorcycle=motorfiets +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular=Over 1 kilometer navigate.in_km=Over %1$s kilometer navigate.in_m=Over %1$s meter @@ -96,3 +119,4 @@ navigate.in_ft=Over %1$s voet navigate.for_mi=Voor %1$s mijl navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/pl_PL.txt b/core/src/main/resources/com/graphhopper/util/pl_PL.txt index 28889957253..c3d827f9280 100644 --- a/core/src/main/resources/com/graphhopper/util/pl_PL.txt +++ b/core/src/main/resources/com/graphhopper/util/pl_PL.txt @@ -13,9 +13,12 @@ turn_slight_right=skręć delikatnie w prawo turn_sharp_left=skręć ostro w lewo turn_sharp_right=skręć ostro w prawo u_turn=zawróć +toward_destination=%1$s i jedź w kierunku %2$s +toward_destination_ref_only=%1$s w kierunku %2$s +toward_destination_with_ref=%1$s i jedź %2$s w kierunku %3$s unknown=nieznana instrukcja '%1$s' via=przez -hour_abbr=g +hour_abbr=h day_abbr=d min_abbr=min km_abbr=km @@ -27,20 +30,22 @@ off_bike=zejdź z roweru cycleway=trasa rowerowa way=trasa small_way=ścieżka -paved=utwierdzona +paved=utwardzona unpaved=nieutwardzona stopover=przystanek %1$s roundabout_enter=Wjedź na rondo roundabout_exit=Zjedź z ronda %1$s zjazdem roundabout_exit_onto=Zjedź z ronda %1$s zjazdem na %2$s -total_ascend=%1$s podejść ogółem -total_descend=%1$s zejść ogółem -way_contains_ford=bród na trasie -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=wsiądź w %1$s -pt_end_trip=wysiądź z %1$s +web.total_ascend=%1$s w górę +web.total_descend=%1$s w dół +web.way_contains_ford=Trasa przez brody +web.way_contains_ferry=Trasa obejmuje promy +web.way_contains_private=Trasa przez prywatne drogi +web.way_contains_toll=Trasa jest płatna +web.way_crosses_border=Trasa przebiega przez granicę państwową +web.way_contains=Trasa przez %1$s +web.tracks=nieutwardzone drogi gruntowe +web.steps=schody pt_transfer_to=przesiądź się na %1$s web.start_label=Początek web.intermediate_label=Punkt pośredni @@ -50,8 +55,18 @@ web.set_intermediate=Ustaw jako punkt pośredni web.set_end=Ustaw jako cel web.center_map=Centruj mapę tutaj web.show_coords=Pokaż współrzędne +web.query_osm=Wyświetl dane obiektów na OSM web.route=Trasa +web.add_to_route=Dodaj punkt pośredni web.delete_from_route=Usuń z trasy +web.open_custom_model_box=Otwórz okienko modelu niestandardowego +web.help_custom_model=Pomoc +web.apply_custom_model=Zastosuj +web.exclude_motorway_example=Pomijaj autostrady +web.limit_speed_example=Ogranicz prędkość +web.exclude_area_example=Wyklucz obszar +web.combined_example=Przykład łączony +web.examples_custom_model=Przykłady web.marker=Znacznik web.gh_offline_info=GraphHopper API nie działa? web.refresh_button=Odśwież stronę @@ -59,25 +74,30 @@ web.server_status=Stan web.zoom_in=Przybliż web.zoom_out=Oddal web.drag_to_reorder=Przeciągnij, aby przestawić -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.route_timed_out=Przekroczono limit czasu +web.route_request_failed=Wyznaczenie trasy nie powiodło się +web.current_location=Moja geolokalizacja +web.searching_location=Wyszukiwanie... +web.searching_location_failed=Nie udało się wyszukać lokalizacji web.via_hint=Przez web.from_hint=Z web.gpx_export_button=Eksportuj GPX +web.gpx_button=GPX +web.hide_button=Ukryj +web.details_button=Szczegóły web.to_hint=Do web.route_info=%1$s zajmie %2$s web.search_button=Szukaj web.more_button=więcej web.pt_route_info=przybycie o %1$s z %2$s przesiadkami (%3$s) web.pt_route_info_walking=przybycie o %1$s pieszo (%2$s) -web.locations_not_found=Nie można wyznaczyć trasy. Lokalizacja(e) nie została(y) znalezione. +web.locations_not_found=Nie można wyznaczyć trasy. Nie znaleziono lokalizacji. +web.search_with_nominatim=Szukaj z Nominatimem +web.powered_by=Dostarczony przez web.bike=Rower web.racingbike=Rower wyścigowy web.mtb=Rower górski -web.car=Samochod +web.car=Samochód web.foot=Pieszo web.hike=Szlakiem turystycznym web.small_truck=Mała ciężarówka @@ -85,14 +105,18 @@ web.bus=Autobus web.truck=Ciężarówka web.staticlink=link web.motorcycle=Motocykl -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= -navigate.in_mi_singular= -navigate.in_mi= -navigate.in_ft= +web.back_to_map=Wróć +web.distance_unit=Odległości w %1$s +web.waiting_for_gps=Oczekiwanie na sygnał GPS... +navigate.in_km_singular=Za 1 kilometr +navigate.in_km=Za %1$s km +navigate.in_m=Za %1$s m +navigate.for_km=przez %1$s kilometrów +navigate.then=, potem +navigate.in_mi_singular=Za 1 milę +navigate.in_mi=Za %1$s mi +navigate.in_ft=Za %1$s ft navigate.for_mi= -navigate.warning= -navigate.accept_risks_after_warning= +navigate.warning=OSTRZEŻENIE: Ta aplikacja jest wysoce eksperymentalna! Używaj na własne ryzyko! +navigate.accept_risks_after_warning=Rozumiem i akceptuję +navigate.start_navigation=Nawiguj diff --git a/core/src/main/resources/com/graphhopper/util/pt_BR.txt b/core/src/main/resources/com/graphhopper/util/pt_BR.txt index 757bff6d1f3..89229e72c13 100644 --- a/core/src/main/resources/com/graphhopper/util/pt_BR.txt +++ b/core/src/main/resources/com/graphhopper/util/pt_BR.txt @@ -13,6 +13,9 @@ turn_slight_right=curva suave à direita turn_sharp_left=curva acentuada à esquerda turn_sharp_right=curva acentuada à direita u_turn=faça um retorno +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=sinalização desconhecida '%1$s' via=via hour_abbr=h @@ -33,14 +36,16 @@ stopover=parada %1$s roundabout_enter=Entre na rotatória roundabout_exit=Na rotatória, saia na %1$s saída roundabout_exit_onto=Na rotatória, saia na %1$s saida em direção a %2$s -total_ascend=subida de %1$s -total_descend=descida de %1$s -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=embarque %1$s -pt_end_trip=desembarque %1$s +web.total_ascend=subida de %1$s +web.total_descend=descida de %1$s +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=mude para %1$s web.start_label=Início web.intermediate_label=Intermediário @@ -50,8 +55,18 @@ web.set_intermediate=Definir como intermediário web.set_end=Definir como fim web.center_map=Centralizar o mapa aqui web.show_coords=Mostrar coordenadas +web.query_osm= web.route=Rota +web.add_to_route= web.delete_from_route=Remover da rota +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Marca web.gh_offline_info=API do GraphHopper offline? web.refresh_button=Recarregar página @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Via web.from_hint=De web.gpx_export_button=Exportar GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Para web.route_info=%1$s levará %2$s web.search_button=Pesquisar @@ -74,6 +92,8 @@ web.more_button=mais web.pt_route_info=chega às %1$s com %2$s trocas (%3$s) web.pt_route_info_walking=chega às %1$s caminhando (%2$s) web.locations_not_found=Rota inviável. Localização(ões) não encontrada(s) na área. +web.search_with_nominatim= +web.powered_by= web.bike=Bicicleta web.racingbike=Bicicleta de corrida web.mtb=Mountainbike @@ -85,6 +105,9 @@ web.bus=Ônibus web.truck=Caminhão web.staticlink=Link estático web.motorcycle=Motocicleta +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/pt_PT.txt b/core/src/main/resources/com/graphhopper/util/pt_PT.txt index b4440b3e94a..b81b6139cc5 100644 --- a/core/src/main/resources/com/graphhopper/util/pt_PT.txt +++ b/core/src/main/resources/com/graphhopper/util/pt_PT.txt @@ -13,6 +13,9 @@ turn_slight_right=vire à curva ligeira à direita turn_sharp_left=vire à curva apertada à esquerda turn_sharp_right=vire à curva apertada à direita u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=por hour_abbr=h @@ -33,14 +36,16 @@ stopover=paragem %1$s roundabout_enter=Entre na rotunda roundabout_exit=Na rotunda, saia na %1$s saída roundabout_exit_onto=Na rotunda, saia na %1$s saida em direção a %2$s -total_ascend=subida de %1$s -total_descend=descida de %1$s -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend=subida de %1$s +web.total_descend=descida de %1$s +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label= web.intermediate_label= @@ -50,8 +55,18 @@ web.set_intermediate= web.set_end= web.center_map= web.show_coords=Mostrar coordenadas +web.query_osm= web.route= +web.add_to_route= web.delete_from_route= +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker= web.gh_offline_info= web.refresh_button= @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Por web.from_hint=De web.gpx_export_button=Exportar GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Para web.route_info=%1$s irá demorar %2$s web.search_button=Pesquisar @@ -74,6 +92,8 @@ web.more_button=mais web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=Roteamento impossível. Localização(ões) não encontrada(s) na área. +web.search_with_nominatim= +web.powered_by= web.bike=Bicicleta web.racingbike=Bicicleta de corrida web.mtb=Bicicleta de montanha @@ -85,6 +105,9 @@ web.bus=Autocarro web.truck=Camião web.staticlink=Ligação permanente web.motorcycle=Motocicleta +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/ro.txt b/core/src/main/resources/com/graphhopper/util/ro.txt index 4cb3693622f..60eb4f1a9cf 100644 --- a/core/src/main/resources/com/graphhopper/util/ro.txt +++ b/core/src/main/resources/com/graphhopper/util/ro.txt @@ -13,6 +13,9 @@ turn_slight_right=schimbați direcția la ușor la dreapta turn_sharp_left=schimbați direcția la brusc la stânga turn_sharp_right=schimbați direcția la brusc la dreapta u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=prin hour_abbr=h @@ -33,14 +36,16 @@ stopover=escala %1$s roundabout_enter=Intrați în sensul giratoriu roundabout_exit=În sensul giratoriu folosiți ieșirea %1$s roundabout_exit_onto=În sensul giratoriu folosiți ieșirea %1$s către %2$s -total_ascend=urcare %1$s -total_descend=coborâre %1$s -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend=urcare %1$s +web.total_descend=coborâre %1$s +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label=Pornire web.intermediate_label=Intermediar @@ -50,8 +55,18 @@ web.set_intermediate=Selectează ca punct intermediar web.set_end=Selectează ca punct de sosire web.center_map=Centrează harta aici web.show_coords=Arată coordonatele +web.query_osm= web.route=Intinerariu +web.add_to_route= web.delete_from_route=Șterge din intinerariu +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Punct de interes web.gh_offline_info=Serverul GraphHopper API este disponibil? web.refresh_button=Reîmprospătează pagina @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Prin web.from_hint=De la web.gpx_export_button=Exportă GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=La web.route_info=%1$s durează %2$s web.search_button=Caută @@ -74,6 +92,8 @@ web.more_button=mai mult web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=Traseul nu este posibil. Locul(locurile) nu pot fi găsite în zonă. +web.search_with_nominatim= +web.powered_by= web.bike=bicicletă web.racingbike=bicicletă de curse web.mtb=Mountainbike @@ -85,6 +105,9 @@ web.bus=Autobuz web.truck=Camion web.staticlink=link web.motorcycle=Motocicletă +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/ru.txt b/core/src/main/resources/com/graphhopper/util/ru.txt index d774030906f..7d305556e3d 100644 --- a/core/src/main/resources/com/graphhopper/util/ru.txt +++ b/core/src/main/resources/com/graphhopper/util/ru.txt @@ -13,6 +13,9 @@ turn_slight_right=Плавно поверните направо turn_sharp_left=Резко поверните налево turn_sharp_right=Резко поверните направо u_turn=Развернитесь +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=Неизвестная инструкция '%1$s' via=через hour_abbr=ч @@ -33,14 +36,16 @@ stopover=остановка %1$s roundabout_enter=Поверните на кольцо roundabout_exit=Сверните на %1$s-й съезд roundabout_exit_onto=Сверните на %1$s-й съезд на %2$s -total_ascend=подъём на %1$s -total_descend=спуск на %1$s -way_contains_ford=На пути есть брод -way_contains_ferry=садитесь на паром -way_contains_private=частная дорога -way_contains_toll=платная дорога -pt_start_trip=Сядьте на %1$s -pt_end_trip=Выйдите из %1$s +web.total_ascend=подъём на %1$s +web.total_descend=спуск на %1$s +web.way_contains_ford=На пути есть брод +web.way_contains_ferry=садитесь на паром +web.way_contains_private=частная дорога +web.way_contains_toll=платная дорога +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=Пересядьте на %1$s web.start_label=Начало web.intermediate_label=Промежуточная точка @@ -50,8 +55,18 @@ web.set_intermediate=Установить промежуточной точко web.set_end=Установить конец здесь web.center_map=Сдвинуть карту сюда web.show_coords=Показать координаты +web.query_osm= web.route=Маршрут +web.add_to_route= web.delete_from_route=Удалить из маршрута +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Значок web.gh_offline_info=GraphHopper API недоступен? web.refresh_button=Обновить страницу @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Через web.from_hint=От web.gpx_export_button=Экспорт GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=До web.route_info=%1$s займет %2$s web.search_button=Поиск @@ -74,6 +92,8 @@ web.more_button=еще web.pt_route_info=Прибытие в %1$s с %2$s пересадками (%3$s) web.pt_route_info_walking=Прибытие в %1$s пешком (%2$s)  web.locations_not_found=Построить маршрут невозможно. Не определено местоположение. +web.search_with_nominatim= +web.powered_by= web.bike=Велосипед web.racingbike=Гоночный велосипед web.mtb=Горный велосипед @@ -85,6 +105,9 @@ web.bus=Автобус web.truck=Грузовой автомобиль web.staticlink=Постоянная ссылка web.motorcycle=Мотоцикл +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular=В 1 километре navigate.in_km=В %1$s километрах navigate.in_m=В %1$s метрах @@ -96,3 +119,4 @@ navigate.in_ft=В %1$s футах navigate.for_mi=В %1$s милях navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/sk.txt b/core/src/main/resources/com/graphhopper/util/sk.txt index 0e8621a6ec3..367aa92f559 100644 --- a/core/src/main/resources/com/graphhopper/util/sk.txt +++ b/core/src/main/resources/com/graphhopper/util/sk.txt @@ -13,6 +13,9 @@ turn_slight_right=odbočte mierne doprava turn_sharp_left=odbočte ostro doľava turn_sharp_right=odbočte ostro doprava u_turn=otočte sa o 180° +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=cez hour_abbr=h @@ -33,14 +36,16 @@ stopover=zastávka %1$s roundabout_enter=Vojdite na kruhový objazd roundabout_exit=Na kruhovom objazde, ho opustite cez %1$s. výjazd roundabout_exit_onto=Na kruhovom objazde, ho opustite cez %1$s. výjazd na %2$s -total_ascend=%1$s celkové stúpanie -total_descend=%1$s celkové klesanie -way_contains_ford=popri ceste sa nachádza brod -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=nastúpte na linku %1$s -pt_end_trip=vystúpte z linky %1$s +web.total_ascend=%1$s celkové stúpanie +web.total_descend=%1$s celkové klesanie +web.way_contains_ford=popri ceste sa nachádza brod +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=prestúpte na linku %1$s web.start_label=Začiatok web.intermediate_label=Bod trasy @@ -50,8 +55,18 @@ web.set_intermediate=Nastaviť ako bod trasy web.set_end=Nastaviť ako koniec web.center_map=Vycentrovať mapu sem web.show_coords=Zobraziť súradnice +web.query_osm= web.route=Trasa +web.add_to_route= web.delete_from_route=Odstrániť z trasy +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Značka web.gh_offline_info=GraphHopper API bez pripojenia? web.refresh_button=Obnoviť stránku @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Cez web.from_hint=Odkiaľ web.gpx_export_button=Export do GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Kam web.route_info=%1$s zaberie %2$s web.search_button=Vyhľadať @@ -74,6 +92,8 @@ web.more_button=viac web.pt_route_info= web.pt_route_info_walking=príchod o %1$s iba chôdzou (%2$s) web.locations_not_found=Navigovanie nie je možné. Umiestnenie nebolo nájdené v oblasti. +web.search_with_nominatim= +web.powered_by= web.bike=Bicykel web.racingbike=Cestný bicykel web.mtb=Horský bicykel @@ -85,6 +105,9 @@ web.bus=Autobus web.truck=Jazdná súprava web.staticlink=nemenný odkaz web.motorcycle=Motocykel +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/sl_SI.txt b/core/src/main/resources/com/graphhopper/util/sl_SI.txt index 0d34bfe4e55..c81bf065049 100644 --- a/core/src/main/resources/com/graphhopper/util/sl_SI.txt +++ b/core/src/main/resources/com/graphhopper/util/sl_SI.txt @@ -13,6 +13,9 @@ turn_slight_right=zavijte rahlo desno turn_sharp_left=zavijte ostro levo turn_sharp_right=zavijte ostro desno u_turn=zavijte za 180° +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=neznano navodilo '%1$s' via=preko hour_abbr=ur @@ -33,14 +36,16 @@ stopover=postanek %1$s roundabout_enter=Zapeljite v krožišče roundabout_exit=V krožišču uporabite %1$s. izhod roundabout_exit_onto=V krožišču uporabite %1$s. izhod, da zavijete na %2$s -total_ascend=Skupni vzpon: %1$s -total_descend=Skupni spust: %1$s -way_contains_ford=Na poti je pregaz -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=Vstopite v/na %1$s -pt_end_trip=Zapustite %1$s +web.total_ascend=Skupni vzpon: %1$s +web.total_descend=Skupni spust: %1$s +web.way_contains_ford=Na poti je pregaz +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=Prestopite na %1$s web.start_label=Začetna točka web.intermediate_label=Vmesna točka @@ -50,8 +55,18 @@ web.set_intermediate=Nastavi kot vmesno točko web.set_end=Nastavi kot končno točko web.center_map=Premakni na sredino web.show_coords=Prikaži koordinate +web.query_osm= web.route=Pot +web.add_to_route= web.delete_from_route=Izbriši iz poti +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Oznaka web.gh_offline_info=Je API GraphHopper morda izklopljen? web.refresh_button=Osveži stran @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Preko web.from_hint=Od web.gpx_export_button=Izvozi GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Do web.route_info=%1$s bo trajalo %2$s web.search_button=Išči @@ -74,6 +92,8 @@ web.more_button=več web.pt_route_info=prispe ob %1$s s/z %2$s-imi prestopi (%3$s) web.pt_route_info_walking=prispe ob %1$s s samo hojo (%3$s) web.locations_not_found=Usmerjanje ni mogoče. Lokacije ni bilo mogoče najti na tem območju. +web.search_with_nominatim= +web.powered_by= web.bike=Kolo web.racingbike=Cestno kolo web.mtb=Gorsko kolo @@ -85,6 +105,9 @@ web.bus=Avtobus web.truck=Tovornjak web.staticlink=Povezava web.motorcycle=Motorno kolo +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/sr_RS.txt b/core/src/main/resources/com/graphhopper/util/sr_RS.txt index a74bd0d36e1..07e2b03c40f 100644 --- a/core/src/main/resources/com/graphhopper/util/sr_RS.txt +++ b/core/src/main/resources/com/graphhopper/util/sr_RS.txt @@ -13,6 +13,9 @@ turn_slight_right=skrenite blago desno turn_sharp_left=skrenite oštro levo turn_sharp_right=skrenite oštro desno u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=preko hour_abbr=h @@ -33,14 +36,16 @@ stopover=zaustavite se za %1$s roundabout_enter=Uđite na kružni tok roundabout_exit=Sa kružnog toka izađite na izlaz %1$s roundabout_exit_onto=Sa kružnog toka izađite na izlaz %1$s na %2$s -total_ascend=%1$s ukupni uspon -total_descend= -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend=%1$s ukupni uspon +web.total_descend= +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label=Početak web.intermediate_label=Prelazni @@ -50,8 +55,18 @@ web.set_intermediate=Postavi kao prelazni web.set_end=Postavi kao kraj web.center_map=Postavi kao centar karte web.show_coords=Prikaži koordinate +web.query_osm= web.route=Ruta +web.add_to_route= web.delete_from_route=Izbriši iz rute +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Oznaka web.gh_offline_info=GraphHopper API nedostupan web.refresh_button=Osveži stranicu @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Preko web.from_hint=Od web.gpx_export_button=Izvezi kao GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Do web.route_info=%1$s trajaće %2$s web.search_button=Pretraži @@ -74,6 +92,8 @@ web.more_button=više web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=Rutiranje nije moguće. Lokacije nisu pronađene na ovome području. +web.search_with_nominatim= +web.powered_by= web.bike=Bicikl web.racingbike=Trkački motocikl web.mtb=Brdski bicikl @@ -85,6 +105,9 @@ web.bus=Autobus web.truck=Kamion web.staticlink=statička veza web.motorcycle=Motocikl +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/sv_SE.txt b/core/src/main/resources/com/graphhopper/util/sv_SE.txt index b89a8c482c8..3ee9a377510 100644 --- a/core/src/main/resources/com/graphhopper/util/sv_SE.txt +++ b/core/src/main/resources/com/graphhopper/util/sv_SE.txt @@ -13,7 +13,10 @@ turn_slight_right=sväng svagt höger turn_sharp_left=sväng kraftigt vänster turn_sharp_right=sväng kraftigt höger u_turn=gör en u-sväng -unknown= +toward_destination= %1$s och kör mot %2$s +toward_destination_ref_only=%1$s mot %2$s +toward_destination_with_ref=%1$s och ta %2$s mot %3$s +unknown=okänd instruktionsskylt '%1$s' via=via hour_abbr= tim day_abbr= d @@ -33,14 +36,16 @@ stopover=delmål %1$s roundabout_enter=Kör in i rondellen roundabout_exit=I rondellen, ta avfart %1$s roundabout_exit_onto=I rondellen, ta avfart %1$s in på %2$s -total_ascend=%1$s stigning -total_descend=%1$s nedstigning -way_contains_ford=Observera att det finns ett vadställe längs rutten -way_contains_ferry=Ta färjan -way_contains_private=Privat väg -way_contains_toll=Avgiftsbelagd väg -pt_start_trip=ta %1$s -pt_end_trip=lämna %1$s +web.total_ascend=%1$s stigning +web.total_descend=%1$s nedstigning +web.way_contains_ford=Observera att det finns ett vadställe längs rutten +web.way_contains_ferry=Ta färjan +web.way_contains_private=Privat väg +web.way_contains_toll=Avgiftsbelagd väg +web.way_crosses_border= +web.way_contains=Rutten inkluderar %1$s +web.tracks= +web.steps=steg pt_transfer_to=byt till %1$s web.start_label=Start web.intermediate_label=delmål @@ -50,8 +55,19 @@ web.set_intermediate=ange delmål web.set_end=Ange som mål web.center_map=Centrera kartan här web.show_coords=Visa koordinater +web.query_osm= web.route=Rutt +web.add_to_route=Lägg till plats web.delete_from_route=Ta bort från rutt +web.open_custom_model_box= +web.help_custom_model=Hjälp +web.apply_custom_model=Applicera +web.exclude_motorway_example= +web.limit_speed_example= +web.cargo_bike_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model=Exempel web.marker=Markör web.gh_offline_info=GrabHopper API offline? web.refresh_button=Ladda om sida @@ -61,12 +77,15 @@ web.zoom_out=Zooma ut web.drag_to_reorder=Dra för att ändra ordning web.route_timed_out= web.route_request_failed= -web.current_location= -web.searching_location= +web.current_location=Nuvarande plats +web.searching_location=Söker plats web.searching_location_failed= web.via_hint=Via web.from_hint=Från web.gpx_export_button=exportera GPX-fil +web.gpx_button=GPX +web.hide_button=Göm +web.details_button=Detaljer web.to_hint=Till web.route_info=%1$s med körtid %2$s web.search_button=Sök @@ -74,6 +93,8 @@ web.more_button=fler web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=Kan inte beräkna rutt. Platsen eller platserna kan inte hittas i området. +web.search_with_nominatim= +web.powered_by= web.bike=Cykel web.racingbike=Tävlingscykel web.mtb=Mountain bike @@ -85,14 +106,18 @@ web.bus=Buss web.truck=Lastbil web.staticlink=direktlänk web.motorcycle=Motorcykel -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= -navigate.in_mi_singular= -navigate.in_mi= -navigate.in_ft= -navigate.for_mi= -navigate.warning= -navigate.accept_risks_after_warning= +web.back_to_map=Backa +web.distance_unit=Avstånden är i %1$s +web.waiting_for_gps=Väntar på GPS signal... +navigate.in_km_singular=Om 1 kilometer +navigate.in_km=Om %1$s kilometer +navigate.in_m=Om %1$s meter +navigate.for_km=i %1$s kilometer +navigate.then=sedan +navigate.in_mi_singular=Om 1 mil +navigate.in_mi=Om %1$s mil +navigate.in_ft=Om %1$s fot +navigate.for_mi=i %1$s mil +navigate.warning=VARNING: Sväng-för-sväng-navigeringen är mycket experimentell! Använd på egen risk! +navigate.accept_risks_after_warning=Jag förstår och godkänner +navigate.start_navigation=Navigera diff --git a/core/src/main/resources/com/graphhopper/util/tr.txt b/core/src/main/resources/com/graphhopper/util/tr.txt index 4e1cb95d426..f7bbb0796e1 100644 --- a/core/src/main/resources/com/graphhopper/util/tr.txt +++ b/core/src/main/resources/com/graphhopper/util/tr.txt @@ -1,79 +1,100 @@ # do not edit manually, instead use spreadsheet https://t.co/f086oJXAEI and script ./core/files/update-translations.sh continue=devam -continue_onto=%1$s üstünde devam -finish=Bitti! -keep_left=solu takip et -keep_right=sağı takip et +continue_onto=%1$s üstünde devam edin +finish=hedefe ulaştınız +keep_left=soldan ilerleyin +keep_right=sağdan ilerleyin turn_onto=%1$s üstünde %2$s -turn_left=sola dön -turn_right=sağa dön -turn_slight_left=sola hafif dön -turn_slight_right=sağa hafif dön -turn_sharp_left=sola keskin dön -turn_sharp_right=sağa keskin dön -u_turn=u dönüşü yap +turn_left=sola dönün +turn_right=sağa dönün +turn_slight_left=hafif sola dönün +turn_slight_right=hafif sağa dönün +turn_sharp_left=tam sola dönün +turn_sharp_right=tam sağa dönün +u_turn=u dönüşü yapın +toward_destination=%1$s dönün ve %2$s istikametinde hareket edin +toward_destination_ref_only=%2$s istikametinde %1$s dönün +toward_destination_with_ref=%1$s ardından %3$s istikametinde %2$s rotasını izleyin unknown=bilinmeyen yön işareti '%1$s' via=yoluyla -hour_abbr=s -day_abbr=g -min_abbr=dak +hour_abbr=sa +day_abbr=gün +min_abbr=dk km_abbr=km m_abbr=m mi_abbr=mi ft_abbr=ft road=yol -off_bike=bisikletten in +off_bike=bisikletten inin cycleway=bisiklet yolu way=yol small_way=patika -paved=kaldırım -unpaved=kaldırımsız yol -stopover=ara nokta %1$s -roundabout_enter=kavşağa gir -roundabout_exit=kavşaktan %1$s çıkışa git -roundabout_exit_onto=kavşaktan %1$s çıkıştan %2$s -total_ascend=%1$s toplam tırmanış -total_descend=%1$s toplam alçalış -way_contains_ford=Dikkat, yolda bir sığ yer var -way_contains_ferry=feribota bin -way_contains_private=özel yol -way_contains_toll=paralı yol -pt_start_trip=%1$s gir -pt_end_trip=%1$s çık +paved=asfalt yol +unpaved=Asfaltsız yol +stopover=varış noktası %1$s +roundabout_enter=dönel kavşağa gir +roundabout_exit=dönel kavşaktan %1$s çıkışa girin +roundabout_exit_onto=dönel kavşaktan %2$s üzerinde %1$s çıkışa girin +web.total_ascend=%1$s toplam tırmanış +web.total_descend=%1$s toplam alçalış +web.way_contains_ford=Dikkat, yolda bir sığ yer var +web.way_contains_ferry=feribota binin +web.way_contains_private=özel yol +web.way_contains_toll=Ücretli yol +web.way_crosses_border=Rota ülke sınırından geçmektedir +web.way_contains=Rota %1$s içermektedir +web.tracks=Asfaltsız toprak yol +web.steps= pt_transfer_to=%1$s geçiş yap web.start_label=Başlangıç -web.intermediate_label=Orta +web.intermediate_label=Ara nokta web.end_label=Son web.set_start=Başlangıç olarak ayarla -web.set_intermediate=Orta olarak ayarla +web.set_intermediate=Ara nokta olarak ayarla web.set_end=Bitiş olarak ayarla web.center_map=Haritayı buraya ortala web.show_coords=Koordinatları göster +web.query_osm= web.route=Rota +web.add_to_route=Konum ekle web.delete_from_route=Rotadan kaldır -web.marker=İşaretçi -web.gh_offline_info=GraphHopper API çevrimdışı? +web.open_custom_model_box= +web.help_custom_model=Yardım +web.apply_custom_model=Uygula +web.exclude_motorway_example= +web.limit_speed_example=Hız limiti +web.cargo_bike_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model=Örnekler +web.marker=İşaretler +web.gh_offline_info=GraphHopper arayüzüne ulaşılamıyor web.refresh_button=Sayfayı yinele web.server_status=Durum web.zoom_in=Yakınlaştır web.zoom_out=Uzaklaştır web.drag_to_reorder=Yeniden sıralamak için sürükle bırak -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.route_timed_out=Rota planlaması zaman aşımına uğradı +web.route_request_failed=Rota oluşturulamadı +web.current_location=Mevcut konum +web.searching_location=Konum aranıyor +web.searching_location_failed=Konum bulunamadı web.via_hint=Yoluyla web.from_hint=Şuradan web.gpx_export_button=GPX dışa aktar +web.gpx_button=GPX +web.hide_button=Gizle +web.details_button=Ayrıntılar web.to_hint=Yönüne web.route_info=%1$s alacak %2$s web.search_button=Ara web.more_button=daha fazlası web.pt_route_info=%2$s aktarma ile varış saati %1$s (%3$s) web.pt_route_info_walking=yürüyerek varış saati %1$s (%2$s) -web.locations_not_found=Rota planlanamadı. Bölgede yer bulunamadı. +web.locations_not_found=Rota oluşturulamadı. Bölgedeki yer(ler) bulunamadı. +web.search_with_nominatim=Nominatim ile ara +web.powered_by=... tarafından desteklenmektedir web.bike=Bisiklet web.racingbike=Yarış Bisikleti web.mtb=Dağ Bisikleti @@ -83,16 +104,20 @@ web.hike=Doğa yürüyüşü web.small_truck=Kamyonet web.bus=Otobüs web.truck=Kamyon -web.staticlink=durağan bağlantı +web.staticlink=kalıcı bağlantı web.motorcycle=motosiklet -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= -navigate.in_mi_singular= -navigate.in_mi= +web.back_to_map=geri +web.distance_unit=Mesafe birimi %1$s +web.waiting_for_gps=GPS sinyali bekleniyor +navigate.in_km_singular=1 kilometre sonra +navigate.in_km=%1$s kilometre sonra +navigate.in_m=%1$s metre sonra +navigate.for_km= %1$s kilometre boyunca +navigate.then=ardından +navigate.in_mi_singular=1 mil sonra +navigate.in_mi=%1$s mil sonra navigate.in_ft= -navigate.for_mi= +navigate.for_mi=%1$s mil boyunca navigate.warning= -navigate.accept_risks_after_warning= +navigate.accept_risks_after_warning=Anladım ve kabul ediyorum +navigate.start_navigation=Başla diff --git a/core/src/main/resources/com/graphhopper/util/uk.txt b/core/src/main/resources/com/graphhopper/util/uk.txt index 71839323467..74aa932bb27 100644 --- a/core/src/main/resources/com/graphhopper/util/uk.txt +++ b/core/src/main/resources/com/graphhopper/util/uk.txt @@ -13,6 +13,9 @@ turn_slight_right=Поверніть трохи правіше turn_sharp_left=Різко поверніть ліворуч turn_sharp_right=Різко поверніть праворуч u_turn=Розверніться +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=Невідома інструкція %1$s via=через hour_abbr= год @@ -33,14 +36,16 @@ stopover=зупинка %1$s roundabout_enter=В’їжджайте на кільце roundabout_exit=На кільці використовуйте з’їзд %1$s roundabout_exit_onto=На кільці використовуйте з’їзд %1$s на %2$s -total_ascend=%1$s загалом підйому -total_descend=%1$s загалом спуску -way_contains_ford=На шляху є брід -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=Сядьте на %1$s -pt_end_trip=Вийдіть з %1$s +web.total_ascend=%1$s загалом підйому +web.total_descend=%1$s загалом спуску +web.way_contains_ford=На шляху є брід +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=Пересядьте на %1$s web.start_label=Початок web.intermediate_label=Проміжна точка @@ -50,8 +55,18 @@ web.set_intermediate=Додати проміжну точку web.set_end=Встановити кінець web.center_map=Відцентрувати мапу тут web.show_coords=Показати координати +web.query_osm= web.route=Маршрут +web.add_to_route= web.delete_from_route=Вилучити з маршруту +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Значок web.gh_offline_info=GraphHopper API в офлайн режимі? web.refresh_button=Оновити сторінку @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=через web.from_hint=Від web.gpx_export_button=Експорт в GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=До web.route_info=%1$s займе %2$s web.search_button=Пошук @@ -74,6 +92,8 @@ web.more_button=ще web.pt_route_info=Прибуття до %1$s з %2$s пересадками (%3$s) web.pt_route_info_walking=Прибуття до %1$s пішки (%2$s)  web.locations_not_found=Побудова маршруту неможлива. Місцезнаходження не визначено. +web.search_with_nominatim= +web.powered_by= web.bike=Велосипед web.racingbike=Шосейний велосипед web.mtb=Гірський велосипед @@ -85,6 +105,9 @@ web.bus=Автобус web.truck=Велика вантажівка web.staticlink=статичне посилання web.motorcycle=Мотоцикл +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/vi_VN.txt b/core/src/main/resources/com/graphhopper/util/vi_VN.txt index 137c6d9be1c..8d4cd8a1d20 100644 --- a/core/src/main/resources/com/graphhopper/util/vi_VN.txt +++ b/core/src/main/resources/com/graphhopper/util/vi_VN.txt @@ -13,6 +13,9 @@ turn_slight_right=rẽ nhẹ sang phải turn_sharp_left=rẽ trái ngay lập tức turn_sharp_right=rẽ phải ngay lập tức u_turn=quay đầu xe +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=không xác định %1$s via=qua hour_abbr=giờ @@ -33,14 +36,16 @@ stopover=chặng dừng chân %1$s roundabout_enter=Đi vào vòng xoay roundabout_exit=Tại vòng xoay, rẽ lối rẽ %1$s roundabout_exit_onto=Tại vòng xoay, rẽ lối rẽ %1$s vào đường %2$s -total_ascend=Đi tiếp %1$s nữa -total_descend=Tổng số hạ %1$s -way_contains_ford=Chú ý, có khúc sông cạn trên đường -way_contains_ferry= -way_contains_private= -way_contains_toll=đường thu phí -pt_start_trip=nhập tuyến %1$s -pt_end_trip=rời tuyến %1$s +web.total_ascend=Đi tiếp %1$s nữa +web.total_descend=Tổng số hạ %1$s +web.way_contains_ford=Chú ý, có khúc sông cạn trên đường +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll=đường thu phí +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=chuyển sang tuyến %1$s web.start_label=Điểm bắt đầu web.intermediate_label=Điểm trung gian @@ -50,8 +55,18 @@ web.set_intermediate=Chọn điểm trung gian web.set_end=Chọn điểm kết thúc web.center_map=Trung tâm bản đồ web.show_coords=Xem tọa độ +web.query_osm= web.route=Lộ trình +web.add_to_route= web.delete_from_route=Xóa khỏi tuyến đường +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=Đánh dấu web.gh_offline_info=Mất kết nối với GH API web.refresh_button=Làm mới lại trang @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=Qua web.from_hint=Từ web.gpx_export_button=Xuất GPX +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=Đến web.route_info=%1$s mất %2$s web.search_button=Tìm @@ -74,6 +92,8 @@ web.more_button=thêm web.pt_route_info=Đến lúc %1$s với %2$s phương tiện(tuyến) (%3$s ) web.pt_route_info_walking=Đến lúc %1$s chỉ bằng cách đi bộ (%2$s) web.locations_not_found=Không tìm thấy lộ trình! Các điểm đã chọn không tìm thấy trong vùng này. +web.search_with_nominatim= +web.powered_by= web.bike=Xe đạp web.racingbike=Xe đạp đua web.mtb=Xe leo núi @@ -85,6 +105,9 @@ web.bus=Xe buýt web.truck=Xe tải web.staticlink=liên kết tĩnh web.motorcycle=Mô tô +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular=Trong 1 km navigate.in_km=Trong %1$s km navigate.in_m=Trong %1$s m @@ -96,3 +119,4 @@ navigate.in_ft=Trong %1$s ft navigate.for_mi=Khoảng %1$s dặm navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/zh_CN.txt b/core/src/main/resources/com/graphhopper/util/zh_CN.txt index ca2913a88a2..54d02d0fae6 100644 --- a/core/src/main/resources/com/graphhopper/util/zh_CN.txt +++ b/core/src/main/resources/com/graphhopper/util/zh_CN.txt @@ -2,10 +2,10 @@ continue=继续 continue_onto=继续行驶到 %1$s -finish=终点到达 +finish=到达终点 keep_left=保持左行 keep_right=保持右行 -turn_onto=%1$s 到 %2$s +turn_onto=%1$s到 %2$s turn_left=左转 turn_right=右转 turn_slight_left=偏左转 @@ -13,7 +13,10 @@ turn_slight_right=偏右转 turn_sharp_left=左急转 turn_sharp_right=右急转 u_turn=掉头 -unknown=未知指示标志 '%1$s' +toward_destination=%1$s,向 %2$s 行驶 +toward_destination_ref_only=往 %2$s 方向%1$s +toward_destination_with_ref=%1$s,由 %2$s 往 %3$s 方向行驶 +unknown=未知标志牌“%1$s” via=途经 hour_abbr=小时 day_abbr=天 @@ -29,19 +32,21 @@ way=路 small_way=小路 paved=路面铺就 unpaved=路面未铺就 -stopover=途中休息 %1$s +stopover=中途点 %1$s roundabout_enter=进入环岛 -roundabout_exit=在环岛内,使用%1$s出口出环岛 -roundabout_exit_onto=在环岛内,使用%1$s出口出环岛,进入%2$s -total_ascend=总上升%1$s -total_descend=总下降%1$s -way_contains_ford=路径中包含河滩 -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip=进入%1$s -pt_end_trip=离开%1$s -pt_transfer_to=变道进入%1$s +roundabout_exit=在环岛内,使用 %1$s 出口出环岛 +roundabout_exit_onto=在环岛内,使用 %1$s 出口出环岛,进入 %2$s +web.total_ascend=总上升 %1$s +web.total_descend=总下降 %1$s +web.way_contains_ford=路径中包含河滩 +web.way_contains_ferry=路径中包含轮渡 +web.way_contains_private=路径中包含私有道路 +web.way_contains_toll=路径中包含收费路段 +web.way_crosses_border=路径中跨越了国界 +web.way_contains=路径中包含%1$s +web.tracks=未铺设的土路 +web.steps=台阶 +pt_transfer_to=换乘%1$s web.start_label=起点 web.intermediate_label=途经点 web.end_label=终点 @@ -50,49 +55,68 @@ web.set_intermediate=设置为途经点 web.set_end=设置为终点 web.center_map=地图居中到这里 web.show_coords=显示坐标 +web.query_osm=查询 OSM web.route=路线 +web.add_to_route=增加位置 web.delete_from_route=从线路中移除 +web.open_custom_model_box=打开自定义模型选项 +web.help_custom_model=帮助 +web.apply_custom_model=应用 +web.exclude_motorway_example=排除高速公路 +web.limit_speed_example=限速 +web.exclude_area_example=排除区域 +web.combined_example=综合范例 +web.examples_custom_model=范例 web.marker=标记 -web.gh_offline_info=无法链接GraphHopper API +web.gh_offline_info=无法连接 GraphHopper API web.refresh_button=刷新网页 web.server_status=状态 web.zoom_in=放大 web.zoom_out=缩小 web.drag_to_reorder=拖动可重新排序 -web.route_timed_out= -web.route_request_failed= -web.current_location= -web.searching_location= -web.searching_location_failed= +web.route_timed_out=导航计算超时 +web.route_request_failed=导航请求失败 +web.current_location=当前位置 +web.searching_location=正在搜索位置 +web.searching_location_failed=搜索位置失败 web.via_hint=途经点 web.from_hint=起点 -web.gpx_export_button=GPX导出 +web.gpx_export_button=GPX 格式导出 +web.gpx_button=GPX +web.hide_button=隐藏 +web.details_button=详情 web.to_hint=终点 -web.route_info=%1$s 的路线,需要 %2$s 时间 +web.route_info=%1$s 的路线,需要 %2$s web.search_button=搜索 web.more_button=更多 -web.pt_route_info= -web.pt_route_info_walking=步行(%2$s), %1$s到达 -web.locations_not_found=地点未找到 +web.pt_route_info=换乘 %2$s 次,在 %1$s 到达 (%3$s) +web.pt_route_info_walking=仅需步行,在 %1$s 到达 (%2$s) +web.locations_not_found=无法导航,因为地点未找到。 +web.search_with_nominatim=用 Nominatim 搜索 +web.powered_by=Powered by web.bike=自行车 web.racingbike=竞技自行车 web.mtb=山地自行车 web.car=驾车 web.foot=步行 web.hike=徒步 -web.small_truck=小火车 +web.small_truck=小货车 web.bus=公交车 web.truck=卡车 -web.staticlink=静态链接 +web.staticlink=永久链接 web.motorcycle=摩托车 -navigate.in_km_singular= -navigate.in_km= -navigate.in_m= -navigate.for_km= -navigate.then= -navigate.in_mi_singular= -navigate.in_mi= -navigate.in_ft= -navigate.for_mi= -navigate.warning= -navigate.accept_risks_after_warning= +web.back_to_map=返回 +web.distance_unit=距离单位:%1$s +web.waiting_for_gps=正在搜索 GPS 信号…… +navigate.in_km_singular=1km 后 +navigate.in_km=%1$skm 后 +navigate.in_m=%1$sm 后 +navigate.for_km=行驶 %1$skm +navigate.then=然后 +navigate.in_mi_singular=1 英里后 +navigate.in_mi=%1$s 英里后 +navigate.in_ft=%1$s 英尺后 +navigate.for_mi=行驶 %1$s 英里 +navigate.warning=警告:该应用程序处于早期实验阶段,使用时风险自负! +navigate.accept_risks_after_warning=我理解并同意 +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/zh_HK.txt b/core/src/main/resources/com/graphhopper/util/zh_HK.txt index 6a545966dc9..a08ec249a49 100644 --- a/core/src/main/resources/com/graphhopper/util/zh_HK.txt +++ b/core/src/main/resources/com/graphhopper/util/zh_HK.txt @@ -13,6 +13,9 @@ turn_slight_right=右轉 turn_sharp_left=左急轉 turn_sharp_right=右急轉 u_turn= +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown= via=途經 hour_abbr=小時 @@ -33,14 +36,16 @@ stopover=中途站 %1$s roundabout_enter=進入迴旋處 roundabout_exit=使用 %1$s 出口離開迴旋處 roundabout_exit_onto=使用 %1$s 出口離開迴旋處到 %2$s -total_ascend=總共上昇 %1$s -total_descend=總共下降 %1$s -way_contains_ford= -way_contains_ferry= -way_contains_private= -way_contains_toll= -pt_start_trip= -pt_end_trip= +web.total_ascend=總共上昇 %1$s +web.total_descend=總共下降 %1$s +web.way_contains_ford= +web.way_contains_ferry= +web.way_contains_private= +web.way_contains_toll= +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to= web.start_label=起點 web.intermediate_label=途經點 @@ -50,8 +55,18 @@ web.set_intermediate=設置為途經點 web.set_end=設置為目的地 web.center_map= web.show_coords=顯示坐標 +web.query_osm= web.route=路線 +web.add_to_route= web.delete_from_route=從路線移除 +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=標記 web.gh_offline_info=無法連接 GraphHopper API web.refresh_button=刷新網頁 @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=途經 web.from_hint=起點 web.gpx_export_button=GPX格式輸出 +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=目的地 web.route_info=%1$s 需時 %2$s web.search_button=搜尋 @@ -74,6 +92,8 @@ web.more_button=更多 web.pt_route_info= web.pt_route_info_walking= web.locations_not_found=找不到起點/目的地 +web.search_with_nominatim= +web.powered_by= web.bike=單車 web.racingbike=競技單車 web.mtb=越野單車 @@ -85,6 +105,9 @@ web.bus=巴士 web.truck=貨車 web.staticlink=鏈接 web.motorcycle=電單車 +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -96,3 +119,4 @@ navigate.in_ft= navigate.for_mi= navigate.warning= navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/main/resources/com/graphhopper/util/zh_TW.txt b/core/src/main/resources/com/graphhopper/util/zh_TW.txt index d629c04b66a..6c7fd1debda 100644 --- a/core/src/main/resources/com/graphhopper/util/zh_TW.txt +++ b/core/src/main/resources/com/graphhopper/util/zh_TW.txt @@ -13,6 +13,9 @@ turn_slight_right=微靠右轉 turn_sharp_left=左急轉 turn_sharp_right=右急轉 u_turn=迴轉 +toward_destination= +toward_destination_ref_only= +toward_destination_with_ref= unknown=未知指示標誌 '%1$s' via=途經 hour_abbr=小時 @@ -33,14 +36,16 @@ stopover=中途點 %1$s roundabout_enter=進入圓環 roundabout_exit=於 %1$s 個出口離開圓環 roundabout_exit_onto=於 %1$s 個出口離開圓環,進入 %2$s -total_ascend=總共上昇 %1$s -total_descend=總共下降 %1$s -way_contains_ford=路徑中含有淺灘 -way_contains_ferry=搭乘渡輪 -way_contains_private=私人道路 -way_contains_toll=收費道路 -pt_start_trip=進入 %1$s -pt_end_trip=離開 %1$s +web.total_ascend=總共上昇 %1$s +web.total_descend=總共下降 %1$s +web.way_contains_ford=路徑中含有淺灘 +web.way_contains_ferry=搭乘渡輪 +web.way_contains_private=私人道路 +web.way_contains_toll=收費道路 +web.way_crosses_border= +web.way_contains= +web.tracks= +web.steps= pt_transfer_to=變換至 %1$s web.start_label=出發點 web.intermediate_label=途經點 @@ -50,8 +55,18 @@ web.set_intermediate=設為途經點 web.set_end=設為抵達點 web.center_map=設為地圖中心 web.show_coords=顯示坐標 +web.query_osm= web.route=路線 +web.add_to_route= web.delete_from_route=從路線中移除 +web.open_custom_model_box= +web.help_custom_model= +web.apply_custom_model= +web.exclude_motorway_example= +web.limit_speed_example= +web.exclude_area_example= +web.combined_example= +web.examples_custom_model= web.marker=標記 web.gh_offline_info=GraphHopper API 離線狀態? web.refresh_button=刷新頁面 @@ -67,6 +82,9 @@ web.searching_location_failed= web.via_hint=途經 web.from_hint=起點 web.gpx_export_button=匯出GPS +web.gpx_button= +web.hide_button= +web.details_button= web.to_hint=迄點 web.route_info=%1$s 需時 %2$s web.search_button=搜尋 @@ -74,6 +92,8 @@ web.more_button=更多 web.pt_route_info=於 %1$s 抵達,%2$s 次轉乘 (%3$s) web.pt_route_info_walking=於 %1$s 抵達,僅步行 (%2$s) web.locations_not_found=無法進行規劃。無法在此區域內找到指定的地點 +web.search_with_nominatim= +web.powered_by= web.bike=自行車 web.racingbike=競技自行車 web.mtb=登山車 @@ -85,6 +105,9 @@ web.bus=公車 web.truck=貨車 web.staticlink=永久鏈結 web.motorcycle=摩托車 +web.back_to_map= +web.distance_unit= +web.waiting_for_gps= navigate.in_km_singular= navigate.in_km= navigate.in_m= @@ -95,4 +118,5 @@ navigate.in_mi= navigate.in_ft= navigate.for_mi= navigate.warning= -navigate.accept_risks_after_warning= +navigate.accept_risks_after_warning= +navigate.start_navigation= diff --git a/core/src/test/java/com/graphhopper/GraphHopperProfileTest.java b/core/src/test/java/com/graphhopper/GraphHopperProfileTest.java index 6ba160ba7ab..6fcd9fb7a62 100644 --- a/core/src/test/java/com/graphhopper/GraphHopperProfileTest.java +++ b/core/src/test/java/com/graphhopper/GraphHopperProfileTest.java @@ -63,14 +63,14 @@ public void vehicleDoesNotExist_error() { final GraphHopper hopper = new GraphHopper(); hopper.setGraphHopperLocation(GH_LOCATION).setStoreOnFlush(false). setProfiles(new Profile("profile").setVehicle("your_car")); - assertIllegalArgument(hopper::load, "entry in encoder list not supported: your_car"); + assertIllegalArgument(hopper::importOrLoad, "entry in vehicle list not supported: your_car"); } @Test public void vehicleDoesNotExist_error2() { final GraphHopper hopper = new GraphHopper().setGraphHopperLocation(GH_LOCATION).setStoreOnFlush(false). setProfiles(new Profile("profile").setVehicle("your_car")); - assertIllegalArgument(hopper::load, "entry in encoder list not supported: your_car"); + assertIllegalArgument(hopper::importOrLoad, "entry in vehicle list not supported: your_car"); } @Test @@ -95,7 +95,7 @@ public void oneVehicleTwoProfilesWithAndWithoutTC2_noError() { public void profileWithUnknownWeighting_error() { final GraphHopper hopper = createHopper(); hopper.setProfiles(new Profile("profile").setVehicle("car").setWeighting("your_weighting")); - assertIllegalArgument(hopper::load, + assertIllegalArgument(hopper::importOrLoad, "Could not create weighting for profile: 'profile'", "Weighting 'your_weighting' not supported" ); @@ -106,7 +106,7 @@ public void chProfileDoesNotExist_error() { final GraphHopper hopper = createHopper(); hopper.setProfiles(new Profile("profile1").setVehicle("car")); hopper.getCHPreparationHandler().setCHProfiles(new CHProfile("other_profile")); - assertIllegalArgument(hopper::load, "CH profile references unknown profile 'other_profile'"); + assertIllegalArgument(hopper::importOrLoad, "CH profile references unknown profile 'other_profile'"); } @Test @@ -117,7 +117,7 @@ public void duplicateCHProfile_error() { new CHProfile("profile"), new CHProfile("profile") ); - assertIllegalArgument(hopper::load, "Duplicate CH reference to profile 'profile'"); + assertIllegalArgument(hopper::importOrLoad, "Duplicate CH reference to profile 'profile'"); } @Test @@ -125,7 +125,7 @@ public void lmProfileDoesNotExist_error() { final GraphHopper hopper = createHopper(); hopper.setProfiles(new Profile("profile1").setVehicle("car")); hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("other_profile")); - assertIllegalArgument(hopper::load, "LM profile references unknown profile 'other_profile'"); + assertIllegalArgument(hopper::importOrLoad, "LM profile references unknown profile 'other_profile'"); } @Test @@ -136,7 +136,7 @@ public void duplicateLMProfile_error() { new LMProfile("profile"), new LMProfile("profile") ); - assertIllegalArgument(hopper::load, "Multiple LM profiles are using the same profile 'profile'"); + assertIllegalArgument(hopper::importOrLoad, "Multiple LM profiles are using the same profile 'profile'"); } @Test @@ -146,7 +146,7 @@ public void unknownLMPreparationProfile_error() { hopper.getLMPreparationHandler().setLMProfiles( new LMProfile("profile").setPreparationProfile("xyz") ); - assertIllegalArgument(hopper::load, "LM profile references unknown preparation profile 'xyz'"); + assertIllegalArgument(hopper::importOrLoad, "LM profile references unknown preparation profile 'xyz'"); } @Test @@ -162,7 +162,7 @@ public void lmPreparationProfileChain_error() { new LMProfile("profile2").setPreparationProfile("profile1"), new LMProfile("profile3").setPreparationProfile("profile2") ); - assertIllegalArgument(hopper::load, "Cannot use 'profile2' as preparation_profile for LM profile 'profile3', because it uses another profile for preparation itself."); + assertIllegalArgument(hopper::importOrLoad, "Cannot use 'profile2' as preparation_profile for LM profile 'profile3', because it uses another profile for preparation itself."); } @Test @@ -176,7 +176,7 @@ public void noLMProfileForPreparationProfile_error() { hopper.getLMPreparationHandler().setLMProfiles( new LMProfile("profile1").setPreparationProfile("profile2") ); - assertIllegalArgument(hopper::load, "Unknown LM preparation profile 'profile2' in LM profile 'profile1' cannot be used as preparation_profile"); + assertIllegalArgument(hopper::importOrLoad, "Unknown LM preparation profile 'profile2' in LM profile 'profile1' cannot be used as preparation_profile"); } private GraphHopper createHopper() { diff --git a/core/src/test/java/com/graphhopper/GraphHopperTest.java b/core/src/test/java/com/graphhopper/GraphHopperTest.java index e1e8319319c..55eddf28409 100644 --- a/core/src/test/java/com/graphhopper/GraphHopperTest.java +++ b/core/src/test/java/com/graphhopper/GraphHopperTest.java @@ -20,11 +20,11 @@ import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; -import com.graphhopper.json.Statement; import com.graphhopper.reader.ReaderWay; import com.graphhopper.reader.dem.SRTMProvider; import com.graphhopper.reader.dem.SkadiProvider; import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.RoadEnvironment; import com.graphhopper.routing.ev.Subnetwork; import com.graphhopper.routing.util.AllEdgesIterator; @@ -36,6 +36,7 @@ import com.graphhopper.routing.util.parsers.TagParser; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.routing.weighting.custom.CustomProfile; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.IntsRef; import com.graphhopper.storage.index.LocationIndexTree; import com.graphhopper.storage.index.Snap; @@ -44,6 +45,7 @@ import com.graphhopper.util.Parameters.Landmark; import com.graphhopper.util.Parameters.Routing; import com.graphhopper.util.details.PathDetail; +import com.graphhopper.util.exceptions.ConnectionNotFoundException; import com.graphhopper.util.exceptions.MaximumNodesExceededException; import com.graphhopper.util.exceptions.PointDistanceExceededException; import com.graphhopper.util.shapes.BBox; @@ -63,6 +65,10 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import static com.graphhopper.json.Statement.If; +import static com.graphhopper.json.Statement.Op.MULTIPLY; +import static com.graphhopper.util.GHUtility.createCircle; +import static com.graphhopper.util.GHUtility.createRectangle; import static com.graphhopper.util.Parameters.Algorithms.*; import static com.graphhopper.util.Parameters.Curbsides.*; import static com.graphhopper.util.Parameters.Routing.U_TURN_COSTS; @@ -78,6 +84,7 @@ public class GraphHopperTest { // map locations private static final String BAYREUTH = DIR + "/north-bayreuth.osm.gz"; + private static final String BAUTZEN = DIR + "/bautzen.osm"; private static final String BERLIN = DIR + "/berlin-siegessaeule.osm.gz"; private static final String KREMS = DIR + "/krems.osm.gz"; private static final String LAUF = DIR + "/Laufamholzstrasse.osm.xml"; @@ -96,12 +103,12 @@ public void setup() { @ParameterizedTest @CsvSource({ - DIJKSTRA + ",false,511", - ASTAR + ",false,444", - DIJKSTRA_BI + ",false,228", - ASTAR_BI + ",false,184", - ASTAR_BI + ",true,49", - DIJKSTRA_BI + ",true,48" + DIJKSTRA + ",false,708", + ASTAR + ",false,363", + DIJKSTRA_BI + ",false,346", + ASTAR_BI + ",false,192", + ASTAR_BI + ",true,46", + DIJKSTRA_BI + ",true,51" }) public void testMonacoDifferentAlgorithms(String algo, boolean withCH, int expectedVisitedNodes) { final String vehicle = "car"; @@ -125,7 +132,7 @@ public void testMonacoDifferentAlgorithms(String algo, boolean withCH, int expec ResponsePath res = rsp.getBest(); assertEquals(3586.9, res.getDistance(), .1); - assertEquals(277112, res.getTime(), 10); + assertEquals(277115, res.getTime(), 10); assertEquals(91, res.getPoints().size()); assertEquals(43.7276852, res.getWaypoints().getLat(0), 1e-7); @@ -148,7 +155,7 @@ public void testMonacoWithInstructions() { setAlgorithm(ASTAR).setProfile(profile)); // identify the number of counts to compare with CH foot route - assertEquals(706, rsp.getHints().getLong("visited_nodes.sum", 0)); + assertEquals(713, rsp.getHints().getLong("visited_nodes.sum", 0)); ResponsePath res = rsp.getBest(); assertEquals(3437.1, res.getDistance(), .1); @@ -221,35 +228,61 @@ public void withoutInstructions() { } @Test - public void testUTurn() { + public void testUTurnInstructions() { final String profile = "profile"; final String vehicle = "car"; - final String weighting = "shortest"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). - setProfiles(new Profile(profile).setVehicle(vehicle).setWeighting(weighting)); + setProfiles(new CustomProfile(profile).setCustomModel(new CustomModel()).setVehicle(vehicle).setTurnCosts(true).putHint(U_TURN_COSTS, 20)); hopper.importOrLoad(); Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.US); - GHRequest request = new GHRequest(); - request.addPoint(new GHPoint(43.743887, 7.431151)); - request.addPoint(new GHPoint(43.744007, 7.431076)); - //Force initial U-Turn - request.setHeadings(Arrays.asList(200.)); + { + GHRequest request = new GHRequest(); + request.addPoint(new GHPoint(43.747418, 7.430371)); + request.addPoint(new GHPoint(43.746853, 7.42974)); + request.addPoint(new GHPoint(43.746929, 7.430458)); + request.setProfile(profile); + request.setCurbsides(Arrays.asList("right", "any", "right")); + GHResponse rsp = hopper.route(request); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); + ResponsePath res = rsp.getBest(); + assertEquals(286, res.getDistance(), 1); + // note that this includes the u-turn time for the second u-turn, but not the first, because it's a waypoint! + assertEquals(54351, res.getTime(), 1); + // the route follows Avenue de l'Annonciade to the waypoint, u-turns there, then does a sharp right turn onto the parallel (dead-end) road, + // does a u-turn at the dead-end and then arrives at the destination + InstructionList il = res.getInstructions(); + assertEquals(6, il.size()); + assertEquals("continue", il.get(0).getTurnDescription(tr)); + assertEquals("waypoint 1", il.get(1).getTurnDescription(tr)); + assertEquals("make a U-turn", il.get(2).getTurnDescription(tr)); + assertEquals("turn sharp right", il.get(3).getTurnDescription(tr)); + assertEquals("make a U-turn", il.get(4).getTurnDescription(tr)); + assertEquals("arrive at destination", il.get(5).getTurnDescription(tr)); + } - request.setAlgorithm(ASTAR).setProfile(profile); - GHResponse rsp = hopper.route(request); + { + GHRequest request = new GHRequest(); + request.addPoint(new GHPoint(43.743887, 7.431151)); + request.addPoint(new GHPoint(43.744007, 7.431076)); + //Force initial (two-lane) U-Turn + request.setHeadings(Arrays.asList(200.)); - assertFalse(rsp.hasErrors()); - ResponsePath res = rsp.getBest(); - InstructionList il = res.getInstructions(); - assertEquals(4, il.size()); + request.setProfile(profile); + GHResponse rsp = hopper.route(request); + + assertFalse(rsp.hasErrors()); + ResponsePath res = rsp.getBest(); + InstructionList il = res.getInstructions(); + assertEquals(4, il.size()); - // Initial U-turn - assertEquals("make a U-turn onto Avenue Princesse Grace", il.get(1).getTurnDescription(tr)); - // Second U-turn to get to destination - assertEquals("make a U-turn onto Avenue Princesse Grace", il.get(2).getTurnDescription(tr)); + // Initial (two-lane) U-turn + assertEquals("make a U-turn onto Avenue Princesse Grace", il.get(1).getTurnDescription(tr)); + // Second (two-lane) U-turn to get to destination + assertEquals("make a U-turn onto Avenue Princesse Grace", il.get(2).getTurnDescription(tr)); + } } private void testImportCloseAndLoad(boolean ch, boolean lm, boolean sort, boolean custom) { @@ -264,14 +297,15 @@ private void testImportCloseAndLoad(boolean ch, boolean lm, boolean sort, boolea Profile profile = new Profile(profileName).setVehicle(vehicle).setWeighting("fastest"); if (custom) { JsonFeature area51Feature = new JsonFeature(); + area51Feature.setId("area51"); area51Feature.setGeometry(new GeometryFactory().createPolygon(new Coordinate[]{ new Coordinate(7.4174, 43.7345), new Coordinate(7.4198, 43.7355), new Coordinate(7.4207, 43.7344), new Coordinate(7.4174, 43.7345)})); - CustomModel customModel = new CustomModel().setDistanceInfluence(0); - customModel.getPriority().add(Statement.If("in_area51", Statement.Op.MULTIPLY, 0.1)); - customModel.getAreas().put("area51", area51Feature); + CustomModel customModel = new CustomModel().setDistanceInfluence(0d); + customModel.getPriority().add(If("in_area51", MULTIPLY, "0.1")); + customModel.getAreas().getFeatures().add(area51Feature); profile = new CustomProfile(profileName).setCustomModel(customModel).setVehicle(vehicle); } hopper.setProfiles(profile); @@ -463,6 +497,7 @@ public void testAlternativeRoutesCar() { GHRequest req = new GHRequest(50.023513, 11.548862, 49.969441, 11.537876). setAlgorithm(ALT_ROUTE).setProfile(profile); req.putHint("alternative_route.max_paths", 3); + req.putHint("alternative_route.max_exploration_factor", 1.2); GHResponse rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); @@ -514,7 +549,31 @@ public void testPointHint() { } @Test - public void testNorthBayreuthDestination() { + public void testForwardBackwardDestination() { + final String profile = "profile"; + GraphHopper hopper = new GraphHopper(). + setGraphHopperLocation(GH_LOCATION). + setOSMFile(BAUTZEN). + setProfiles(new Profile(profile).setVehicle("car").setWeighting("fastest")); + hopper.setMinNetworkSize(0); + hopper.importOrLoad(); + + Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.US); + + GHResponse rsp = hopper.route(new GHRequest(51.1915, 14.416, 51.192, 14.412).setProfile(profile)); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); + assertEquals("keep right and take B 96 toward Bautzen-West, Hoyerswerda", + rsp.getBest().getInstructions().get(1).getTurnDescription(tr)); + assertEquals("turn left onto Hoyerswerdaer Straße and drive toward Hoyerswerda, Kleinwelka", + rsp.getBest().getInstructions().get(2).getTurnDescription(tr)); + + rsp = hopper.route(new GHRequest(51.191, 14.414, 51.1884, 14.41).setProfile(profile)); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); + assertEquals("turn left and take A 4 toward Dresden", rsp.getBest().getInstructions().get(1).getTurnDescription(tr)); + } + + @Test + public void testNorthBayreuthAccessDestination() { final String profile = "profile"; final String vehicle = "car"; final String weighting = "fastest"; @@ -537,12 +596,11 @@ public void testNorthBayreuthDestination() { public void testNorthBayreuthBlockedEdges() { final String profile = "profile"; final String vehicle = "car"; - final String weighting = "fastest"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). - setProfiles(new Profile(profile).setVehicle(vehicle).setWeighting(weighting)); + setProfiles(new CustomProfile(profile).setCustomModel(new CustomModel()).setVehicle(vehicle)); hopper.importOrLoad(); GHRequest req = new GHRequest(49.985272, 11.506151, 49.986107, 11.507202). @@ -552,8 +610,10 @@ public void testNorthBayreuthBlockedEdges() { assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(122, rsp.getBest().getDistance(), 1); - // block point 49.985759,11.50687 - req.putHint(Routing.BLOCK_AREA, "49.985759,11.50687"); + // block road at 49.985759,11.50687 + CustomModel customModel = new CustomModel().addToPriority(If("in_blocked_area", MULTIPLY, "0")); + customModel.getAreas().getFeatures().add(createCircle("blocked_area", 49.985759, 11.50687, 5)); + req.setCustomModel(customModel); rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(365, rsp.getBest().getDistance(), 1); @@ -565,49 +625,56 @@ public void testNorthBayreuthBlockedEdges() { assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(6685, rsp.getBest().getDistance(), 1); - // block by area - String someArea = "49.97986,11.472902,50.003946,11.534357"; - req.putHint(Routing.BLOCK_AREA, someArea); + // block rectangular area + customModel.getAreas().getFeatures().clear(); + customModel.getAreas().getFeatures().add(createRectangle("blocked_area", 49.97986, 11.472902, 50.003946, 11.534357)); + req.setCustomModel(customModel); rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(13988, rsp.getBest().getDistance(), 1); // Add blocked point to above area, to increase detour - req.putHint(Routing.BLOCK_AREA, "50.017578,11.547527;" + someArea); + customModel.getAreas().getFeatures().add(createCircle("blocked_point", 50.017578, 11.547527, 5)); + customModel.addToPriority(If("in_blocked_point", MULTIPLY, "0")); rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(14601, rsp.getBest().getDistance(), 1); // block by edge IDs -> i.e. use small circular area - req.putHint(Routing.BLOCK_AREA, "49.979929,11.520066,200"); + customModel = new CustomModel().addToPriority(If("in_blocked_area", MULTIPLY, "0")); + customModel.getAreas().getFeatures().add(createCircle("blocked_area", 49.979929, 11.520066, 200)); + req.setCustomModel(customModel); rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(12173, rsp.getBest().getDistance(), 1); - req.putHint(Routing.BLOCK_AREA, "49.980868,11.516397,150"); + customModel.getAreas().getFeatures().clear(); + customModel.getAreas().getFeatures().add(createCircle("blocked_area", 49.980868, 11.516397, 150)); rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(12173, rsp.getBest().getDistance(), 1); // block by edge IDs -> i.e. use small rectangular area - req.putHint(Routing.BLOCK_AREA, "49.981875,11.515818,49.979522,11.521407"); + customModel.getAreas().getFeatures().clear(); + customModel.getAreas().getFeatures().add(createRectangle("blocked_area", 49.981875, 11.515818, 49.979522, 11.521407)); rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(12173, rsp.getBest().getDistance(), 1); - // blocking works for all weightings req = new GHRequest(50.009504, 11.490669, 50.024726, 11.496162). setProfile(profile); rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(1807, rsp.getBest().getDistance(), 1); - req.putHint(Routing.BLOCK_AREA, "50.018277,11.492336"); + customModel.getAreas().getFeatures().clear(); + customModel.getAreas().getFeatures().add(createCircle("blocked_area", 50.018277, 11.492336, 5)); + req.setCustomModel(customModel); rsp = hopper.route(req); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(3363, rsp.getBest().getDistance(), 1); - // query point and snapped point are different => block snapped point only => show that block_area changes lookup + // query point and snapped point are different => block snapped point only => show that blocking an area changes lookup req = new GHRequest(49.984465, 11.507009, 49.986107, 11.507202). setProfile(profile); rsp = hopper.route(req); @@ -615,18 +682,28 @@ public void testNorthBayreuthBlockedEdges() { assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(155, rsp.getBest().getDistance(), 10); - req.putHint(Routing.BLOCK_AREA, "49.984434,11.505212,49.985394,11.506333"); + customModel.getAreas().getFeatures().clear(); + customModel.getAreas().getFeatures().add(createRectangle("blocked_area", 49.984434, 11.505212, 49.985394, 11.506333)); + req.setCustomModel(customModel); rsp = hopper.route(req); - assertEquals(11.508, rsp.getBest().getWaypoints().getLon(0), 0.001); + // we do not snap onto Grüngraben (within the blocked area), but onto Lohweg and then we need to go to Hauptstraße + // and turn left onto Waldhüttenstraße. Note that if we exclude footway we get an entirely different path, because + // the start point snaps all the way to the East onto the end of Bergstraße (because Lohweg gets no longer split + // into several edges...) + assertEquals(11.506, rsp.getBest().getWaypoints().getLon(0), 0.001); assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); - assertEquals(1185, rsp.getBest().getDistance(), 10); + assertEquals(510, rsp.getBest().getDistance(), 10); - // first point is contained in block_area => error + // first point is contained in blocked area => error req = new GHRequest(49.979, 11.516, 49.986107, 11.507202). setProfile(profile); - req.putHint(Routing.BLOCK_AREA, "49.981875,11.515818,49.979522,11.521407"); + customModel.getAreas().getFeatures().clear(); + customModel.getAreas().getFeatures().add(createRectangle("blocked_area", 49.981875, 11.515818, 49.979522, 11.521407)); + req.setCustomModel(customModel); rsp = hopper.route(req); assertTrue(rsp.hasErrors(), "expected errors"); + assertEquals(1, rsp.getErrors().size()); + assertTrue(rsp.getErrors().get(0) instanceof ConnectionNotFoundException); } @Test @@ -635,7 +712,7 @@ public void testCustomModel() { final String customCar = "custom_car"; final String emptyCar = "empty_car"; CustomModel customModel = new CustomModel(); - customModel.addToSpeed(Statement.If("road_class == TERTIARY || road_class == TRACK", Statement.Op.MULTIPLY, 0.1)); + customModel.addToSpeed(If("road_class == TERTIARY || road_class == TRACK", MULTIPLY, "0.1")); GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(BAYREUTH). @@ -653,16 +730,16 @@ public void testCustomModel() { assertDistance(hopper, emptyCar, new CustomModel(customModel), 13223); // now we prevent using unclassified roads as well and the route goes even further north CustomModel strictCustomModel = new CustomModel().addToSpeed( - Statement.If("road_class == TERTIARY || road_class == TRACK || road_class == UNCLASSIFIED", Statement.Op.MULTIPLY, 0.1)); + If("road_class == TERTIARY || road_class == TRACK || road_class == UNCLASSIFIED", MULTIPLY, "0.1")); assertDistance(hopper, emptyCar, strictCustomModel, 19289); // we can achieve the same by 'adding' a rule to the server-side custom model CustomModel customModelWithUnclassifiedRule = new CustomModel().addToSpeed( - Statement.If("road_class == UNCLASSIFIED", Statement.Op.MULTIPLY, 0.1) + If("road_class == UNCLASSIFIED", MULTIPLY, "0.1") ); assertDistance(hopper, customCar, customModelWithUnclassifiedRule, 19289); // now we use distance influence to avoid the detour - assertDistance(hopper, customCar, new CustomModel(customModelWithUnclassifiedRule).setDistanceInfluence(200), 8725); - assertDistance(hopper, customCar, new CustomModel(customModelWithUnclassifiedRule).setDistanceInfluence(100), 14475); + assertDistance(hopper, customCar, new CustomModel(customModelWithUnclassifiedRule).setDistanceInfluence(200d), 8725); + assertDistance(hopper, customCar, new CustomModel(customModelWithUnclassifiedRule).setDistanceInfluence(100d), 14475); } private void assertDistance(GraphHopper hopper, String profile, CustomModel customModel, double expectedDistance) { @@ -786,11 +863,11 @@ public void testMonacoPathDetails() { Map> details = res.getPathDetails(); assertEquals(1, details.size()); List detailList = details.get(Parameters.Details.AVERAGE_SPEED); - assertEquals(9, detailList.size()); + assertEquals(10, detailList.size()); assertEquals(5.0, detailList.get(0).getValue()); assertEquals(0, detailList.get(0).getFirst()); assertEquals(3.0, detailList.get(1).getValue()); - assertEquals(res.getPoints().size() - 1, detailList.get(8).getLast()); + assertEquals(res.getPoints().size() - 1, detailList.get(9).getLast()); } @Test @@ -1055,14 +1132,13 @@ public void testSRTMWithTunnelInterpolation(boolean withTunnelInterpolation) { if (!withTunnelInterpolation) { hopper.setTagParserFactory(new DefaultTagParserFactory() { @Override - public TagParser create(EncodedValueLookup lookup, String name) { - TagParser parser = super.create(lookup, name); + public TagParser create(EncodedValueLookup lookup, String name, PMap properties) { + TagParser parser = super.create(lookup, name, properties); if (name.equals("road_environment")) parser = new OSMRoadEnvironmentParser(lookup.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class)) { @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay readerWay, IntsRef relationFlags) { + public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay readerWay, IntsRef relationFlags) { // do not change RoadEnvironment to avoid triggering tunnel interpolation - return edgeFlags; } }; return parser; @@ -1186,24 +1262,21 @@ public void testSkadiElevationProvider() { @Test public void testKremsCyclewayInstructionsWithWayTypeInfo() { - final String profile1 = "foot_profile"; - final String profile2 = "bike_profile"; - final String vehicle1 = "foot"; - final String vehicle2 = "bike"; - final String weighting = "fastest"; + final String footProfile = "foot_profile"; + final String bikeProfile = "bike_profile"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(KREMS). setProfiles( - new Profile(profile1).setVehicle(vehicle1).setWeighting(weighting), - new Profile(profile2).setVehicle(vehicle2).setWeighting(weighting)). + new Profile(footProfile).setVehicle("foot").setWeighting("fastest"), + new CustomProfile(bikeProfile).setCustomModel(new CustomModel()).setVehicle("bike")). setStoreOnFlush(true). importOrLoad(); Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.US); GHResponse rsp = hopper.route(new GHRequest(48.410987, 15.599492, 48.383419, 15.659294). - setProfile(profile2)); + setProfile(bikeProfile)); assertFalse(rsp.hasErrors()); ResponsePath res = rsp.getBest(); assertEquals(6931.8, res.getDistance(), .1); @@ -1221,16 +1294,15 @@ public void testKremsCyclewayInstructionsWithWayTypeInfo() { assertEquals("keep left onto Hoher Markt", il.get(4).getTurnDescription(tr)); assertEquals("turn right onto Wegscheid", il.get(6).getTurnDescription(tr)); assertEquals("continue onto Wegscheid", il.get(7).getTurnDescription(tr)); - assertEquals("turn right onto Ringstraße, L73", il.get(8).getTurnDescription(tr)); + assertEquals("turn right onto Ringstraße", il.get(8).getTurnDescription(tr)); assertEquals("keep left onto Eyblparkstraße", il.get(9).getTurnDescription(tr)); assertEquals("keep left onto Austraße", il.get(10).getTurnDescription(tr)); assertEquals("keep left onto Rechte Kremszeile", il.get(11).getTurnDescription(tr)); //.. assertEquals("turn right onto Treppelweg", il.get(15).getTurnDescription(tr)); - // do not return 'get off bike' for foot rsp = hopper.route(new GHRequest(48.410987, 15.599492, 48.411172, 15.600371). - setAlgorithm(ASTAR).setProfile(profile1)); + setAlgorithm(ASTAR).setProfile(footProfile)); assertFalse(rsp.hasErrors()); il = rsp.getBest().getInstructions(); assertEquals("continue onto Obere Landstraße", il.get(0).getTurnDescription(tr)); @@ -1317,14 +1389,11 @@ public void testCircularJunctionInstructionsWithCH() { @Test public void testMultipleVehiclesWithCH() { - final String profile1 = "profile1"; - final String profile2 = "profile2"; - final String vehicle1 = "bike"; - final String vehicle2 = "car"; - final String weighting = "fastest"; + final String bikeProfile = "bike_profile"; + final String carProfile = "car_profile"; List profiles = asList( - new Profile(profile1).setVehicle(vehicle1).setWeighting(weighting), - new Profile(profile2).setVehicle(vehicle2).setWeighting(weighting) + new CustomProfile(bikeProfile).setCustomModel(new CustomModel()).setVehicle("bike"), + new Profile(carProfile).setVehicle("car").setWeighting("fastest") ); GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). @@ -1332,34 +1401,33 @@ public void testMultipleVehiclesWithCH() { setProfiles(profiles). setStoreOnFlush(true); hopper.getCHPreparationHandler().setCHProfiles( - new CHProfile(profile1), - new CHProfile(profile2) + new CHProfile(bikeProfile), + new CHProfile(carProfile) ); hopper.importOrLoad(); - String str = hopper.getEncodingManager().toString(); GHResponse rsp = hopper.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826) - .setProfile("profile2")); + .setProfile(carProfile)); ResponsePath res = rsp.getBest(); - assertFalse(rsp.hasErrors(), "car routing for " + str + " should not have errors:" + rsp.getErrors()); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(207, res.getTime() / 1000f, 1); assertEquals(2837, res.getDistance(), 1); rsp = hopper.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826) - .setProfile("profile1")); + .setProfile(bikeProfile)); res = rsp.getBest(); - assertFalse(rsp.hasErrors(), "bike routing for " + str + " should not have errors:" + rsp.getErrors()); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); assertEquals(511, res.getTime() / 1000f, 1); assertEquals(2481, res.getDistance(), 1); rsp = hopper.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826) .setProfile("profile3")); - assertTrue(rsp.hasErrors(), "only profile1 and profile2 exist, request for profile3 should fail"); + assertTrue(rsp.hasErrors(), "only car_profile and bike_profile exist, request for profile3 should fail"); GHRequest req = new GHRequest(). addPoint(new GHPoint(43.741069, 7.426854)). addPoint(new GHPoint(43.744445, 7.429483)). setHeadings(Arrays.asList(0., 190.)). - setProfile("profile1"); + setProfile(bikeProfile); rsp = hopper.route(req); assertTrue(rsp.hasErrors(), "heading not allowed for CH enabled graph"); @@ -1554,7 +1622,6 @@ public void testCrossQuery() { final String profile1 = "p1"; final String profile2 = "p2"; final String profile3 = "p3"; - final String vehicle = "car"; GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile(MONACO). @@ -1577,17 +1644,17 @@ public void testCrossQuery() { hopper.importOrLoad(); // flex - testCrossQueryAssert(profile1, hopper, 528.3, 166, true); - testCrossQueryAssert(profile2, hopper, 635.8, 160, true); - testCrossQueryAssert(profile3, hopper, 815.2, 158, true); + testCrossQueryAssert(profile1, hopper, 528.3, 196, true); + testCrossQueryAssert(profile2, hopper, 635.8, 198, true); + testCrossQueryAssert(profile3, hopper, 815.2, 198, true); // LM (should be the same as flex, but with less visited nodes!) - testCrossQueryAssert(profile1, hopper, 528.3, 74, false); - testCrossQueryAssert(profile2, hopper, 635.8, 124, false); - // this is actually interesting: the number of visited nodes *increases* once again (while it strictly decreases - // with rising distance factor for flex): cross-querying 'works', but performs *worse*, because the landmarks - // were not customized for the weighting in use. Creating a separate LM preparation for profile3 yields 74 - testCrossQueryAssert(profile3, hopper, 815.2, 162, false); + testCrossQueryAssert(profile1, hopper, 528.3, 108, false); + testCrossQueryAssert(profile2, hopper, 635.8, 162, false); + // this is actually interesting: the number of visited nodes increases: cross-querying 'works', + // but can even perform *worse*, because the landmarks were not customized for the weighting in use. + // Creating a separate LM preparation for profile3 yields 108 (not shown) + testCrossQueryAssert(profile3, hopper, 815.2, 202, false); } private void testCrossQueryAssert(String profile, GraphHopper hopper, double expectedWeight, int expectedVisitedNodes, boolean disableLM) { @@ -1598,6 +1665,53 @@ private void testCrossQueryAssert(String profile, GraphHopper hopper, double exp assertEquals(expectedVisitedNodes, visitedNodes); } + @Test + public void testLMConstraintsForCustomProfiles() { + GraphHopper hopper = new GraphHopper(). + setGraphHopperLocation(GH_LOCATION). + setOSMFile(MONACO). + setProfiles( + new CustomProfile("p1").setCustomModel(new CustomModel().setDistanceInfluence(100d)).setVehicle("car"), + new CustomProfile("p2").setCustomModel(new CustomModel().setDistanceInfluence(100d)).setVehicle("car")). + setStoreOnFlush(true); + + hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("p1")); + hopper.setMinNetworkSize(0); + hopper.importOrLoad(); + + GHResponse response = hopper.route(new GHRequest(43.727687, 7.418737, 43.74958, 7.436566). + setProfile("p1").putHint("lm.disable", false)); + assertEquals(3587, response.getBest().getDistance(), 1); + + // use smaller distance influence to force violating the LM constraint + final CustomModel customModel = new CustomModel().setDistanceInfluence(0d); + response = hopper.route(new GHRequest(43.727687, 7.418737, 43.74958, 7.436566). + setCustomModel(customModel). + setProfile("p1").putHint("lm.disable", false)); + assertTrue(response.hasErrors(), response.getErrors().toString()); + assertEquals(IllegalArgumentException.class, response.getErrors().get(0).getClass()); + + // but disabling LM must make it working as no LM is used + response = hopper.route(new GHRequest(43.727687, 7.418737, 43.74958, 7.436566). + setCustomModel(customModel). + setProfile("p1").putHint("lm.disable", true)); + assertFalse(response.hasErrors(), response.getErrors().toString()); + assertEquals(3587, response.getBest().getDistance(), 1); + + // currently required to disable LM for p2 too, see #1904 (default is LM for *all* profiles once LM preparation is enabled for any profile) + response = hopper.route(new GHRequest(43.727687, 7.418737, 43.74958, 7.436566). + setCustomModel(customModel). + setProfile("p2")); + assertTrue(response.getErrors().get(0).toString().contains("Cannot find LM preparation for the requested profile: 'p2'"), response.getErrors().toString()); + assertEquals(IllegalArgumentException.class, response.getErrors().get(0).getClass()); + + response = hopper.route(new GHRequest(43.727687, 7.418737, 43.74958, 7.436566). + setCustomModel(customModel). + setProfile("p2").putHint("lm.disable", true)); + assertFalse(response.hasErrors(), response.getErrors().toString()); + assertEquals(3587, response.getBest().getDistance(), 1); + } + @Test public void testCreateWeightingHintsMerging() { final String profile = "profile"; @@ -1697,7 +1811,7 @@ public void testDisablingLM() { req.putHint(Landmark.DISABLE, true); res = hopper.route(req); - assertTrue(res.getHints().getInt("visited_nodes.sum", 0) > 200); + assertTrue(res.getHints().getInt("visited_nodes.sum", 0) > 170); } @ParameterizedTest @@ -1717,7 +1831,7 @@ public void testCompareAlgos(boolean turnCosts) { long seed = System.nanoTime(); Random rnd = new Random(seed); for (int i = 0; i < 100; i++) { - BBox bounds = hopper.getGraphHopperStorage().getBounds(); + BBox bounds = hopper.getBaseGraph().getBounds(); double lat1 = bounds.minLat + rnd.nextDouble() * (bounds.maxLat - bounds.minLat); double lat2 = bounds.minLat + rnd.nextDouble() * (bounds.maxLat - bounds.minLat); double lon1 = bounds.minLon + rnd.nextDouble() * (bounds.maxLon - bounds.minLon); @@ -1798,9 +1912,9 @@ public void testIssue1960() { assertEquals(1995.38, pathLM.getDistance(), 0.1); assertEquals(1995.38, path.getDistance(), 0.1); - assertEquals(149497, pathCH.getTime()); - assertEquals(149497, pathLM.getTime()); - assertEquals(149497, path.getTime()); + assertEquals(149504, pathCH.getTime()); + assertEquals(149504, pathLM.getTime()); + assertEquals(149504, path.getTime()); } @Test @@ -1814,17 +1928,23 @@ public void testTurnCostsOnOff() { setOSMFile(MOSCOW). // add profile with turn costs first when no flag encoder is explicitly added setProfiles( - new Profile(profile2).setVehicle(vehicle).setWeighting(weighting).setTurnCosts(true), + new Profile(profile2).setVehicle(vehicle).setWeighting(weighting).setTurnCosts(true).putHint(U_TURN_COSTS, 30), new Profile(profile1).setVehicle(vehicle).setWeighting(weighting).setTurnCosts(false) ). setStoreOnFlush(true); hopper.importOrLoad(); GHRequest req = new GHRequest(55.813357, 37.5958585, 55.811042, 37.594689); + req.setPathDetails(Arrays.asList("distance", "time")).getHints().putObject("instructions", true); req.setProfile("profile_no_turn_costs"); - assertEquals(400, hopper.route(req).getBest().getDistance(), 1); + ResponsePath best = hopper.route(req).getBest(); + assertEquals(400, best.getDistance(), 1); + consistenceCheck(best); + req.setProfile("profile_turn_costs"); - assertEquals(1044, hopper.route(req).getBest().getDistance(), 1); + best = hopper.route(req).getBest(); + assertEquals(476, best.getDistance(), 1); + consistenceCheck(best); } @Test @@ -1945,9 +2065,9 @@ public void testEncoderWithTurnCostSupport_stillAllows_nodeBasedRouting() { @Test public void testOneWaySubnetwork_issue1807() { - // There is a straight-only turn relation at the junction of Franziskastraße and Gudulastraße, which restricts + // There is a straight-only turn restriction at the junction of Franziskastraße and Gudulastraße, which restricts // turning onto Gudulastraße. However, Gudulastraße can also not be accessed from the south/west, because - // its a one-way. This creates a subnetwork that is not accessible at all. We can only detect this if we + // it is a one-way. This creates a subnetwork that is not accessible at all. We can only detect this if we // consider the turn restrictions during the subnetwork search. GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). @@ -1976,6 +2096,33 @@ public void testOneWaySubnetwork_issue1807() { assertEquals(658, rsp.getBest().getDistance(), 1); } + @Test + public void testTagParserProcessingOrder() { + // it does not matter when the OSMBikeNetworkTagParser is added (before or even after BikeCommonPriorityParser) + // as it is a different type but it is important that OSMSmoothnessParser is added before smoothnessEnc is used + // in BikeCommonAverageSpeedParser + GraphHopper hopper = new GraphHopper(). + setGraphHopperLocation(GH_LOCATION). + setOSMFile(BAYREUTH). + setMinNetworkSize(0). + setProfiles(new CustomProfile("bike").setCustomModel(new CustomModel()).setVehicle("bike")); + + hopper.importOrLoad(); + GHRequest req = new GHRequest(new GHPoint(49.98021, 11.50730), new GHPoint(49.98026, 11.50795)); + req.setProfile("bike"); + GHResponse rsp = hopper.route(req); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); + // due to smoothness=bad => 7 seconds longer + assertEquals(21, rsp.getBest().getTime() / 1000.0, 1); + + req = new GHRequest(new GHPoint(50.015067, 11.502093), new GHPoint(50.014694, 11.499748)); + req.setProfile("bike"); + rsp = hopper.route(req); + assertFalse(rsp.hasErrors(), rsp.getErrors().toString()); + // due to bike network (relation 2247905) a lower route weight => otherwise 29.0 + assertEquals(23.2, rsp.getBest().getRouteWeight(), .1); + } + @Test public void testEdgeCount() { GraphHopper hopper = new GraphHopper(). @@ -1985,10 +2132,10 @@ public void testEdgeCount() { setProfiles(new Profile("car").setVehicle("car").setWeighting("fastest")); hopper.importOrLoad(); int count = 0; - AllEdgesIterator iter = hopper.getGraphHopperStorage().getAllEdges(); + AllEdgesIterator iter = hopper.getBaseGraph().getAllEdges(); while (iter.next()) count++; - assertEquals(hopper.getGraphHopperStorage().getEdges(), count); + assertEquals(hopper.getBaseGraph().getEdges(), count); } @Test @@ -2006,8 +2153,8 @@ public void testCurbsides() { GHPoint q = new GHPoint(50.014141, 11.497552); final String itz = "Itzgrund"; final String rotmain = "An den Rotmainauen"; - final String bayreuth = "Bayreuther Straße, KU 18"; - final String kulmbach = "Kulmbacher Straße, KU 18"; + final String bayreuth = "Bayreuther Straße"; + final String kulmbach = "Kulmbacher Straße"; final String adamSeiler = "Adam-Seiler-Straße"; final String friedhof = "Friedhofsweg"; assertCurbsidesPath(h, p, q, asList(CURBSIDE_RIGHT, CURBSIDE_RIGHT), 344, asList(itz, rotmain, rotmain)); @@ -2122,13 +2269,13 @@ public void testCHWithFiniteUTurnCosts() { GHRequest req = new GHRequest(p, q); req.setProfile("my_profile"); // we force the start/target directions such that there are u-turns right after we start and right before - // we reach the target + // we reach the target. at the start location we do a u-turn at the crossing with the *steps* ('ghost junction') req.setCurbsides(Arrays.asList("right", "right")); GHResponse res = h.route(req); assertFalse(res.hasErrors(), "routing should not fail"); - assertEquals(266.8, res.getBest().getRouteWeight(), 0.1); - assertEquals(2116, res.getBest().getDistance(), 1); - assertEquals(266800, res.getBest().getTime(), 1000); + assertEquals(242.9, res.getBest().getRouteWeight(), 0.1); + assertEquals(1917, res.getBest().getDistance(), 1); + assertEquals(243000, res.getBest().getTime(), 1000); } @Test @@ -2145,7 +2292,7 @@ public void simplifyWithInstructionsAndPathDetails() { .addPoint(new GHPoint(50.016895, 11.4923)) .addPoint(new GHPoint(50.003464, 11.49157)) .setProfile(profile) - .setPathDetails(Arrays.asList("street_name", "max_speed")); + .setPathDetails(Arrays.asList(KVStorage.KeyValue.STREET_REF, "max_speed")); req.putHint("elevation", true); GHResponse rsp = hopper.route(req); @@ -2166,37 +2313,39 @@ public void simplifyWithInstructionsAndPathDetails() { assertInstruction(instructions.get(0), "KU 11", "[0, 4[", 4, 4); assertInstruction(instructions.get(1), "B 85", "[4, 16[", 12, 12); // via instructions have length = 0, but the point list must not be empty! - assertInstruction(instructions.get(2), "", "[16, 17[", 0, 1); + assertInstruction(instructions.get(2), null, "[16, 17[", 0, 1); assertInstruction(instructions.get(3), "B 85", "[16, 32[", 16, 16); - assertInstruction(instructions.get(4), "", "[32, 34[", 2, 2); + assertInstruction(instructions.get(4), null, "[32, 34[", 2, 2); assertInstruction(instructions.get(5), "KU 18", "[34, 37[", 3, 3); assertInstruction(instructions.get(6), "St 2189", "[37, 38[", 1, 1); - assertInstruction(instructions.get(7), "", "[38, 40[", 2, 2); + assertInstruction(instructions.get(7), null, "[38, 40[", 2, 2); // finish instructions have length = 0, but the point list must not be empty! - assertInstruction(instructions.get(8), "", "[40, 41[", 0, 1); + assertInstruction(instructions.get(8), null, "[40, 41[", 0, 1); // check max speeds List speeds = path.getPathDetails().get("max_speed"); assertDetail(speeds.get(0), "null [0, 4]"); assertDetail(speeds.get(1), "70.0 [4, 6]"); - assertDetail(speeds.get(2), "100.0 [6, 31]"); - assertDetail(speeds.get(3), "80.0 [31, 32]"); - assertDetail(speeds.get(4), "null [32, 37]"); - assertDetail(speeds.get(5), "50.0 [37, 38]"); - assertDetail(speeds.get(6), "null [38, 40]"); - - // check street_names - List streetNames = path.getPathDetails().get("street_name"); + assertDetail(speeds.get(2), "100.0 [6, 16]"); + assertDetail(speeds.get(3), "100.0 [16, 31]"); // we do not merge path details at via points + assertDetail(speeds.get(4), "80.0 [31, 32]"); + assertDetail(speeds.get(5), "null [32, 37]"); + assertDetail(speeds.get(6), "50.0 [37, 38]"); + assertDetail(speeds.get(7), "null [38, 40]"); + + // check street names + List streetNames = path.getPathDetails().get(KVStorage.KeyValue.STREET_REF); assertDetail(streetNames.get(0), "KU 11 [0, 4]"); - assertDetail(streetNames.get(1), "B 85 [4, 32]"); - assertDetail(streetNames.get(2), " [32, 34]"); - assertDetail(streetNames.get(3), "KU 18 [34, 37]"); - assertDetail(streetNames.get(4), "St 2189 [37, 38]"); - assertDetail(streetNames.get(5), " [38, 40]"); + assertDetail(streetNames.get(1), "B 85 [4, 16]"); + assertDetail(streetNames.get(2), "B 85 [16, 32]"); + assertDetail(streetNames.get(3), "null [32, 34]"); + assertDetail(streetNames.get(4), "KU 18 [34, 37]"); + assertDetail(streetNames.get(5), "St 2189 [37, 38]"); + assertDetail(streetNames.get(6), "null [38, 40]"); } - private void assertInstruction(Instruction instruction, String expectedName, String expectedInterval, int expectedLength, int expectedPoints) { - assertEquals(expectedName, instruction.getName()); + private void assertInstruction(Instruction instruction, String expectedRef, String expectedInterval, int expectedLength, int expectedPoints) { + assertEquals(expectedRef, instruction.getExtraInfoJSON().get(KVStorage.KeyValue.STREET_REF)); assertEquals(expectedInterval, ((ShallowImmutablePointList) instruction.getPoints()).getIntervalString()); assertEquals(expectedLength, instruction.getLength()); assertEquals(expectedPoints, instruction.getPoints().size()); @@ -2206,6 +2355,52 @@ private void assertDetail(PathDetail detail, String expected) { assertEquals(expected, detail.toString()); } + @ParameterizedTest + @CsvSource(value = {"true,true", "true,false", "false,true", "false,false"}) + public void simplifyKeepsWaypoints(boolean elevation, boolean instructions) { + GraphHopper h = new GraphHopper(). + setGraphHopperLocation(GH_LOCATION). + setOSMFile(MONACO). + setProfiles(new Profile("car").setVehicle("car").setWeighting("fastest")); + if (elevation) + h.setElevationProvider(new SRTMProvider(DIR)); + h.importOrLoad(); + + List reqPoints = asList( + new GHPoint(43.741736, 7.428043), + new GHPoint(43.741248, 7.4274), + new GHPoint(43.73906, 7.426694), + new GHPoint(43.736337, 7.420592), + new GHPoint(43.735585, 7.419734), + new GHPoint(43.734857, 7.41909), + new GHPoint(43.73389, 7.418578), + new GHPoint(43.733204, 7.418755), + new GHPoint(43.731969, 7.416949) + ); + GHRequest req = new GHRequest(reqPoints).setProfile("car"); + req.putHint("instructions", instructions); + GHResponse res = h.route(req); + assertFalse(res.hasErrors()); + assertEquals(elevation ? 1828 : 1793, res.getBest().getDistance(), 1); + PointList points = res.getBest().getPoints(); + PointList wayPoints = res.getBest().getWaypoints(); + assertEquals(reqPoints.size(), wayPoints.size()); + assertEquals(points.is3D(), wayPoints.is3D()); + assertPointlistContainsSublist(points, wayPoints); + } + + private static void assertPointlistContainsSublist(PointList pointList, PointList subList) { + // we check if all points in sublist exist in pointlist, in the order given by sublist + int j = 0; + for (int i = 0; i < pointList.size(); i++) + if (pointList.getLat(i) == subList.getLat(j) && pointList.getLon(i) == subList.getLon(j) && (!pointList.is3D() || pointList.getEle(i) == subList.getEle(j))) + j++; + if (j != subList.size()) + fail("point list does not contain point " + j + " of sublist: " + subList.get(j) + + "\npoint list: " + pointList + + "\nsublist : " + subList); + } + @Test public void testNoLoad() { String profile = "profile"; @@ -2324,10 +2519,11 @@ public void testBarriers() { GraphHopper hopper = new GraphHopper(). setGraphHopperLocation(GH_LOCATION). setOSMFile("../map-matching/files/leipzig_germany.osm.pbf"). + setVehiclesString("car|block_private=false"). setProfiles( - new Profile("car").setVehicle("car").setWeighting("fastest"), - new Profile("bike").setVehicle("bike").setWeighting("fastest"), - new Profile("foot").setVehicle("foot").setWeighting("fastest") + new CustomProfile("car").setCustomModel(new CustomModel()).setVehicle("car"), + new CustomProfile("bike").setCustomModel(new CustomModel()).setVehicle("bike"), + new CustomProfile("foot").setCustomModel(new CustomModel()).setVehicle("foot") ). setMinNetworkSize(0); hopper.importOrLoad(); @@ -2389,6 +2585,28 @@ public void testBarriers() { bikeRsp = hopper.route(new GHRequest(51.355455, 12.40202, 51.355318, 12.401741).setProfile("bike")); assertEquals(24, bikeRsp.getBest().getDistance(), 1); } + + { + // node tag "ford" should be recognized in road_environment + GHResponse footRsp = hopper.route(new GHRequest(51.290141, 12.365849, 51.290996, 12.366155).setProfile("foot")); + assertEquals(105, footRsp.getBest().getDistance(), 1); + + footRsp = hopper.route(new GHRequest(51.290141, 12.365849, 51.290996, 12.366155). + setCustomModel(new CustomModel().addToPriority(If("road_environment == FORD", MULTIPLY, "0"))).setProfile("foot")); + assertEquals(330, footRsp.getBest().getDistance(), 1); + } + + { + // private access restriction as node tag + GHResponse rsp = hopper.route(new GHRequest(51.327411, 12.429598, 51.32723, 12.429979).setProfile("car")); + assertEquals(39, rsp.getBest().getDistance(), 1); + + rsp = hopper.route(new GHRequest(51.327411, 12.429598, 51.32723, 12.429979). + setCustomModel(new CustomModel().addToPriority(If("road_access == PRIVATE", MULTIPLY, "0"))). + setProfile("car")); + assertFalse(rsp.hasErrors()); + assertEquals(20, rsp.getBest().getDistance(), 1); + } } @Test @@ -2486,6 +2704,55 @@ void averageSpeedPathDetailBug() { assertEquals(467, distance, 1); } + @Test + void timeDetailBug() { + final String profile = "profile"; + GraphHopper hopper = new GraphHopper() + .setProfiles(new Profile(profile).setVehicle("car").setWeighting("fastest").setTurnCosts(true).putHint(U_TURN_COSTS, 80)) + .setGraphHopperLocation(GH_LOCATION) + .setMinNetworkSize(200) + .setOSMFile(BAYREUTH); + hopper.importOrLoad(); + GHRequest request = new GHRequest(Arrays.asList( + new GHPoint(50.020838, 11.494918), + new GHPoint(50.024795, 11.498973), + new GHPoint(50.023141, 11.496441))); + request.setProfile(profile); + request.getHints().putObject("instructions", true); + request.setPathDetails(Arrays.asList("distance", "time")); + GHResponse response = hopper.route(request); + assertFalse(response.hasErrors(), response.getErrors().toString()); + + consistenceCheck(response.getBest()); + } + + private void consistenceCheck(ResponsePath path) { + double distance = path.getDistance(); + long time = path.getTime(); + + double instructionDistance = 0; + long instructionTime = 0; + for (Instruction i : path.getInstructions()) { + instructionDistance += i.getDistance(); + instructionTime += i.getTime(); + } + + assertEquals(time, instructionTime); + assertEquals(distance, instructionDistance, 1e-3); + + double pathDetailDistance = 0; + for (PathDetail pd : path.getPathDetails().get("distance")) { + pathDetailDistance += (Double) pd.getValue(); + } + assertEquals(distance, pathDetailDistance, 1e-3); + + long pathDetailTime = 0; + for (PathDetail pd : path.getPathDetails().get("time")) { + pathDetailTime += (Long) pd.getValue(); + } + assertEquals(time, pathDetailTime); + } + @Test public void testLoadGraph_implicitEncodedValues_issue1862() { GraphHopper hopper = new GraphHopper() @@ -2496,10 +2763,10 @@ public void testLoadGraph_implicitEncodedValues_issue1862() { .setGraphHopperLocation(GH_LOCATION) .setOSMFile(BAYREUTH); hopper.importOrLoad(); - int nodes = hopper.getGraphHopperStorage().getNodes(); + int nodes = hopper.getBaseGraph().getNodes(); hopper.close(); - // load without configured FlagEncoders + // load without configured graph.vehicles hopper = new GraphHopper(); hopper.setProfiles(Arrays.asList( new Profile("p_car").setVehicle("car").setWeighting("fastest"), @@ -2507,21 +2774,46 @@ public void testLoadGraph_implicitEncodedValues_issue1862() { ); hopper.setGraphHopperLocation(GH_LOCATION); assertTrue(hopper.load()); - hopper.getGraphHopperStorage(); - assertEquals(nodes, hopper.getGraphHopperStorage().getNodes()); + hopper.getBaseGraph(); + assertEquals(nodes, hopper.getBaseGraph().getNodes()); hopper.close(); - // load via explicitly configured FlagEncoders + // load via explicitly configured graph.vehicles hopper = new GraphHopper(); - hopper.setFlagEncodersString("car,bike"); + hopper.setVehiclesString("car,bike"); hopper.setProfiles(Arrays.asList( new Profile("p_car").setVehicle("car").setWeighting("fastest"), new Profile("p_bike").setVehicle("bike").setWeighting("fastest")) ); hopper.setGraphHopperLocation(GH_LOCATION); assertTrue(hopper.load()); - assertEquals(nodes, hopper.getGraphHopperStorage().getNodes()); + assertEquals(nodes, hopper.getBaseGraph().getNodes()); hopper.close(); } + @Test + void testLoadingWithAnotherSpeedFactorWorks() { + { + GraphHopper hopper = new GraphHopper() + .setVehiclesString("car|speed_factor=7") + .setProfiles(new Profile("car").setVehicle("car").setWeighting("fastest")) + .setGraphHopperLocation(GH_LOCATION) + .setOSMFile(BAYREUTH); + hopper.importOrLoad(); + } + { + // now we use another speed_factor, but changing the flag encoder string has no effect when we are loading + // a graph. This API is a bit confusing, but we have been mixing configuration options that only matter + // during import with those that only matter when routing for some time already. At some point we should + // separate the 'import' from the 'routing' config (and split the GraphHopper class). + GraphHopper hopper = new GraphHopper() + .setVehiclesString("car|speed_factor=9") + .setProfiles(new Profile("car").setVehicle("car").setWeighting("fastest")) + .setGraphHopperLocation(GH_LOCATION); + hopper.load(); + assertEquals(2969, hopper.getBaseGraph().getNodes()); + } + } + } + diff --git a/core/src/test/java/com/graphhopper/isochrone/algorithm/ShortestPathTreeTest.java b/core/src/test/java/com/graphhopper/isochrone/algorithm/ShortestPathTreeTest.java index e39bae66fea..dc1939bdf31 100644 --- a/core/src/test/java/com/graphhopper/isochrone/algorithm/ShortestPathTreeTest.java +++ b/core/src/test/java/com/graphhopper/isochrone/algorithm/ShortestPathTreeTest.java @@ -1,16 +1,17 @@ package com.graphhopper.isochrone.algorithm; import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -59,15 +60,15 @@ public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { }; - private final EncodingManager encodingManager = EncodingManager.create("car"); - private final FlagEncoder carEncoder = encodingManager.getEncoder("car"); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); private BaseGraph graph; @BeforeEach public void setUp() { graph = new BaseGraph.Builder(encodingManager).create(); - // 8 // / // 0-1-2-3 @@ -75,31 +76,30 @@ public void setUp() { // 4-5-- | // |/ \--7 // 6----/ - GHUtility.setSpeed(10, true, false, carEncoder, ((Graph) graph).edge(0, 1).setDistance(70)); - GHUtility.setSpeed(20, true, false, carEncoder, ((Graph) graph).edge(0, 4).setDistance(50)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, ((Graph) graph).edge(0, 1).setDistance(70)); + GHUtility.setSpeed(20, true, false, accessEnc, speedEnc, ((Graph) graph).edge(0, 4).setDistance(50)); - GHUtility.setSpeed(10, true, true, carEncoder, ((Graph) graph).edge(1, 4).setDistance(70)); - GHUtility.setSpeed(10, true, true, carEncoder, ((Graph) graph).edge(1, 5).setDistance(70)); - GHUtility.setSpeed(10, true, true, carEncoder, ((Graph) graph).edge(1, 2).setDistance(200)); + GHUtility.setSpeed(10, true, true, accessEnc, speedEnc, ((Graph) graph).edge(1, 4).setDistance(70)); + GHUtility.setSpeed(10, true, true, accessEnc, speedEnc, ((Graph) graph).edge(1, 5).setDistance(70)); + GHUtility.setSpeed(10, true, true, accessEnc, speedEnc, ((Graph) graph).edge(1, 2).setDistance(200)); - GHUtility.setSpeed(10, true, false, carEncoder, ((Graph) graph).edge(5, 2).setDistance(50)); - GHUtility.setSpeed(10, true, false, carEncoder, ((Graph) graph).edge(2, 3).setDistance(50)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, ((Graph) graph).edge(5, 2).setDistance(50)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, ((Graph) graph).edge(2, 3).setDistance(50)); - GHUtility.setSpeed(20, true, false, carEncoder, ((Graph) graph).edge(5, 3).setDistance(110)); - GHUtility.setSpeed(10, true, false, carEncoder, ((Graph) graph).edge(3, 7).setDistance(70)); + GHUtility.setSpeed(20, true, false, accessEnc, speedEnc, ((Graph) graph).edge(5, 3).setDistance(110)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, ((Graph) graph).edge(3, 7).setDistance(70)); - GHUtility.setSpeed(20, true, false, carEncoder, ((Graph) graph).edge(4, 6).setDistance(50)); - GHUtility.setSpeed(10, true, false, carEncoder, ((Graph) graph).edge(5, 4).setDistance(70)); + GHUtility.setSpeed(20, true, false, accessEnc, speedEnc, ((Graph) graph).edge(4, 6).setDistance(50)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, ((Graph) graph).edge(5, 4).setDistance(70)); - GHUtility.setSpeed(10, true, false, carEncoder, ((Graph) graph).edge(5, 6).setDistance(70)); - GHUtility.setSpeed(20, true, false, carEncoder, ((Graph) graph).edge(7, 5).setDistance(50)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, ((Graph) graph).edge(5, 6).setDistance(70)); + GHUtility.setSpeed(20, true, false, accessEnc, speedEnc, ((Graph) graph).edge(7, 5).setDistance(50)); - GHUtility.setSpeed(20, true, true, carEncoder, ((Graph) graph).edge(6, 7).setDistance(50)); - GHUtility.setSpeed(20, true, true, carEncoder, ((Graph) graph).edge(3, 8).setDistance(25)); + GHUtility.setSpeed(20, true, true, accessEnc, speedEnc, ((Graph) graph).edge(6, 7).setDistance(50)); + GHUtility.setSpeed(20, true, true, accessEnc, speedEnc, ((Graph) graph).edge(3, 8).setDistance(25)); } private int countDirectedEdges(BaseGraph graph) { - BooleanEncodedValue accessEnc = carEncoder.getAccessEnc(); int result = 0; AllEdgesIterator iter = graph.getAllEdges(); while (iter.next()) { @@ -119,7 +119,7 @@ public void tearDown() { @Test public void testSPTAndIsochrone25Seconds() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(carEncoder, new PMap()), false, TraversalMode.NODE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(accessEnc, speedEnc), false, TraversalMode.NODE_BASED); instance.setTimeLimit(25_000); instance.search(0, result::add); assertEquals(3, result.size()); @@ -135,7 +135,7 @@ public void testSPTAndIsochrone25Seconds() { @Test public void testSPT26Seconds() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(carEncoder, new PMap()), false, TraversalMode.NODE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(accessEnc, speedEnc), false, TraversalMode.NODE_BASED); instance.setTimeLimit(26_000); instance.search(0, result::add); assertEquals(4, result.size()); @@ -150,7 +150,7 @@ public void testSPT26Seconds() { @Test public void testNoTimeLimit() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(carEncoder, new PMap()), false, TraversalMode.NODE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(accessEnc, speedEnc), false, TraversalMode.NODE_BASED); instance.setTimeLimit(Double.MAX_VALUE); instance.search(0, result::add); assertEquals(9, result.size()); @@ -170,7 +170,7 @@ public void testNoTimeLimit() { @Test public void testEdgeBasedWithFreeUTurns() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(carEncoder, new PMap()), false, TraversalMode.EDGE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(accessEnc, speedEnc), false, TraversalMode.EDGE_BASED); instance.setTimeLimit(Double.MAX_VALUE); instance.search(0, result::add); // The origin, and every end of every directed edge, are traversed. @@ -202,7 +202,7 @@ public void testEdgeBasedWithFreeUTurns() { @Test public void testEdgeBasedWithForbiddenUTurns() { - FastestWeighting fastestWeighting = new FastestWeighting(carEncoder, new PMap(), FORBIDDEN_UTURNS); + FastestWeighting fastestWeighting = new FastestWeighting(accessEnc, speedEnc, FORBIDDEN_UTURNS); List result = new ArrayList<>(); ShortestPathTree instance = new ShortestPathTree(graph, fastestWeighting, false, TraversalMode.EDGE_BASED); instance.setTimeLimit(Double.MAX_VALUE); @@ -236,7 +236,7 @@ public void testEdgeBasedWithForbiddenUTurns() { @Test public void testEdgeBasedWithFinitePositiveUTurnCost() { TimeBasedUTurnCost turnCost = new TimeBasedUTurnCost(80000); - FastestWeighting fastestWeighting = new FastestWeighting(carEncoder, new PMap(), turnCost); + FastestWeighting fastestWeighting = new FastestWeighting(accessEnc, speedEnc, turnCost); List result = new ArrayList<>(); ShortestPathTree instance = new ShortestPathTree(graph, fastestWeighting, false, TraversalMode.EDGE_BASED); instance.setTimeLimit(Double.MAX_VALUE); @@ -271,7 +271,7 @@ public void testEdgeBasedWithFinitePositiveUTurnCost() { @Test public void testEdgeBasedWithSmallerUTurnCost() { TimeBasedUTurnCost turnCost = new TimeBasedUTurnCost(20000); - FastestWeighting fastestWeighting = new FastestWeighting(carEncoder, new PMap(), turnCost); + FastestWeighting fastestWeighting = new FastestWeighting(accessEnc, speedEnc, turnCost); List result = new ArrayList<>(); ShortestPathTree instance = new ShortestPathTree(graph, fastestWeighting, false, TraversalMode.EDGE_BASED); instance.setTimeLimit(Double.MAX_VALUE); @@ -306,7 +306,7 @@ public void testEdgeBasedWithSmallerUTurnCost() { @Test public void testSearchByDistance() { List result = new ArrayList<>(); - ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(carEncoder, new PMap()), false, TraversalMode.NODE_BASED); + ShortestPathTree instance = new ShortestPathTree(graph, new FastestWeighting(accessEnc, speedEnc), false, TraversalMode.NODE_BASED); instance.setDistanceLimit(110.0); instance.search(5, result::add); assertEquals(6, result.size()); diff --git a/core/src/test/java/com/graphhopper/reader/dem/BridgeElevationInterpolatorTest.java b/core/src/test/java/com/graphhopper/reader/dem/BridgeElevationInterpolatorTest.java index ad0c3e78eeb..49f7985ec6a 100644 --- a/core/src/test/java/com/graphhopper/reader/dem/BridgeElevationInterpolatorTest.java +++ b/core/src/test/java/com/graphhopper/reader/dem/BridgeElevationInterpolatorTest.java @@ -19,8 +19,6 @@ import com.graphhopper.coll.GHIntHashSet; import com.graphhopper.routing.ev.RoadEnvironment; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.storage.IntsRef; import com.graphhopper.storage.NodeAccess; import com.graphhopper.util.*; import org.junit.jupiter.api.Test; @@ -33,10 +31,8 @@ public class BridgeElevationInterpolatorTest extends EdgeElevationInterpolatorTest { @Override - protected IntsRef createInterpolatableFlags() { - IntsRef edgeFlags = new IntsRef(1); - roadEnvEnc.setEnum(false, edgeFlags, RoadEnvironment.BRIDGE); - return edgeFlags; + protected RoadEnvironment getInterpolatableRoadEnvironment() { + return RoadEnvironment.BRIDGE; } @Override @@ -71,9 +67,8 @@ public void interpolatesElevationOfPillarNodes() { na.setNode(8, 30, 10, 10); na.setNode(9, 40, 10, 0); - FlagEncoder encoder = encodingManager.getEncoder("car"); EdgeIteratorState edge01, edge12, edge23, edge34, edge56, edge67, edge78, edge89, edge17, edge27, edge37; - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, edge01 = graph.edge(0, 1).setDistance(10), edge12 = graph.edge(1, 2).setDistance(10), edge23 = graph.edge(2, 3).setDistance(10), @@ -88,19 +83,19 @@ public void interpolatesElevationOfPillarNodes() { edge17.setWayGeometry(Helper.createPointList3D(12, 2, 200, 14, 4, 400, 16, 6, 600, 18, 8, 800)); - edge01.setFlags(normalFlags); - edge12.setFlags(normalFlags); - edge23.setFlags(normalFlags); - edge34.setFlags(normalFlags); + edge01.set(roadEnvEnc, normalRoadEnvironment); + edge12.set(roadEnvEnc, normalRoadEnvironment); + edge23.set(roadEnvEnc, normalRoadEnvironment); + edge34.set(roadEnvEnc, normalRoadEnvironment); - edge56.setFlags(normalFlags); - edge67.setFlags(interpolatableFlags); - edge78.setFlags(interpolatableFlags); - edge89.setFlags(normalFlags); + edge56.set(roadEnvEnc, normalRoadEnvironment); + edge67.set(roadEnvEnc, interpolatableRoadEnvironment); + edge78.set(roadEnvEnc, interpolatableRoadEnvironment); + edge89.set(roadEnvEnc, normalRoadEnvironment); - edge17.setFlags(interpolatableFlags); - edge27.setFlags(interpolatableFlags); - edge37.setFlags(interpolatableFlags); + edge17.set(roadEnvEnc, interpolatableRoadEnvironment); + edge27.set(roadEnvEnc, interpolatableRoadEnvironment); + edge37.set(roadEnvEnc, interpolatableRoadEnvironment); final GHIntHashSet outerNodeIds = new GHIntHashSet(); final GHIntHashSet innerNodeIds = new GHIntHashSet(); diff --git a/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java b/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java index 487ae1a1c41..fc7302f40d4 100644 --- a/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java +++ b/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java @@ -19,15 +19,10 @@ import com.graphhopper.coll.GHBitSetImpl; import com.graphhopper.coll.GHIntHashSet; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadEnvironment; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.IntsRef; import com.graphhopper.util.EdgeIteratorState; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; /** @@ -36,34 +31,29 @@ public abstract class EdgeElevationInterpolatorTest { protected static final double PRECISION = ElevationInterpolator.EPSILON2; - protected IntsRef interpolatableFlags; - protected IntsRef normalFlags; + protected RoadEnvironment interpolatableRoadEnvironment; + protected RoadEnvironment normalRoadEnvironment; protected BaseGraph graph; protected EnumEncodedValue roadEnvEnc; - protected FlagEncoder encoder; + protected BooleanEncodedValue accessEnc; + protected DecimalEncodedValue speedEnc; protected EncodingManager encodingManager; protected EdgeElevationInterpolator edgeElevationInterpolator; - @SuppressWarnings("resource") @BeforeEach public void setUp() { - encoder = FlagEncoders.createCar(); - encodingManager = EncodingManager.create(encoder); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); graph = new BaseGraph.Builder(encodingManager).set3D(true).create(); roadEnvEnc = encodingManager.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class); edgeElevationInterpolator = createEdgeElevationInterpolator(); - interpolatableFlags = createInterpolatableFlags(); - normalFlags = new IntsRef(1); - roadEnvEnc.setEnum(false, normalFlags, RoadEnvironment.ROAD); + interpolatableRoadEnvironment = getInterpolatableRoadEnvironment(); + normalRoadEnvironment = RoadEnvironment.ROAD; } - @AfterEach - public void tearDown() { - graph.close(); - } - - protected abstract IntsRef createInterpolatableFlags(); + protected abstract RoadEnvironment getInterpolatableRoadEnvironment(); protected EdgeElevationInterpolator createEdgeElevationInterpolator() { return new EdgeElevationInterpolator(graph, roadEnvEnc, RoadEnvironment.BRIDGE); diff --git a/core/src/test/java/com/graphhopper/reader/dem/GraphElevationSmoothingTest.java b/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationSmoothingTest.java similarity index 50% rename from core/src/test/java/com/graphhopper/reader/dem/GraphElevationSmoothingTest.java rename to core/src/test/java/com/graphhopper/reader/dem/EdgeElevationSmoothingTest.java index d2a75f5018c..69d8a03379c 100644 --- a/core/src/test/java/com/graphhopper/reader/dem/GraphElevationSmoothingTest.java +++ b/core/src/test/java/com/graphhopper/reader/dem/EdgeElevationSmoothingTest.java @@ -25,7 +25,7 @@ /** * @author Robin Boldt */ -public class GraphElevationSmoothingTest { +public class EdgeElevationSmoothingTest { @Test public void interpolatesElevationOfPillarNodes() { @@ -34,7 +34,7 @@ public void interpolatesElevationOfPillarNodes() { pl1.add(0, 0, 0); pl1.add(0.0005, 0.0005, 100); pl1.add(0.001, 0.001, 50); - GraphElevationSmoothing.smoothElevation(pl1); + EdgeElevationSmoothing.smoothMovingAverage(pl1); assertEquals(3, pl1.size()); assertEquals(50, pl1.getEle(1), .1); @@ -44,7 +44,7 @@ public void interpolatesElevationOfPillarNodes() { pl2.add(0.0016, 0.0015, 150); pl2.add(0.0017, 0.0015, 220); pl2.add(0.002, 0.002, 20); - GraphElevationSmoothing.smoothElevation(pl2); + EdgeElevationSmoothing.smoothMovingAverage(pl2); assertEquals(5, pl2.size()); assertEquals(120, pl2.getEle(1), .1); // This is not 120 anymore, as the point at index 1 was smoothed from 160=>120 @@ -53,4 +53,45 @@ public void interpolatesElevationOfPillarNodes() { assertEquals(50, pl2.getEle(0), .1); } + @Test + public void smoothRamer() { + PointList pl1 = new PointList(3, true); + pl1.add(0, 0, 0); + pl1.add(0.0005, 0.0005, 100); + pl1.add(0.001, 0.001, 50); + EdgeElevationSmoothing.smoothRamer(pl1, 70); + assertEquals(3, pl1.size()); + assertEquals(100, pl1.getEle(1), .1); + EdgeElevationSmoothing.smoothRamer(pl1, 75); + assertEquals(3, pl1.size()); + assertEquals(25, pl1.getEle(1), .1); + } + + @Test + public void smoothRamer2() { + PointList pl2 = new PointList(3, true); + pl2.add(0.001, 0.001, 50); + pl2.add(0.0015, 0.0015, 160); + pl2.add(0.0016, 0.0015, 150); + pl2.add(0.0017, 0.0015, 220); + pl2.add(0.002, 0.002, 20); + EdgeElevationSmoothing.smoothRamer(pl2, 100); + assertEquals(5, pl2.size()); + assertEquals(190, pl2.getEle(1), 1); // modify as too small in interval [0,4] + assertEquals(210, pl2.getEle(2), 1); // modify as too small in interval [0,4] + assertEquals(220, pl2.getEle(3), .1); // keep as it is bigger than maxElevationDelta in interval [0,4] + } + + @Test + public void smoothRamerNoMaximumFound() { + PointList pl2 = new PointList(3, true); + pl2.add(60.03307, 20.82262, 5.35); + pl2.add(60.03309, 20.82269, 5.42); + pl2.add(60.03307, 20.82262, 5.35); + EdgeElevationSmoothing.smoothRamer(pl2, 10); + assertEquals(3, pl2.size()); + assertEquals(5.35, pl2.getEle(0), 0.01); + assertEquals(5.35, pl2.getEle(1), 0.01); + assertEquals(5.35, pl2.getEle(2), 0.01); + } } diff --git a/core/src/test/java/com/graphhopper/reader/dem/HGTProviderTest.java b/core/src/test/java/com/graphhopper/reader/dem/HGTProviderTest.java new file mode 100644 index 00000000000..47a6dce57f2 --- /dev/null +++ b/core/src/test/java/com/graphhopper/reader/dem/HGTProviderTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.dem; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HGTProviderTest { + + @Disabled + @Test + void getEle() { + HGTProvider hgt = new HGTProvider("/your/path/to/hgt/"); + assertEquals(511, hgt.getEle(49.1, 11.7), 1); + assertEquals(0, hgt.getEle(0.6, 0.6), 1); + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/reader/dem/TunnelElevationInterpolatorTest.java b/core/src/test/java/com/graphhopper/reader/dem/TunnelElevationInterpolatorTest.java index 6f78ba86f81..aa286f46fea 100644 --- a/core/src/test/java/com/graphhopper/reader/dem/TunnelElevationInterpolatorTest.java +++ b/core/src/test/java/com/graphhopper/reader/dem/TunnelElevationInterpolatorTest.java @@ -19,7 +19,6 @@ import com.graphhopper.coll.GHIntHashSet; import com.graphhopper.routing.ev.RoadEnvironment; -import com.graphhopper.storage.IntsRef; import com.graphhopper.storage.NodeAccess; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; @@ -33,10 +32,8 @@ public class TunnelElevationInterpolatorTest extends EdgeElevationInterpolatorTest { @Override - protected IntsRef createInterpolatableFlags() { - IntsRef edgeFlags = new IntsRef(1); - roadEnvEnc.setEnum(false, edgeFlags, RoadEnvironment.TUNNEL); - return edgeFlags; + protected RoadEnvironment getInterpolatableRoadEnvironment() { + return RoadEnvironment.TUNNEL; } @Override @@ -61,13 +58,13 @@ public void doesNotInterpolateElevationOfTunnelWithZeroOuterNodes() { na.setNode(3, 30, 0, 20); na.setNode(4, 40, 0, 0); - EdgeIteratorState edge01 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); - EdgeIteratorState edge12 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); - EdgeIteratorState edge34 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10)); + EdgeIteratorState edge01 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + EdgeIteratorState edge12 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + EdgeIteratorState edge34 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); - edge01.setFlags(interpolatableFlags); - edge12.setFlags(interpolatableFlags); - edge34.setFlags(interpolatableFlags); + edge01.set(roadEnvEnc, interpolatableRoadEnvironment); + edge12.set(roadEnvEnc, interpolatableRoadEnvironment); + edge34.set(roadEnvEnc, interpolatableRoadEnvironment); final GHIntHashSet outerNodeIds = new GHIntHashSet(); final GHIntHashSet innerNodeIds = new GHIntHashSet(); @@ -100,15 +97,15 @@ public void interpolatesElevationOfTunnelWithSingleOuterNode() { na.setNode(3, 30, 0, 20); na.setNode(4, 40, 0, 00); - EdgeIteratorState edge01 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); - EdgeIteratorState edge12 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); - EdgeIteratorState edge23 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(10)); - EdgeIteratorState edge34 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10)); + EdgeIteratorState edge01 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + EdgeIteratorState edge12 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + EdgeIteratorState edge23 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10)); + EdgeIteratorState edge34 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); - edge01.setFlags(interpolatableFlags); - edge12.setFlags(interpolatableFlags); - edge23.setFlags(normalFlags); - edge34.setFlags(interpolatableFlags); + edge01.set(roadEnvEnc, interpolatableRoadEnvironment); + edge12.set(roadEnvEnc, interpolatableRoadEnvironment); + edge23.set(roadEnvEnc, normalRoadEnvironment); + edge34.set(roadEnvEnc, interpolatableRoadEnvironment); final GHIntHashSet outerNodeIds = new GHIntHashSet(); final GHIntHashSet innerNodeIds = new GHIntHashSet(); @@ -141,15 +138,15 @@ public void interpolatesElevationOfTunnelWithTwoOuterNodes() { na.setNode(3, 30, 0, 30); na.setNode(4, 40, 0, 40); - EdgeIteratorState edge01 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); - EdgeIteratorState edge12 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); - EdgeIteratorState edge23 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(10)); - EdgeIteratorState edge34 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10)); + EdgeIteratorState edge01 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + EdgeIteratorState edge12 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + EdgeIteratorState edge23 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10)); + EdgeIteratorState edge34 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); - edge01.setFlags(normalFlags); - edge12.setFlags(interpolatableFlags); - edge23.setFlags(interpolatableFlags); - edge34.setFlags(normalFlags); + edge01.set(roadEnvEnc, normalRoadEnvironment); + edge12.set(roadEnvEnc, interpolatableRoadEnvironment); + edge23.set(roadEnvEnc, interpolatableRoadEnvironment); + edge34.set(roadEnvEnc, normalRoadEnvironment); final GHIntHashSet outerNodeIds = new GHIntHashSet(); final GHIntHashSet innerNodeIds = new GHIntHashSet(); @@ -191,21 +188,21 @@ public void interpolatesElevationOfTunnelWithThreeOuterNodes() { na.setNode(6, 30, 10, 30); na.setNode(7, 40, 10, 40); - EdgeIteratorState edge01 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); - EdgeIteratorState edge12 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); - EdgeIteratorState edge23 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(10)); - EdgeIteratorState edge34 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10)); - EdgeIteratorState edge25 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 5).setDistance(10)); - EdgeIteratorState edge56 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(10)); - EdgeIteratorState edge67 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(10)); - - edge01.setFlags(normalFlags); - edge12.setFlags(interpolatableFlags); - edge23.setFlags(interpolatableFlags); - edge34.setFlags(normalFlags); - edge25.setFlags(interpolatableFlags); - edge56.setFlags(interpolatableFlags); - edge67.setFlags(normalFlags); + EdgeIteratorState edge01 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + EdgeIteratorState edge12 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + EdgeIteratorState edge23 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10)); + EdgeIteratorState edge34 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); + EdgeIteratorState edge25 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 5).setDistance(10)); + EdgeIteratorState edge56 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(10)); + EdgeIteratorState edge67 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(10)); + + edge01.set(roadEnvEnc, normalRoadEnvironment); + edge12.set(roadEnvEnc, interpolatableRoadEnvironment); + edge23.set(roadEnvEnc, interpolatableRoadEnvironment); + edge34.set(roadEnvEnc, normalRoadEnvironment); + edge25.set(roadEnvEnc, interpolatableRoadEnvironment); + edge56.set(roadEnvEnc, interpolatableRoadEnvironment); + edge67.set(roadEnvEnc, normalRoadEnvironment); final GHIntHashSet outerNodeIds = new GHIntHashSet(); final GHIntHashSet innerNodeIds = new GHIntHashSet(); @@ -253,7 +250,7 @@ public void interpolatesElevationOfTunnelWithFourOuterNodes() { na.setNode(9, 40, 10, 0); EdgeIteratorState edge01, edge12, edge23, edge34, edge56, edge67, edge78, edge89, edge27; - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, edge01 = graph.edge(0, 1).setDistance(10), edge12 = graph.edge(1, 2).setDistance(10), edge23 = graph.edge(2, 3).setDistance(10), @@ -264,17 +261,17 @@ public void interpolatesElevationOfTunnelWithFourOuterNodes() { edge89 = graph.edge(8, 9).setDistance(10), edge27 = graph.edge(2, 7).setDistance(10)); - edge01.setFlags(normalFlags); - edge12.setFlags(interpolatableFlags); - edge23.setFlags(interpolatableFlags); - edge34.setFlags(normalFlags); + edge01.set(roadEnvEnc, normalRoadEnvironment); + edge12.set(roadEnvEnc, interpolatableRoadEnvironment); + edge23.set(roadEnvEnc, interpolatableRoadEnvironment); + edge34.set(roadEnvEnc, normalRoadEnvironment); - edge56.setFlags(normalFlags); - edge67.setFlags(interpolatableFlags); - edge78.setFlags(interpolatableFlags); - edge89.setFlags(normalFlags); + edge56.set(roadEnvEnc, normalRoadEnvironment); + edge67.set(roadEnvEnc, interpolatableRoadEnvironment); + edge78.set(roadEnvEnc, interpolatableRoadEnvironment); + edge89.set(roadEnvEnc, normalRoadEnvironment); - edge27.setFlags(interpolatableFlags); + edge27.set(roadEnvEnc, interpolatableRoadEnvironment); final GHIntHashSet outerNodeIds = new GHIntHashSet(); final GHIntHashSet innerNodeIds = new GHIntHashSet(); diff --git a/core/src/test/java/com/graphhopper/reader/osm/ExtractMembersTest.java b/core/src/test/java/com/graphhopper/reader/osm/ExtractMembersTest.java new file mode 100644 index 00000000000..2a9c1c3155c --- /dev/null +++ b/core/src/test/java/com/graphhopper/reader/osm/ExtractMembersTest.java @@ -0,0 +1,116 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.carrotsearch.hppc.LongArrayList; +import com.graphhopper.reader.ReaderRelation; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static com.graphhopper.reader.ReaderElement.Type.NODE; +import static com.graphhopper.reader.ReaderElement.Type.WAY; +import static org.junit.jupiter.api.Assertions.*; + +class ExtractMembersTest { + private ReaderRelation relation; + + @BeforeEach + void setup() { + relation = new ReaderRelation(0); + relation.setTag("type", "restriction"); + } + + @Test + void simpleViaNode() throws OSMRestrictionException { + relation.add(new ReaderRelation.Member(WAY, 1, "from")); + relation.add(new ReaderRelation.Member(NODE, 2, "via")); + relation.add(new ReaderRelation.Member(WAY, 3, "to")); + RestrictionMembers restrictionMembers = RestrictionConverter.extractMembers(relation); + assertEquals(LongArrayList.from(1), restrictionMembers.getFromWays()); + assertEquals(2, restrictionMembers.getViaOSMNode()); + assertEquals(LongArrayList.from(3), restrictionMembers.getToWays()); + assertNull(restrictionMembers.getViaWays()); + } + + @Test + void noVia() { + relation.add(new ReaderRelation.Member(WAY, 1, "from")); + relation.add(new ReaderRelation.Member(WAY, 2, "to")); + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> RestrictionConverter.extractMembers(relation)); + assertTrue(e.getMessage().contains("has no member with role 'via'"), e.getMessage()); + } + + @Test + void multipleViaNodes() { + relation.add(new ReaderRelation.Member(WAY, 1, "from")); + relation.add(new ReaderRelation.Member(NODE, 2, "via")); + relation.add(new ReaderRelation.Member(NODE, 3, "via")); + relation.add(new ReaderRelation.Member(WAY, 4, "to")); + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> RestrictionConverter.extractMembers(relation)); + assertTrue(e.getMessage().contains("has multiple members with role 'via' and type 'node'"), e.getMessage()); + } + + @Test + void multipleFromButNotNoEntry() { + relation.add(new ReaderRelation.Member(WAY, 1, "from")); + relation.add(new ReaderRelation.Member(WAY, 2, "from")); + relation.add(new ReaderRelation.Member(NODE, 3, "via")); + relation.add(new ReaderRelation.Member(WAY, 4, "to")); + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> RestrictionConverter.extractMembers(relation)); + assertTrue(e.getMessage().contains("has multiple members with role 'from' even though it is not a 'no_entry' restriction"), e.getMessage()); + } + + @Test + void noEntry() throws OSMRestrictionException { + relation.setTag("restriction", "no_entry"); + relation.add(new ReaderRelation.Member(WAY, 1, "from")); + relation.add(new ReaderRelation.Member(WAY, 2, "from")); + relation.add(new ReaderRelation.Member(NODE, 3, "via")); + relation.add(new ReaderRelation.Member(WAY, 4, "to")); + RestrictionMembers res = RestrictionConverter.extractMembers(relation); + assertEquals(LongArrayList.from(1, 2), res.getFromWays()); + assertEquals(3, res.getViaOSMNode()); + assertEquals(LongArrayList.from(4), res.getToWays()); + } + + @Test + void multipleToButNoNoExit() { + relation.setTag("restriction", "no_left_turn"); + relation.add(new ReaderRelation.Member(WAY, 1, "from")); + relation.add(new ReaderRelation.Member(NODE, 2, "via")); + relation.add(new ReaderRelation.Member(WAY, 3, "to")); + relation.add(new ReaderRelation.Member(WAY, 4, "to")); + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> RestrictionConverter.extractMembers(relation)); + assertTrue(e.getMessage().contains("has multiple members with role 'to' even though it is not a 'no_exit' restriction"), e.getMessage()); + } + + @Test + void noExit() throws OSMRestrictionException { + relation.setTag("restriction", "no_exit"); + relation.add(new ReaderRelation.Member(WAY, 1, "from")); + relation.add(new ReaderRelation.Member(NODE, 2, "via")); + relation.add(new ReaderRelation.Member(WAY, 3, "to")); + relation.add(new ReaderRelation.Member(WAY, 4, "to")); + RestrictionMembers res = RestrictionConverter.extractMembers(relation); + assertEquals(LongArrayList.from(1), res.getFromWays()); + assertEquals(2, res.getViaOSMNode()); + assertEquals(LongArrayList.from(3, 4), res.getToWays()); + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java b/core/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java index cdbad782967..92fad8a9502 100644 --- a/core/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java +++ b/core/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java @@ -23,6 +23,7 @@ import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; +import com.graphhopper.routing.ev.EncodedValue; import com.graphhopper.routing.lm.LandmarkStorage; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.weighting.custom.CustomProfile; @@ -40,6 +41,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI; import static org.junit.jupiter.api.Assertions.*; @@ -164,13 +166,13 @@ public void testQueryLocationIndexWithBBox() { setOSMFile("../core/files/monaco.osm.gz"); gh.importOrLoad(); - final NodeAccess na = gh.getGraphHopperStorage().getNodeAccess(); + final NodeAccess na = gh.getBaseGraph().getNodeAccess(); final Collection indexNodeList = new TreeSet<>(); LocationIndexTree index = (LocationIndexTree) gh.getLocationIndex(); - final EdgeExplorer edgeExplorer = gh.getGraphHopperStorage().createEdgeExplorer(); + final EdgeExplorer edgeExplorer = gh.getBaseGraph().createEdgeExplorer(); final BBox bbox = new BBox(7.422, 7.429, 43.729, 43.734); index.query(bbox, edgeId -> { - EdgeIteratorState edge = gh.getGraphHopperStorage().getEdgeIteratorStateForKey(edgeId * 2); + EdgeIteratorState edge = gh.getBaseGraph().getEdgeIteratorStateForKey(edgeId * 2); for (int i = 0; i < 2; i++) { int nodeId = i == 0 ? edge.getBaseNode() : edge.getAdjNode(); double lat = na.getLat(nodeId); @@ -180,7 +182,7 @@ public void testQueryLocationIndexWithBBox() { } }); - assertEquals(57, indexNodeList.size()); + assertEquals(152, indexNodeList.size()); for (int nodeId : indexNodeList) { if (!bbox.contains(na.getLat(nodeId), na.getLon(nodeId))) fail("bbox " + bbox + " should contain " + nodeId); @@ -190,7 +192,7 @@ public void testQueryLocationIndexWithBBox() { new BreadthFirstSearch() { @Override protected GHBitSet createBitSet() { - return new GHBitSetImpl(gh.getGraphHopperStorage().getNodes()); + return new GHBitSetImpl(gh.getBaseGraph().getNodes()); } @Override @@ -417,8 +419,8 @@ public void testFootAndCar() { // Now it doesn't work like that anymore, so I set this parameter so the test doesn't fail. ((LocationIndexTree) instance.getLocationIndex()).setMaxRegionSearch(300); - assertEquals(5, instance.getGraphHopperStorage().getNodes()); - assertEquals(8, instance.getGraphHopperStorage().getEdges()); + assertEquals(5, instance.getBaseGraph().getNodes()); + assertEquals(8, instance.getBaseGraph().getEdges()); // A to D GHResponse grsp = instance.route(new GHRequest(11.1, 50, 11.3, 51).setProfile(profile1)); @@ -454,76 +456,70 @@ public void testFootAndCar() { } @Test - public void testFailsForWrongConfig() { + public void testNothingHappensWhenFlagEncodersAreChangedForLoad() { instance = new GraphHopper().init( new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("datareader.dataaccess", "RAM"). - putObject("graph.flag_encoders", "foot,car"). + putObject("graph.vehicles", "foot,car"). + putObject("import.osm.ignored_highways", ""). setProfiles(Arrays.asList( new Profile("foot").setVehicle("foot").setWeighting("fastest"), new Profile("car").setVehicle("car").setWeighting("fastest") ))). setGraphHopperLocation(ghLoc); instance.importOrLoad(); - assertEquals(5, instance.getGraphHopperStorage().getNodes()); + assertEquals(5, instance.getBaseGraph().getNodes()); instance.close(); - // different config (flagEncoder list) - try { - GraphHopper tmpGH = new GraphHopper().init( - new GraphHopperConfig(). - putObject("datareader.file", testOsm3). - putObject("datareader.dataaccess", "RAM"). - putObject("graph.flag_encoders", "foot"). - setProfiles(Collections.singletonList( - new Profile("foot").setVehicle("foot").setWeighting("fastest") - ))). - setOSMFile(testOsm3). - setGraphHopperLocation(ghLoc); - tmpGH.load(); - fail(); - } catch (Exception ex) { - assertTrue(ex.getMessage().startsWith("Encoding does not match"), ex.getMessage()); - } - - // different order is no longer okay, see #350 - try { - GraphHopper tmpGH = new GraphHopper().init(new GraphHopperConfig(). - putObject("datareader.file", testOsm3). - putObject("datareader.dataaccess", "RAM"). - putObject("graph.flag_encoders", "car,foot"). - setProfiles(Arrays.asList( - new Profile("car").setVehicle("car").setWeighting("fastest"), - new Profile("foot").setVehicle("foot").setWeighting("fastest") - ))). - setOSMFile(testOsm3) - .setGraphHopperLocation(ghLoc); - tmpGH.load(); - fail(); - } catch (Exception ex) { - assertTrue(ex.getMessage().startsWith("Encoding does not match"), ex.getMessage()); - } + // different flagEncoder list has no effect when loading, so it does not matter, but the profiles must be the same + GraphHopper tmpGH = new GraphHopper().init( + new GraphHopperConfig(). + putObject("datareader.file", testOsm3). + putObject("datareader.dataaccess", "RAM"). + putObject("graph.vehicles", "foot"). + putObject("import.osm.ignored_highways", ""). + setProfiles(Collections.singletonList( + new Profile("foot").setVehicle("foot").setWeighting("fastest") + ))). + setOSMFile(testOsm3). + setGraphHopperLocation(ghLoc); + IllegalStateException e = assertThrows(IllegalStateException.class, tmpGH::load); + assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); - // different encoded values should fail to load + // different order of graph.vehicles is also fine, but profiles must be in same order + tmpGH = new GraphHopper().init(new GraphHopperConfig(). + putObject("datareader.file", testOsm3). + putObject("datareader.dataaccess", "RAM"). + putObject("graph.vehicles", "car,foot"). + putObject("import.osm.ignored_highways", ""). + setProfiles(Arrays.asList( + new Profile("car").setVehicle("car").setWeighting("fastest"), + new Profile("foot").setVehicle("foot").setWeighting("fastest") + ))). + setOSMFile(testOsm3) + .setGraphHopperLocation(ghLoc); + e = assertThrows(IllegalStateException.class, tmpGH::load); + assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); + + // different encoded values do not matter either instance = new GraphHopper().init( new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("datareader.dataaccess", "RAM"). putObject("graph.encoded_values", "road_class"). - putObject("graph.flag_encoders", "foot,car"). + putObject("graph.vehicles", "foot,car"). + putObject("import.osm.ignored_highways", ""). setProfiles(Arrays.asList( new Profile("foot").setVehicle("foot").setWeighting("fastest"), new Profile("car").setVehicle("car").setWeighting("fastest") ))). setOSMFile(testOsm3). setGraphHopperLocation(ghLoc); - try { - instance.load(); - fail(); - } catch (Exception ex) { - assertTrue(ex.getMessage().startsWith("Encoded values do not match"), ex.getMessage()); - } + instance.load(); + assertEquals(5, instance.getBaseGraph().getNodes()); + assertEquals("foot_access,foot_average_speed,foot_priority,car_access,car_average_speed,foot_subnetwork,car_subnetwork,roundabout,road_class,road_class_link,road_environment,max_speed,road_access,foot_network", + instance.getEncodingManager().getEncodedValues().stream().map(EncodedValue::getName).collect(Collectors.joining(","))); } @Test @@ -532,7 +528,8 @@ public void testFailsForWrongEVConfig() { new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("datareader.dataaccess", "RAM"). - putObject("graph.flag_encoders", "foot,car"). + putObject("graph.vehicles", "foot,car"). + putObject("import.osm.ignored_highways", ""). setProfiles(Arrays.asList( new Profile("foot").setVehicle("foot").setWeighting("fastest"), new Profile("car").setVehicle("car").setWeighting("fastest") @@ -540,26 +537,28 @@ public void testFailsForWrongEVConfig() { setGraphHopperLocation(ghLoc); instance.importOrLoad(); // older versions <= 0.12 did not store this property, ensure that we fail to load it - instance.getGraphHopperStorage().getProperties().remove("graph.encoded_values"); - instance.getGraphHopperStorage().flush(); - assertEquals(5, instance.getGraphHopperStorage().getNodes()); + instance.getProperties().remove("graph.encoded_values"); + instance.getBaseGraph().flush(); + assertEquals(5, instance.getBaseGraph().getNodes()); instance.close(); - // different encoded values should fail to load + // different encoded values are ignored anyway instance = new GraphHopper().init( new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("datareader.dataaccess", "RAM"). putObject("graph.location", ghLoc). putObject("graph.encoded_values", "road_environment,road_class"). - putObject("graph.flag_encoders", "foot,car"). + putObject("graph.vehicles", "foot,car"). + putObject("import.osm.ignored_highways", ""). setProfiles(Arrays.asList( new Profile("foot").setVehicle("foot").setWeighting("fastest"), new Profile("car").setVehicle("car").setWeighting("fastest") ))). setOSMFile(testOsm3); - Exception ex = assertThrows(Exception.class, () -> instance.load()); - assertTrue(ex.getMessage().startsWith("Encoded values do not match"), ex.getMessage()); + instance.load(); + assertEquals(5, instance.getBaseGraph().getNodes()); + assertEquals("foot_access,foot_average_speed,foot_priority,car_access,car_average_speed,foot_subnetwork,car_subnetwork,roundabout,road_class,road_class_link,road_environment,max_speed,road_access,foot_network", instance.getEncodingManager().getEncodedValues().stream().map(EncodedValue::getName).collect(Collectors.joining(","))); } @Test @@ -642,13 +641,15 @@ public void testFootOnly() { setProfiles(new Profile(profile).setVehicle(vehicle).setWeighting(weighting)). setGraphHopperLocation(ghLoc). setOSMFile(testOsm3); + // exclude motorways which aren't accessible for foot + instance.getReaderConfig().setIgnoredHighways(Arrays.asList("motorway")); instance.getCHPreparationHandler().setCHProfiles(new CHProfile(profile)); instance.importOrLoad(); ((LocationIndexTree) instance.getLocationIndex()).setMaxRegionSearch(300); - assertEquals(2, instance.getGraphHopperStorage().getNodes()); - assertEquals(2, instance.getGraphHopperStorage().getAllEdges().length()); + assertEquals(2, instance.getBaseGraph().getNodes()); + assertEquals(2, instance.getBaseGraph().getAllEdges().length()); // A to E only for foot GHResponse grsp = instance.route(new GHRequest(11.1, 50, 11.19, 52).setProfile(profile)); @@ -667,7 +668,8 @@ public void testVia() { init(new GraphHopperConfig(). putObject("datareader.file", testOsm3). putObject("prepare.min_network_size", 0). - putObject("graph.flag_encoders", vehicle). + putObject("graph.vehicles", vehicle). + putObject("import.osm.ignored_highways", ""). setProfiles(Collections.singletonList(new Profile(profile).setVehicle(vehicle).setWeighting(weighting))). setCHProfiles(Collections.singletonList(new CHProfile(profile))) ). @@ -732,11 +734,11 @@ public void testMultipleCHPreparationsInParallel() { assertEquals((long) shortcutCount, chGraph.getValue().getShortcuts()); String keyError = Parameters.CH.PREPARE + "error." + name; - String valueError = hopper.getGraphHopperStorage().getProperties().get(keyError); + String valueError = hopper.getProperties().get(keyError); assertTrue(valueError.isEmpty(), "Properties for " + name + " should NOT contain error " + valueError + " [" + threadCount + "]"); String key = Parameters.CH.PREPARE + "date." + name; - String value = hopper.getGraphHopperStorage().getProperties().get(key); + String value = hopper.getProperties().get(key); assertFalse(value.isEmpty(), "Properties for " + name + " did NOT contain finish date [" + threadCount + "]"); } hopper.close(); @@ -781,11 +783,11 @@ public void testMultipleLMPreparationsInParallel() { assertEquals((int) landmarksCount, landmarks.getValue().getSubnetworksWithLandmarks()); String keyError = Parameters.Landmark.PREPARE + "error." + name; - String valueError = hopper.getGraphHopperStorage().getProperties().get(keyError); + String valueError = hopper.getProperties().get(keyError); assertTrue(valueError.isEmpty(), "Properties for " + name + " should NOT contain error " + valueError + " [" + threadCount + "]"); String key = Parameters.Landmark.PREPARE + "date." + name; - String value = hopper.getGraphHopperStorage().getProperties().get(key); + String value = hopper.getProperties().get(key); assertFalse(value.isEmpty(), "Properties for " + name + " did NOT contain finish date [" + threadCount + "]"); } hopper.close(); @@ -814,7 +816,7 @@ public void testProfilesMustNotBeChanged() { { GraphHopper hopper = createHopperWithProfiles(Arrays.asList( new Profile("car").setVehicle("car").setWeighting("fastest"), - new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3)).setVehicle("car") + new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3d)).setVehicle("car") )); hopper.importOrLoad(); hopper.close(); @@ -823,16 +825,26 @@ public void testProfilesMustNotBeChanged() { // load without problem GraphHopper hopper = createHopperWithProfiles(Arrays.asList( new Profile("car").setVehicle("car").setWeighting("fastest"), - new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3)).setVehicle("car") + new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3d)).setVehicle("car") )); hopper.importOrLoad(); hopper.close(); } + { + // problem: the vehicle was changed. this is not allowed. + GraphHopper hopper = createHopperWithProfiles(Arrays.asList( + new Profile("car").setVehicle("bike").setWeighting("fastest"), + new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3d)).setVehicle("car") + )); + IllegalStateException e = assertThrows(IllegalStateException.class, hopper::importOrLoad); + assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); + hopper.close(); + } { // problem: the profile changed (slightly). we do not allow this because we would potentially need to re-calculate the subnetworks GraphHopper hopper = createHopperWithProfiles(Arrays.asList( new Profile("car").setVehicle("car").setWeighting("fastest"), - new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(80)).setVehicle("car") + new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(80d)).setVehicle("car") )); IllegalStateException e = assertThrows(IllegalStateException.class, hopper::importOrLoad); assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); @@ -841,14 +853,12 @@ public void testProfilesMustNotBeChanged() { { // problem: we add another profile, which is not allowed, because there would be no subnetwork ev for it GraphHopper hopper = createHopperWithProfiles(Arrays.asList( - new Profile("car").setVehicle("car").setWeighting("shortest"), - new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3)).setVehicle("car"), + new Profile("car").setVehicle("car").setWeighting("fastest"), + new CustomProfile("custom").setCustomModel(new CustomModel().setDistanceInfluence(3d)).setVehicle("car"), new Profile("car2").setVehicle("car").setWeighting("fastest") )); IllegalStateException e = assertThrows(IllegalStateException.class, hopper::importOrLoad); - // so far we get another error message in this case, because GraphHopperStorage checks the encoded values - // in loadExisting already - assertTrue(e.getMessage().contains("Encoded values do not match"), e.getMessage()); + assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); hopper.close(); } { @@ -858,7 +868,7 @@ public void testProfilesMustNotBeChanged() { new Profile("car").setVehicle("car").setWeighting("shortest") )); IllegalStateException e = assertThrows(IllegalStateException.class, hopper::importOrLoad); - assertTrue(e.getMessage().contains("Encoded values do not match"), e.getMessage()); + assertTrue(e.getMessage().contains("Profiles do not match"), e.getMessage()); hopper.close(); } } @@ -868,6 +878,7 @@ private GraphHopper createHopperWithProfiles(List profiles) { hopper.init(new GraphHopperConfig() .putObject("graph.location", ghLoc) .putObject("datareader.file", testOsm) + .putObject("import.osm.ignored_highways", "") .setProfiles(profiles) ); return hopper; diff --git a/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java b/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java index 97dbac9e177..8b979c543af 100644 --- a/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java +++ b/core/src/test/java/com/graphhopper/reader/osm/OSMReaderTest.java @@ -22,14 +22,17 @@ import com.graphhopper.GraphHopper; import com.graphhopper.GraphHopperTest; import com.graphhopper.config.Profile; +import com.graphhopper.reader.ReaderElement; import com.graphhopper.reader.ReaderRelation; +import com.graphhopper.reader.ReaderWay; import com.graphhopper.reader.dem.ElevationProvider; import com.graphhopper.reader.dem.SRTMProvider; +import com.graphhopper.reader.osm.conditional.DateRangeParser; import com.graphhopper.routing.OSMReaderConfig; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.*; import com.graphhopper.routing.util.countryrules.CountryRuleFactory; -import com.graphhopper.routing.util.parsers.CountryParser; +import com.graphhopper.routing.util.parsers.*; import com.graphhopper.storage.*; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; @@ -41,7 +44,9 @@ import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import static com.graphhopper.util.GHUtility.readCountries; @@ -57,12 +62,11 @@ public class OSMReaderTest { private final String file2 = "test-osm2.xml"; private final String file3 = "test-osm3.xml"; private final String file4 = "test-osm4.xml"; - private final String file7 = "test-osm7.xml"; private final String fileBarriers = "test-barriers.xml"; private final String dir = "./target/tmp/test-db"; - private FlagEncoder carEncoder; private BooleanEncodedValue carAccessEnc; - private FlagEncoder footEncoder; + private DecimalEncodedValue carSpeedEnc; + private BooleanEncodedValue footAccessEnc; private EdgeExplorer carOutExplorer; private EdgeExplorer carAllExplorer; @@ -79,12 +83,13 @@ public void tearDown() { @Test public void testMain() { GraphHopper hopper = new GraphHopperFacade(file1).importOrLoad(); - GraphHopperStorage graph = hopper.getGraphHopperStorage(); + BaseGraph graph = hopper.getBaseGraph(); + StorableProperties properties = hopper.getProperties(); - assertNotNull(graph.getProperties().get("datareader.import.date")); - assertNotEquals("", graph.getProperties().get("datareader.import.date")); + assertNotNull(properties.get("datareader.import.date")); + assertNotEquals("", properties.get("datareader.import.date")); - assertEquals("2013-01-02T01:10:14Z", graph.getProperties().get("datareader.data.date")); + assertEquals("2013-01-02T01:10:14Z", properties.get("datareader.data.date")); assertEquals(4, graph.getNodes()); int n20 = AbstractGraphStorageTester.getIdOf(graph, 52); @@ -140,7 +145,7 @@ protected int findID(LocationIndex index, double lat, double lon) { @Test public void testSort() { GraphHopper hopper = new GraphHopperFacade(file1).setSortGraph(true).importOrLoad(); - NodeAccess na = hopper.getGraphHopperStorage().getNodeAccess(); + NodeAccess na = hopper.getBaseGraph().getNodeAccess(); assertEquals(10, na.getLon(findID(hopper.getLocationIndex(), 49, 10)), 1e-3); assertEquals(51.249, na.getLat(findID(hopper.getLocationIndex(), 51.2492152, 9.4317166)), 1e-3); } @@ -150,9 +155,10 @@ public void testOneWay() { GraphHopper hopper = new GraphHopperFacade(file2) .setMinNetworkSize(0) .importOrLoad(); - GraphHopperStorage graph = hopper.getGraphHopperStorage(); + BaseGraph graph = hopper.getBaseGraph(); + StorableProperties properties = hopper.getProperties(); - assertEquals("2014-01-02T01:10:14Z", graph.getProperties().get("datareader.data.date")); + assertEquals("2014-01-02T01:10:14Z", properties.get("datareader.data.date")); int n20 = AbstractGraphStorageTester.getIdOf(graph, 52.0); int n22 = AbstractGraphStorageTester.getIdOf(graph, 52.133); @@ -202,7 +208,7 @@ public void testFerry() { public void cleanUp() { } }.importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); int n40 = AbstractGraphStorageTester.getIdOf(graph, 54.0); int n50 = AbstractGraphStorageTester.getIdOf(graph, 55.0); @@ -212,13 +218,13 @@ public void cleanUp() { int n80 = AbstractGraphStorageTester.getIdOf(graph, 54.1); EdgeIterator iter = carOutExplorer.setBaseNode(n80); iter.next(); - assertEquals(5, iter.get(carEncoder.getAverageSpeedEnc()), 1e-1); + assertEquals(5, iter.get(carSpeedEnc), 1e-1); // duration 01:10 is given => more precise speed calculation! // ~111km (from 54.0,10.1 to 55.0,10.2) in duration=70 minutes => 95km/h => / 1.4 => 71km/h iter = carOutExplorer.setBaseNode(n40); iter.next(); - assertEquals(70, iter.get(carEncoder.getAverageSpeedEnc()), 1e-1); + assertEquals(70, iter.get(carSpeedEnc), 1e-1); } @Test @@ -228,18 +234,18 @@ public void testMaxSpeed() { public void cleanUp() { } }.importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); int n60 = AbstractGraphStorageTester.getIdOf(graph, 56.0); EdgeIterator iter = carOutExplorer.setBaseNode(n60); iter.next(); - assertEquals(35, iter.get(carEncoder.getAverageSpeedEnc()), 1e-1); + assertEquals(35, iter.get(carSpeedEnc), 1e-1); } @Test public void testWayReferencesNotExistingAdjNode_issue19() { GraphHopper hopper = new GraphHopperFacade(file4).importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); assertEquals(2, graph.getNodes()); // the missing node is ignored, but the separated nodes are still connected @@ -253,7 +259,7 @@ public void testWayReferencesNotExistingAdjNode_issue19() { @Test public void testDoNotRejectEdgeIfFirstNodeIsMissing_issue2221() { GraphHopper hopper = new GraphHopperFacade("test-osm9.xml").importOrLoad(); - GraphHopperStorage graph = hopper.getGraphHopperStorage(); + BaseGraph graph = hopper.getBaseGraph(); assertEquals(2, graph.getNodes()); assertEquals(1, graph.getEdges()); AllEdgesIterator iter = graph.getAllEdges(); @@ -273,7 +279,7 @@ public void testDoNotRejectEdgeIfFirstNodeIsMissing_issue2221() { @Test public void test_edgeDistanceWhenFirstNodeIsMissing_issue2221() { GraphHopper hopper = new GraphHopperFacade("test-osm10.xml").importOrLoad(); - GraphHopperStorage graph = hopper.getGraphHopperStorage(); + BaseGraph graph = hopper.getBaseGraph(); assertEquals(3, graph.getNodes()); assertEquals(3, graph.getEdges()); AllEdgesIterator iter = graph.getAllEdges(); @@ -290,7 +296,7 @@ public void testFoot() { GraphHopper hopper = new GraphHopperFacade(file3) .setMinNetworkSize(0) .importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); int n10 = AbstractGraphStorageTester.getIdOf(graph, 11.1); int n20 = AbstractGraphStorageTester.getIdOf(graph, 12); @@ -303,7 +309,7 @@ public void testFoot() { assertEquals(GHUtility.asSet(n10, n30, n40), GHUtility.getNeighbors(carAllExplorer.setBaseNode(n20))); assertEquals(GHUtility.asSet(n30, n40), GHUtility.getNeighbors(carOutExplorer.setBaseNode(n20))); - EdgeExplorer footOutExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(footEncoder.getAccessEnc())); + EdgeExplorer footOutExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(footAccessEnc)); assertEquals(GHUtility.asSet(n20, n50), GHUtility.getNeighbors(footOutExplorer.setBaseNode(n10))); assertEquals(GHUtility.asSet(n20, n50), GHUtility.getNeighbors(footOutExplorer.setBaseNode(n30))); assertEquals(GHUtility.asSet(n10, n30), GHUtility.getNeighbors(footOutExplorer.setBaseNode(n20))); @@ -312,26 +318,10 @@ public void testFoot() { @Test public void testNegativeIds() { String fileNegIds = "test-osm-negative-ids.xml"; - GraphHopper hopper = new GraphHopperFacade(fileNegIds).importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); - assertEquals(4, graph.getNodes()); - int n20 = AbstractGraphStorageTester.getIdOf(graph, 52); - int n10 = AbstractGraphStorageTester.getIdOf(graph, 51.2492152); - int n30 = AbstractGraphStorageTester.getIdOf(graph, 51.2); - assertEquals(GHUtility.asSet(n20), GHUtility.getNeighbors(carOutExplorer.setBaseNode(n10))); - assertEquals(3, GHUtility.count(carOutExplorer.setBaseNode(n20))); - assertEquals(GHUtility.asSet(n20), GHUtility.getNeighbors(carOutExplorer.setBaseNode(n30))); - - EdgeIterator iter = carOutExplorer.setBaseNode(n20); - assertTrue(iter.next()); - - assertTrue(iter.next()); - assertEquals(n30, iter.getAdjNode()); - assertEquals(93147, iter.getDistance(), 1); - - assertTrue(iter.next()); - assertEquals(n10, iter.getAdjNode()); - assertEquals(88643, iter.getDistance(), 1); + Exception exception = assertThrows(RuntimeException.class, () -> { + new GraphHopperFacade(fileNegIds).importOrLoad(); + }); + assertTrue(exception.getCause().getMessage().contains("Invalid OSM NODE Id: -10;")); } @Test @@ -340,7 +330,7 @@ public void testBarriers() { setMinNetworkSize(0). importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); // we ignore the barrier at node 50, but not the one at node 20 assertEquals(7, graph.getNodes()); assertEquals(7, graph.getEdges()); @@ -379,7 +369,7 @@ public void testBarrierBetweenWays() { setMinNetworkSize(0). importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); // there are seven ways, but there should also be six barrier edges // we first split the loop way into two parts, and then we split the barrier node => 3 edges total assertEquals(7 + 6, graph.getEdges()); @@ -397,7 +387,7 @@ public void testBarrierBetweenWays() { @Test public void testFords() { GraphHopper hopper = new GraphHopper(); - hopper.setFlagEncodersString("car|block_fords=true"); + hopper.setVehiclesString("car|block_fords=true"); hopper.setOSMFile(getClass().getResource("test-barriers3.xml").getFile()). setGraphHopperLocation(dir). setProfiles( @@ -405,15 +395,15 @@ public void testFords() { ). setMinNetworkSize(0). importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); // our way is split into five edges, because there are two ford nodes assertEquals(5, graph.getEdges()); - FlagEncoder encoder = hopper.getEncodingManager().fetchEdgeEncoders().get(0); + BooleanEncodedValue accessEnc = hopper.getEncodingManager().getBooleanEncodedValue(VehicleAccess.key("car")); int blocked = 0; int notBlocked = 0; AllEdgesIterator edge = graph.getAllEdges(); while (edge.next()) { - if (!edge.get(encoder.getAccessEnc())) + if (!edge.get(accessEnc)) blocked++; else notBlocked++; @@ -434,7 +424,7 @@ public void avoidsLoopEdges_1525() { } void checkLoop(GraphHopper hopper) { - GraphHopperStorage graph = hopper.getGraphHopperStorage(); + BaseGraph graph = hopper.getBaseGraph(); // A, B, E and one of C or D should be tower nodes, in any case C and D should not be collapsed entirely // into a loop edge from B to B. @@ -468,7 +458,7 @@ public void testBarriersOnTowerNodes() { GraphHopper hopper = new GraphHopperFacade(fileBarriers). setMinNetworkSize(0). importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); // we ignore the barrier at node 50 // 10-20-30 produces three edges: 10-20, 20-2x, 2x-30, the second one is a barrier edge assertEquals(7, graph.getNodes()); @@ -491,36 +481,48 @@ public void testBarriersOnTowerNodes() { @Test public void testRelation() { - TagParserManager manager = TagParserManager.create("bike"); + EnumEncodedValue bikeNetworkEnc = new EnumEncodedValue<>(BikeNetwork.KEY, RouteNetwork.class); + EncodingManager manager = new EncodingManager.Builder().add(bikeNetworkEnc).build(); + OSMParsers osmParsers = new OSMParsers() + .addRelationTagParser(relConf -> new OSMBikeNetworkTagParser(bikeNetworkEnc, relConf)); ReaderRelation osmRel = new ReaderRelation(1); - osmRel.add(new ReaderRelation.Member(ReaderRelation.WAY, 1, "")); - osmRel.add(new ReaderRelation.Member(ReaderRelation.WAY, 2, "")); + osmRel.add(new ReaderRelation.Member(ReaderElement.Type.WAY, 1, "")); + osmRel.add(new ReaderRelation.Member(ReaderElement.Type.WAY, 2, "")); + + // this is pretty ugly: the bike network parser writes to the edge flags we pass into it, but at a location we + // don't know, so we need to get the internal enc to read the flags below + EnumEncodedValue transformEnc = ((OSMBikeNetworkTagParser) osmParsers.getRelationTagParsers().get(0)).getTransformerRouteRelEnc(); osmRel.setTag("route", "bicycle"); osmRel.setTag("network", "lcn"); - IntsRef flags = manager.createRelationFlags(); - manager.handleRelationTags(osmRel, flags); - assertFalse(flags.isEmpty()); + IntsRef relFlags = manager.createRelationFlags(); + IntsRefEdgeIntAccess intAccess = new IntsRefEdgeIntAccess(relFlags); + int edgeId = 0; + osmParsers.handleRelationTags(osmRel, relFlags); + assertEquals(RouteNetwork.LOCAL, transformEnc.getEnum(false, edgeId, intAccess)); // unchanged network - IntsRef before = IntsRef.deepCopyOf(flags); - manager.handleRelationTags(osmRel, flags); - assertEquals(before, flags); + IntsRef before = IntsRef.deepCopyOf(relFlags); + osmParsers.handleRelationTags(osmRel, relFlags); + assertEquals(before, relFlags); + assertEquals(RouteNetwork.LOCAL, transformEnc.getEnum(false, edgeId, intAccess)); + assertEquals(RouteNetwork.LOCAL, transformEnc.getEnum(false, edgeId, intAccess)); // overwrite network osmRel.setTag("network", "ncn"); - manager.handleRelationTags(osmRel, flags); - assertNotEquals(before, flags); + osmParsers.handleRelationTags(osmRel, relFlags); + assertEquals(RouteNetwork.NATIONAL, transformEnc.getEnum(false, edgeId, intAccess)); + assertNotEquals(before, relFlags); } @Test - public void testTurnRestrictions() { + public void testTurnRestrictionsFromXML() { String fileTurnRestrictions = "test-restrictions.xml"; GraphHopper hopper = new GraphHopperFacade(fileTurnRestrictions, true, ""). importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); assertEquals(15, graph.getNodes()); TurnCostStorage tcStorage = graph.getTurnCostStorage(); assertNotNull(tcStorage); @@ -580,10 +582,36 @@ public void testTurnRestrictions() { assertTrue(tcStorage.get(carTCEnc, edge11_14, n11, edge10_11) == 0); assertTrue(tcStorage.get(bikeTCEnc, edge11_14, n11, edge10_11) == 0); - assertTrue(tcStorage.get(carTCEnc, edge10_11, n11, edge11_14) == 0); + // the turn is restricted for car even though it turns into a one-way, but we treat this separately now + assertTrue(tcStorage.get(carTCEnc, edge10_11, n11, edge11_14) > 0); assertTrue(tcStorage.get(bikeTCEnc, edge10_11, n11, edge11_14) > 0); } + @Test + public void testTurnRestrictionsViaHgvTransportationMode() { + String fileTurnRestrictions = "test-restrictions.xml"; + GraphHopper hopper = new GraphHopperFacade(fileTurnRestrictions, true, ""). + importOrLoad(); + + Graph graph = hopper.getBaseGraph(); + assertEquals(15, graph.getNodes()); + TurnCostStorage tcStorage = graph.getTurnCostStorage(); + assertNotNull(tcStorage); + + int n3 = AbstractGraphStorageTester.getIdOf(graph, 52, 11); + int n8 = AbstractGraphStorageTester.getIdOf(graph, 54, 11); + int n9 = AbstractGraphStorageTester.getIdOf(graph, 54, 10); + + int edge9_3 = GHUtility.getEdge(graph, n9, n3).getEdge(); + int edge3_8 = GHUtility.getEdge(graph, n3, n8).getEdge(); + + DecimalEncodedValue carTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("car")); + DecimalEncodedValue roadsTCEnc = hopper.getEncodingManager().getDecimalEncodedValue(TurnCost.key("roads")); + + assertTrue(tcStorage.get(carTCEnc, edge9_3, n3, edge3_8) == 0); + assertTrue(tcStorage.get(roadsTCEnc, edge9_3, n3, edge3_8) > 0); + } + @Test public void testRoadAttributes() { String fileRoadAttributes = "test-road-attributes.xml"; @@ -595,7 +623,7 @@ public void testRoadAttributes() { DecimalEncodedValue heightEnc = hopper.getEncodingManager().getDecimalEncodedValue(MaxHeight.KEY); DecimalEncodedValue weightEnc = hopper.getEncodingManager().getDecimalEncodedValue(MaxWeight.KEY); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); assertEquals(5, graph.getNodes()); int na = AbstractGraphStorageTester.getIdOf(graph, 11.1, 50); @@ -638,7 +666,7 @@ public void testReadEleFromDataProvider() { hopper.setElevationProvider(provider); hopper.importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); int n10 = AbstractGraphStorageTester.getIdOf(graph, 49.501); int n30 = AbstractGraphStorageTester.getIdOf(graph, 49.5011); int n50 = AbstractGraphStorageTester.getIdOf(graph, 49.5001); @@ -658,17 +686,23 @@ public void testReadEleFromDataProvider() { @Test public void testTurnFlagCombination() { GraphHopper hopper = new GraphHopper(); - hopper.setFlagEncoderFactory((name, config) -> { + hopper.setVehicleEncodedValuesFactory((name, config) -> { if (name.equals("truck")) { - return new CarTagParser(new PMap(config).putObject("name", "truck")) { - @Override - public TransportationMode getTransportationMode() { - return TransportationMode.HGV; - } - }; + return VehicleEncodedValues.car(new PMap(config).putObject("name", "truck")); } else { - return new DefaultFlagEncoderFactory().createFlagEncoder(name, config); + return new DefaultVehicleEncodedValuesFactory().createVehicleEncodedValues(name, config); + } + }); + hopper.setVehicleTagParserFactory((lookup, name, config) -> { + if (name.equals("truck")) { + return new VehicleTagParsers( + new CarAccessParser(lookup.getBooleanEncodedValue(VehicleAccess.key("truck")), lookup.getBooleanEncodedValue(Roundabout.KEY), config, TransportationMode.HGV) + .init(config.getObject("date_range_parser", new DateRangeParser())), + new CarAverageSpeedParser(lookup.getDecimalEncodedValue(VehicleSpeed.key("truck")), 120), + null + ); } + return new DefaultVehicleTagParserFactory().createParsers(lookup, name, config); }); hopper.setOSMFile(getClass().getResource("test-multi-profile-turn-restrictions.xml").getFile()). setGraphHopperLocation(dir). @@ -683,7 +717,7 @@ public TransportationMode getTransportationMode() { DecimalEncodedValue truckTCEnc = manager.getDecimalEncodedValue(TurnCost.key("truck")); DecimalEncodedValue bikeTCEnc = manager.getDecimalEncodedValue(TurnCost.key("bike")); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); TurnCostStorage tcStorage = graph.getTurnCostStorage(); int edge1 = GHUtility.getEdge(graph, 1, 0).getEdge(); @@ -714,7 +748,7 @@ public void testConditionalTurnRestriction() { setMinNetworkSize(0). importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); assertEquals(8, graph.getNodes()); TurnCostStorage tcStorage = graph.getTurnCostStorage(); assertNotNull(tcStorage); @@ -782,7 +816,7 @@ public void testMultipleTurnRestrictions() { GraphHopper hopper = new GraphHopperFacade(fileMultipleConditionalTurnRestrictions, true, ""). importOrLoad(); - Graph graph = hopper.getGraphHopperStorage(); + Graph graph = hopper.getBaseGraph(); assertEquals(5, graph.getNodes()); TurnCostStorage tcStorage = graph.getTurnCostStorage(); assertNotNull(tcStorage); @@ -823,7 +857,7 @@ public void testMultipleTurnRestrictions() { public void testPreferredLanguage() { GraphHopper hopper = new GraphHopperFacade(file1, false, "de"). importOrLoad(); - GraphHopperStorage graph = hopper.getGraphHopperStorage(); + BaseGraph graph = hopper.getBaseGraph(); int n20 = AbstractGraphStorageTester.getIdOf(graph, 52); EdgeIterator iter = carOutExplorer.setBaseNode(n20); assertTrue(iter.next()); @@ -831,7 +865,7 @@ public void testPreferredLanguage() { hopper = new GraphHopperFacade(file1, false, "el"). importOrLoad(); - graph = hopper.getGraphHopperStorage(); + graph = hopper.getBaseGraph(); n20 = AbstractGraphStorageTester.getIdOf(graph, 52); iter = carOutExplorer.setBaseNode(n20); assertTrue(iter.next()); @@ -844,9 +878,8 @@ public void testDataDateWithinPBF() { GraphHopper hopper = new GraphHopperFacade("test-osm6.pbf") .setMinNetworkSize(0) .importOrLoad(); - GraphHopperStorage graph = hopper.getGraphHopperStorage(); - - assertEquals("2014-01-02T00:10:14Z", graph.getProperties().get("datareader.data.date")); + StorableProperties properties = hopper.getProperties(); + assertEquals("2014-01-02T00:10:14Z", properties.get("datareader.data.date")); } @Test @@ -865,23 +898,6 @@ public void testCrossBoundary_issue667() { assertEquals(112, snap.getClosestEdge().getDistance() / 1000, 1); } - @Test - public void testRoutingRequestFails_issue665() { - GraphHopper hopper = new GraphHopper() - .setOSMFile(getClass().getResource(file7).getFile()) - .setProfiles( - new Profile("profile1").setVehicle("car").setWeighting("fastest"), - new Profile("profile2").setVehicle("motorcycle").setWeighting("curvature") - ) - .setGraphHopperLocation(dir); - hopper.importOrLoad(); - GHRequest req = new GHRequest(48.977277, 8.256896, 48.978876, 8.254884). - setProfile("profile2"); - - GHResponse ghRsp = hopper.route(req); - assertFalse(ghRsp.hasErrors(), ghRsp.getErrors().toString()); - } - @Test public void testRoadClassInfo() { GraphHopper gh = new GraphHopper() { @@ -898,23 +914,26 @@ protected File _getOSMFile() { GHResponse response = gh.route(new GHRequest(51.2492152, 9.4317166, 52.133, 9.1) .setProfile("profile") .setPathDetails(Collections.singletonList(RoadClass.KEY))); + assertFalse(response.hasErrors(), response.getErrors().toString()); List list = response.getBest().getPathDetails().get(RoadClass.KEY); assertEquals(3, list.size()); assertEquals(RoadClass.MOTORWAY.toString(), list.get(0).getValue()); response = gh.route(new GHRequest(51.2492152, 9.4317166, 52.133, 9.1) .setProfile("profile") - .setPathDetails(Collections.singletonList(Toll.KEY))); + .setPathDetails(Arrays.asList(Toll.KEY, Country.KEY))); Throwable ex = response.getErrors().get(0); - assertTrue(ex.getMessage().contains("You requested the details [toll]"), ex.getMessage()); + assertEquals("Cannot find the path details: [toll, country]", ex.getMessage()); } @Test public void testCountries() throws IOException { - TagParserManager em = TagParserManager.create("car"); + EncodingManager em = new EncodingManager.Builder().build(); EnumEncodedValue roadAccessEnc = em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); - BaseGraph graph = new BaseGraph.Builder(em.getEncodingManager()).create(); - OSMReader reader = new OSMReader(graph, em, new OSMReaderConfig()); + OSMParsers osmParsers = new OSMParsers(); + osmParsers.addWayTagParser(new OSMRoadAccessParser(roadAccessEnc, OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR))); + BaseGraph graph = new BaseGraph.Builder(em).create(); + OSMReader reader = new OSMReader(graph, osmParsers, new OSMReaderConfig()); reader.setCountryRuleFactory(new CountryRuleFactory()); reader.setAreaIndex(createCountryIndex()); // there are two edges, both with highway=track, one in Berlin, one in Paris @@ -928,19 +947,26 @@ public void testCountries() throws IOException { assertEquals(RoadAccess.DESTINATION, edgeBerlin.get(roadAccessEnc)); // for Paris there is no such rule, we just get the default RoadAccess.YES assertEquals(RoadAccess.YES, edgeParis.get(roadAccessEnc)); + + ReaderWay way = new ReaderWay(0L); + PointList list = new PointList(); + list.add(49.214906, -2.156067); + reader.setArtificialWayTags(list, way, 10, Collections.singletonList(new HashMap<>())); + assertEquals("JEY", way.getTag("country", null).toString()); } @Test public void testCurvedWayAlongBorder() throws IOException { // see https://discuss.graphhopper.com/t/country-of-way-is-wrong-on-road-near-border-with-curvature/6908/2 - EnumEncodedValue countryEnc = new EnumEncodedValue<>(Country.KEY, Country.class); - TagParserManager em = TagParserManager.start() - .add(FlagEncoders.createCar()) + EnumEncodedValue countryEnc = Country.create(); + EncodingManager em = EncodingManager.start() + .add(VehicleEncodedValues.car(new PMap())) .add(countryEnc) - .add(new CountryParser(countryEnc)) .build(); - BaseGraph graph = new BaseGraph.Builder(em.getEncodingManager()).create(); - OSMReader reader = new OSMReader(graph, em, new OSMReaderConfig()); + OSMParsers osmParsers = new OSMParsers() + .addWayTagParser(new CountryParser(countryEnc)); + BaseGraph graph = new BaseGraph.Builder(em).create(); + OSMReader reader = new OSMReader(graph, osmParsers, new OSMReaderConfig()); reader.setCountryRuleFactory(new CountryRuleFactory()); reader.setAreaIndex(createCountryIndex()); reader.setFile(new File(getClass().getResource("test-osm12.xml").getFile())); @@ -970,24 +996,26 @@ public GraphHopperFacade(String osmFile, boolean turnCosts, String prefLang) { setStoreOnFlush(false); setOSMFile(osmFile); setGraphHopperLocation(dir); + if (turnCosts) setVehiclesString("roads|turn_costs=true|transportation_mode=HGV"); setProfiles( new Profile("foot").setVehicle("foot").setWeighting("fastest"), new Profile("car").setVehicle("car").setWeighting("fastest").setTurnCosts(turnCosts), - new Profile("bike").setVehicle("bike").setWeighting("fastest").setTurnCosts(turnCosts) + new Profile("bike").setVehicle("bike").setWeighting("fastest").setTurnCosts(turnCosts), + new Profile("roads").setVehicle("roads").setWeighting("fastest").setTurnCosts(turnCosts) ); getReaderConfig().setPreferredLanguage(prefLang); } @Override protected void importOSM() { - GraphHopperStorage tmpGraph = new GraphBuilder(getEncodingManager()).set3D(hasElevation()).withTurnCosts(getEncodingManager().needsTurnCostsSupport()).build(); - setGraphHopperStorage(tmpGraph); + BaseGraph baseGraph = new BaseGraph.Builder(getEncodingManager()).set3D(hasElevation()).withTurnCosts(getEncodingManager().needsTurnCostsSupport()).build(); + setBaseGraph(baseGraph); super.importOSM(); - carEncoder = getEncodingManager().getEncoder("car"); - footEncoder = getEncodingManager().getEncoder("foot"); - carAccessEnc = carEncoder.getAccessEnc(); - carOutExplorer = getGraphHopperStorage().createEdgeExplorer(AccessFilter.outEdges(carAccessEnc)); - carAllExplorer = getGraphHopperStorage().createEdgeExplorer(AccessFilter.allEdges(carAccessEnc)); + carAccessEnc = getEncodingManager().getBooleanEncodedValue(VehicleAccess.key("car")); + carSpeedEnc = getEncodingManager().getDecimalEncodedValue(VehicleSpeed.key("car")); + carOutExplorer = getBaseGraph().createEdgeExplorer(AccessFilter.outEdges(carAccessEnc)); + carAllExplorer = getBaseGraph().createEdgeExplorer(AccessFilter.allEdges(carAccessEnc)); + footAccessEnc = getEncodingManager().getBooleanEncodedValue(VehicleAccess.key("foot")); } @Override diff --git a/core/src/test/java/com/graphhopper/reader/osm/OSMTurnRelationTest.java b/core/src/test/java/com/graphhopper/reader/osm/OSMTurnRelationTest.java deleted file mode 100644 index 3f9bf3fff53..00000000000 --- a/core/src/test/java/com/graphhopper/reader/osm/OSMTurnRelationTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.reader.osm; - -import com.graphhopper.reader.OSMTurnRelation; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author Peter Karich - */ -public class OSMTurnRelationTest { - @Test - public void testAcceptsTurnRelation() { - List vehicleTypes = new ArrayList<>(Arrays.asList("motorcar", "motor_vehicle", "vehicle")); - List vehicleTypesExcept = new ArrayList<>(); - OSMTurnRelation osmTurnRelation = new OSMTurnRelation(1, 1, 1, OSMTurnRelation.Type.NOT); - assertTrue(osmTurnRelation.isVehicleTypeConcernedByTurnRestriction(vehicleTypes)); - - vehicleTypesExcept.add("bus"); - osmTurnRelation.setVehicleTypesExcept(vehicleTypesExcept); - assertTrue(osmTurnRelation.isVehicleTypeConcernedByTurnRestriction(vehicleTypes)); - - vehicleTypesExcept.clear(); - vehicleTypesExcept.add("vehicle"); - osmTurnRelation.setVehicleTypesExcept(vehicleTypesExcept); - assertFalse(osmTurnRelation.isVehicleTypeConcernedByTurnRestriction(vehicleTypes)); - - vehicleTypesExcept.clear(); - vehicleTypesExcept.add("motor_vehicle"); - vehicleTypesExcept.add("vehicle"); - osmTurnRelation.setVehicleTypesExcept(vehicleTypesExcept); - assertFalse(osmTurnRelation.isVehicleTypeConcernedByTurnRestriction(vehicleTypes)); - - vehicleTypesExcept.clear(); - osmTurnRelation.setVehicleTypeRestricted("bus"); - osmTurnRelation.setVehicleTypesExcept(vehicleTypesExcept); - assertFalse(osmTurnRelation.isVehicleTypeConcernedByTurnRestriction(vehicleTypes)); - - osmTurnRelation.setVehicleTypeRestricted("vehicle"); - assertTrue(osmTurnRelation.isVehicleTypeConcernedByTurnRestriction(vehicleTypes)); - } -} diff --git a/core/src/test/java/com/graphhopper/reader/OSMElementTest.java b/core/src/test/java/com/graphhopper/reader/osm/ReaderElementTest.java similarity index 74% rename from core/src/test/java/com/graphhopper/reader/OSMElementTest.java rename to core/src/test/java/com/graphhopper/reader/osm/ReaderElementTest.java index 7bbd7c0049b..6c2413c2a20 100644 --- a/core/src/test/java/com/graphhopper/reader/OSMElementTest.java +++ b/core/src/test/java/com/graphhopper/reader/osm/ReaderElementTest.java @@ -15,20 +15,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.graphhopper.reader; +package com.graphhopper.reader.osm; +import com.graphhopper.reader.ReaderElement; +import com.graphhopper.reader.ReaderWay; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; /** * @author Peter Karich */ -public class OSMElementTest { +public class ReaderElementTest { @Test public void testHasTag() { ReaderElement instance = new ReaderWay(1); @@ -40,7 +41,7 @@ public void testHasTag() { @Test public void testSetTags() { ReaderElement instance = new ReaderWay(1); - Map map = new HashMap<>(); + Map map = new HashMap<>(); map.put("test", "xy"); instance.setTags(map); assertTrue(instance.hasTag("test", "xy")); @@ -48,4 +49,13 @@ public void testSetTags() { instance.setTags(null); assertFalse(instance.hasTag("test", "xy")); } + + @Test + public void testInvalidIDs() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + new ReaderWay(-1); + }); + assertTrue(exception.getMessage().contains("Invalid OSM WAY Id: -1;")); + } + } diff --git a/core/src/test/java/com/graphhopper/reader/osm/RestrictionTagParserTest.java b/core/src/test/java/com/graphhopper/reader/osm/RestrictionTagParserTest.java new file mode 100644 index 00000000000..48235338ec1 --- /dev/null +++ b/core/src/test/java/com/graphhopper/reader/osm/RestrictionTagParserTest.java @@ -0,0 +1,177 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +import static com.graphhopper.reader.osm.RestrictionType.NO; +import static com.graphhopper.reader.osm.RestrictionType.ONLY; +import static org.junit.jupiter.api.Assertions.*; + +class RestrictionTagParserTest { + private final Map tags = new LinkedHashMap<>(); + + @BeforeEach + void setup() { + tags.clear(); + tags.put("type", "restriction"); + } + + private RestrictionTagParser.Result parseForVehicleTypes(String... vehicleTypes) throws OSMRestrictionException { + return new RestrictionTagParser(Arrays.asList(vehicleTypes), null).parseRestrictionTags(tags); + } + + @Test + void noRestrictions() { + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> parseForVehicleTypes("motorcar")); + assertTrue(e.getMessage().contains("neither has a 'restriction' nor 'restriction:' tags"), e.getMessage()); + } + + @Test + void exceptButNoRestriction() { + tags.put("restriction:bicycle", "no_right_turn"); + tags.put("except", "psv"); + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> parseForVehicleTypes("psv")); + assertTrue(e.getMessage().contains("has an 'except', but no 'restriction' or 'restriction:conditional' tag"), e.getMessage()); + } + + @Test + void exceptButOnlyLimitedRestriction() { + tags.put("restriction:hgv:conditional", "no_right_turn @ (weight > 3.5)"); + tags.put("except", "psv"); + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> parseForVehicleTypes("psv")); + assertTrue(e.getMessage().contains("has an 'except', but no 'restriction' or 'restriction:conditional' tag"), e.getMessage()); + } + + @Test + void exceptWithConditional() throws OSMRestrictionException { + tags.put("restriction:conditional", "no_right_turn @ (weight > 3.5)"); + tags.put("except", "psv"); + // we do not handle conditional restrictions yet, but no warning for except+restriction:conditional, + // because this combination of tags could make sense + assertNull(parseForVehicleTypes("psv")); + } + + @Test + void restrictionAndLimitedRestriction() { + // restriction and restriction:vehicle + tags.put("restriction", "no_left_turn"); + tags.put("restriction:psv", "no_left_turn"); + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> parseForVehicleTypes("psv")); + assertTrue(e.getMessage().contains("has a 'restriction' tag, but also 'restriction:' tags"), e.getMessage()); + } + + @Test + void restrictionAndLimitedRestriction_giveWay() throws OSMRestrictionException { + // this could happen for real, or at least it wouldn't be nonsensical. we ignore give_way so far, but do not + // ignore or warn about the entire relation + tags.put("restriction:bicycle", "give_way"); + tags.put("restriction", "no_left_turn"); + RestrictionTagParser.Result res = parseForVehicleTypes("bicycle"); + assertEquals("no_left_turn", res.getRestriction()); + assertEquals(NO, res.getRestrictionType()); + } + + @Test + void bicycle_giveWay() { + tags.put("restriction:bicycle", "give_way"); + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, () -> parseForVehicleTypes("bicycle")); + assertTrue(e.isWithoutWarning()); + } + + @Test + void conditional() throws OSMRestrictionException { + // So far we are ignoring conditional restrictions, even though for example weight restrictions could + // be interesting, even though some of them could probably be tagged as restriction:hgv + tags.put("restriction:conditional", "no_left_turn @ (weight > 3.5)"); + assertNull(parseForVehicleTypes("motorcar")); + } + + @Test + void except() throws OSMRestrictionException { + tags.put("restriction", "no_left_turn"); + tags.put("except", "psv"); + assertNull(parseForVehicleTypes("psv")); + } + + @Test + void exceptOther() throws OSMRestrictionException { + tags.put("restriction", "only_left_turn"); + tags.put("except", "psv"); + RestrictionTagParser.Result res = parseForVehicleTypes("motorcar"); + assertEquals("only_left_turn", res.getRestriction()); + assertEquals(ONLY, res.getRestrictionType()); + } + + @Test + void limitedToVehicle() throws OSMRestrictionException { + tags.put("restriction:motorcar", "no_left_turn"); + RestrictionTagParser.Result res = parseForVehicleTypes("motorcar"); + assertEquals("no_left_turn", res.getRestriction()); + assertEquals(NO, res.getRestrictionType()); + } + + @Test + void limitedToOtherVehicle() throws OSMRestrictionException { + tags.put("restriction:motorcar", "no_left_turn"); + RestrictionTagParser.Result res = parseForVehicleTypes("bicycle"); + assertNull(res); + } + + @Test + void limitedMultiple() throws OSMRestrictionException { + tags.put("restriction:motorcar", "no_left_turn"); + tags.put("restriction:psv", "no_left_turn"); + RestrictionTagParser.Result res = parseForVehicleTypes("motorcar"); + assertEquals("no_left_turn", res.getRestriction()); + assertEquals(NO, res.getRestrictionType()); + } + + @Test + void limitedMultipleOther() throws OSMRestrictionException { + tags.put("restriction:motorcar", "no_left_turn"); + tags.put("restriction:psv", "no_left_turn"); + RestrictionTagParser.Result res = parseForVehicleTypes("bicycle"); + assertNull(res); + } + + @Test + void multipleVehicleTypes() throws OSMRestrictionException { + tags.put("restriction", "only_right_turn"); + RestrictionTagParser.Result res = parseForVehicleTypes("motorcar", "motor_vehicle", "vehicle"); + assertEquals("only_right_turn", res.getRestriction()); + assertEquals(ONLY, res.getRestrictionType()); + } + + @Test + void exceptBus() throws OSMRestrictionException { + tags.put("restriction", "only_right_turn"); + // todo: how should we handle except=bus? should it be excluded for psv? + tags.put("except", "psv"); + RestrictionTagParser.Result res = parseForVehicleTypes("motorcar", "motor_vehicle", "vehicle"); + assertEquals("only_right_turn", res.getRestriction()); + assertEquals(ONLY, res.getRestrictionType()); + assertNull(parseForVehicleTypes("psv")); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/reader/osm/WayToEdgeConverterTest.java b/core/src/test/java/com/graphhopper/reader/osm/WayToEdgeConverterTest.java new file mode 100644 index 00000000000..d6a2cdc69eb --- /dev/null +++ b/core/src/test/java/com/graphhopper/reader/osm/WayToEdgeConverterTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.carrotsearch.hppc.IntArrayList; +import com.carrotsearch.hppc.LongArrayList; +import com.carrotsearch.hppc.cursors.IntCursor; +import com.graphhopper.storage.BaseGraph; +import org.junit.jupiter.api.Test; + +import java.util.Iterator; +import java.util.function.LongFunction; + +import static org.junit.jupiter.api.Assertions.*; + +class WayToEdgeConverterTest { + + @Test + void convertForViaWays() throws OSMRestrictionException { + BaseGraph graph = new BaseGraph.Builder(1).create(); + for (int i = 0; i < 10; i++) + graph.edge(i, i + 1); + WayToEdgeConverter.EdgeResult edgeResult = new WayToEdgeConverter(graph, way -> IntArrayList.from(Math.toIntExact(way)).iterator()) + .convertForViaWays(ways(0), ways(2, 6, 4, 1, 7, 3, 5, 8), ways(9)); + assertEquals(IntArrayList.from(1, 2, 3, 4, 5, 6, 7, 8), edgeResult.getViaEdges()); + assertEquals(IntArrayList.from(1, 2, 3, 4, 5, 6, 7, 8, 9), edgeResult.getNodes()); + } + + @Test + void convertForViaWays_throwsIfViaWayIsSplitIntoMultipleEdges() { + BaseGraph graph = new BaseGraph.Builder(1).create(); + graph.edge(0, 1); + graph.edge(1, 2); + graph.edge(2, 3); + graph.edge(3, 4); + LongFunction> edgesByWay = way -> { + // way 0 and 2 simply correspond to edges 0 and 3, but way 1 is split into the two edges 1 and 2 + if (way == 1) return IntArrayList.from(1, 2).iterator(); + else return IntArrayList.from(Math.toIntExact(way)).iterator(); + }; + OSMRestrictionException e = assertThrows(OSMRestrictionException.class, + () -> new WayToEdgeConverter(graph, edgesByWay).convertForViaWays(ways(0), ways(1), ways(2))); + assertTrue(e.getMessage().contains("has via member way that isn't split at adjacent ways"), e.getMessage()); + } + + private LongArrayList ways(long... ways) { + return LongArrayList.from(ways); + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/reader/osm/WayToEdgesMapTest.java b/core/src/test/java/com/graphhopper/reader/osm/WayToEdgesMapTest.java new file mode 100644 index 00000000000..e1505d9b312 --- /dev/null +++ b/core/src/test/java/com/graphhopper/reader/osm/WayToEdgesMapTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.reader.osm; + +import com.carrotsearch.hppc.IntArrayList; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class WayToEdgesMapTest { + + @Test + void notReserved() { + WayToEdgesMap wayToEdgesMap = new WayToEdgesMap(); + wayToEdgesMap.reserve(5); + wayToEdgesMap.putIfReserved(5, 4); + wayToEdgesMap.putIfReserved(6, 2); + checkEdges(wayToEdgesMap, 5, 4); + // since we did not reserve 6, no edges were added. This is exactly what we want for turn restrictions when + // reading OSM: first we reserve all the ways that appear in restriction relations and later we only add the + // edges for these (but not other) ways. + checkEdges(wayToEdgesMap, 6); + } + + @Test + void waysMustBeInOrder() { + WayToEdgesMap wayToEdgesMap = new WayToEdgesMap(); + wayToEdgesMap.reserve(10); + wayToEdgesMap.reserve(11); + wayToEdgesMap.putIfReserved(10, 3); + wayToEdgesMap.putIfReserved(11, 5); + assertThrows(IllegalArgumentException.class, () -> wayToEdgesMap.putIfReserved(10, 3)); + } + + @Test + void basic() { + WayToEdgesMap wayToEdgesMap = new WayToEdgesMap(); + wayToEdgesMap.reserve(10); + wayToEdgesMap.reserve(11); + wayToEdgesMap.reserve(12); + wayToEdgesMap.reserve(6); + wayToEdgesMap.reserve(1234); + wayToEdgesMap.reserve(7); + wayToEdgesMap.putIfReserved(10, 3); + wayToEdgesMap.putIfReserved(10, 5); + wayToEdgesMap.putIfReserved(6, 1); + wayToEdgesMap.putIfReserved(1234, 12); + wayToEdgesMap.putIfReserved(1234, 13); + wayToEdgesMap.putIfReserved(1234, 13); + wayToEdgesMap.putIfReserved(7, 2); + checkEdges(wayToEdgesMap, 10, 3, 5); + checkEdges(wayToEdgesMap, 6, 1); + checkEdges(wayToEdgesMap, 1234, 12, 13, 13); + checkEdges(wayToEdgesMap, 7, 2); + checkEdges(wayToEdgesMap, 42); + } + + @Test + void another() { + WayToEdgesMap wayToEdgesMap = new WayToEdgesMap(); + wayToEdgesMap.reserve(1); + wayToEdgesMap.reserve(2); + wayToEdgesMap.reserve(3); + wayToEdgesMap.putIfReserved(1, 0); + wayToEdgesMap.putIfReserved(1, 1); + wayToEdgesMap.putIfReserved(3, 3); + wayToEdgesMap.putIfReserved(3, 4); + checkEdges(wayToEdgesMap, 1, 0, 1); + checkEdges(wayToEdgesMap, 3, 3, 4); + } + + @Test + void reserveButDoNotPut() { + WayToEdgesMap wayToEdgesMap = new WayToEdgesMap(); + wayToEdgesMap.reserve(1); + wayToEdgesMap.reserve(2); + wayToEdgesMap.putIfReserved(2, 42); + checkEdges(wayToEdgesMap, 1); + checkEdges(wayToEdgesMap, 2, 42); + } + + private void checkEdges(WayToEdgesMap wayToEdgesMap, long way, int... expected) { + IntArrayList edges = new IntArrayList(); + wayToEdgesMap.getEdges(way).forEachRemaining(c -> edges.add(c.value)); + assertEquals(IntArrayList.from(expected), edges); + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspectorTest.java b/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspectorTest.java index c8560c553af..5efca154e75 100644 --- a/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspectorTest.java +++ b/core/src/test/java/com/graphhopper/reader/osm/conditional/ConditionalOSMTagInspectorTest.java @@ -17,7 +17,6 @@ */ package com.graphhopper.reader.osm.conditional; -import com.graphhopper.reader.ConditionalTagInspector; import com.graphhopper.reader.ReaderWay; import org.junit.jupiter.api.Test; @@ -44,10 +43,10 @@ private static Set getSampleRestrictedValues() { } private static Set getSamplePermissiveValues() { - Set restrictedValues = new HashSet<>(); - restrictedValues.add("yes"); - restrictedValues.add("permissive"); - return restrictedValues; + Set permissiveValues = new HashSet<>(); + permissiveValues.add("yes"); + permissiveValues.add("permissive"); + return permissiveValues; } private static List getSampleConditionalTags() { diff --git a/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java b/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java new file mode 100644 index 00000000000..33b260e7c85 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/AStarBidirectionTest.java @@ -0,0 +1,121 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing; + +import com.carrotsearch.hppc.IntArrayList; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; +import com.graphhopper.routing.weighting.ShortestWeighting; +import com.graphhopper.routing.weighting.WeightApproximator; +import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.GHUtility; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AStarBidirectionTest { + @Test + void infeasibleApproximator_noException() { + // An infeasible approximator means that the weight of the entries polled from the priority queue does not + // increase monotonically. Here we deliberately choose the approximations and edge distances such that the fwd + // search first explores the 0-1-2-3-4 branch, then polls node 10 which causes an update for node 2, but the + // search stops before node 2 is polled again such that nodes 3 and 4 cannot be updated, because the bwd search + // already arrived and the stopping criterion is fulfilled. Node 2 still remains in the queue at this point. + // This means the resulting path contains the invalid search tree branch 2(old)-3-4 and is not the shortest path, + // because the SPTEntry for node 3 still points to the outdated/deleted entry for node 2. + // We do not expect an exception, though, because for an infeasible approximator we cannot expect optimal paths. + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + BaseGraph graph = new BaseGraph.Builder(em).create(); + // 0-1----2-3-4----5-6-7-8-9 + // \ / + // 10 + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); + // the distance 1-2 is longer than 1-10-2 + // we deliberately use 2-1 as storage direction, even though the edge points from 1 to 2, because this way + // we can reproduce the 'Calculating time should not require to read speed from edge in wrong direction' error + // from #2600 + graph.edge(2, 1).setDistance(300).set(accessEnc, false, true).set(speedEnc, 60); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(100)); + // distance 4-5 is very long + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(10_000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 8).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(8, 9).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 10).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(10, 2).setDistance(100)); + + Weighting weighting = new ShortestWeighting(accessEnc, speedEnc); + AStarBidirection algo = new AStarBidirection(graph, weighting, TraversalMode.NODE_BASED); + algo.setApproximation(new InfeasibleApproximator()); + Path path = algo.calcPath(0, 9); + // the path is not the shortest path, but the suboptimal one we get for this approximator + assertEquals(11_000, path.getDistance()); + assertEquals(IntArrayList.from(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), path.calcNodes()); + + // this returns the correct path + Dijkstra dijkstra = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED); + Path optimalPath = dijkstra.calcPath(0, 9); + assertEquals(10_900, optimalPath.getDistance()); + assertEquals(IntArrayList.from(0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9), optimalPath.calcNodes()); + } + + private static class InfeasibleApproximator implements WeightApproximator { + int to; + + @Override + public double approximate(int currentNode) { + // we only consider the fwd search (going to 9). for the bwd search we simply approximate 0 + if (to != 9) + return 0; + // we use a super-simple approximator that just returns 0 for all nodes but one. for node 10 we use + // a 'better' approximation that is still off. it is certainly not an over-approximation, because of the + // long edge 4-5, but it makes the approximator infeasible, because + // d(10, 2) + h(2) = 100 + 0 = 100 and h(10) = 1000, so it does not hold that d(10, 2) + h(2) >= h(10) + if (currentNode == 10) + return 1000; + else + return 0; + } + + @Override + public void setTo(int to) { + this.to = to; + } + + @Override + public WeightApproximator reverse() { + // the reverse approximator is a different object (different 'to' field), but runs the same code + return new InfeasibleApproximator(); + } + + @Override + public double getSlack() { + return 0; + } + } +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java index ca3cd63148e..daf70797c94 100644 --- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java +++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteCHTest.java @@ -19,9 +19,11 @@ import com.graphhopper.routing.ch.NodeOrderingProvider; import com.graphhopper.routing.ch.PrepareContractionHierarchies; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.CHConfig; @@ -36,8 +38,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class AlternativeRouteCHTest { - private final FlagEncoder carFE = FlagEncoders.createCar(); - private final EncodingManager em = EncodingManager.create(carFE); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); public BaseGraph createTestGraph(EncodingManager tmpEM) { final BaseGraph graph = new BaseGraph.Builder(tmpEM).create(); @@ -56,7 +59,7 @@ public BaseGraph createTestGraph(EncodingManager tmpEM) { // has to be locally-shortest to be considered. // So we get all three alternatives. - GHUtility.setSpeed(60, 60, carFE, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(5, 6).setDistance(10000), graph.edge(6, 3).setDistance(10000), graph.edge(3, 4).setDistance(10000), @@ -81,7 +84,7 @@ private RoutingCHGraph prepareCH(BaseGraph graph) { // meet on all four possible paths from 5 to 10 // 5 ---> 11 will be reachable via shortcuts, as 11 is on shortest path 5 --> 12 final int[] nodeOrdering = new int[]{0, 10, 12, 4, 3, 2, 5, 1, 6, 7, 8, 9, 11}; - CHConfig chConfig = CHConfig.nodeBased("p", new FastestWeighting(carFE)); + CHConfig chConfig = CHConfig.nodeBased("p", new FastestWeighting(accessEnc, speedEnc)); PrepareContractionHierarchies contractionHierarchies = PrepareContractionHierarchies.fromGraph(graph, chConfig); contractionHierarchies.useFixedNodeOrdering(NodeOrderingProvider.fromArray(nodeOrdering)); PrepareContractionHierarchies.Result res = contractionHierarchies.doWork(); diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteEdgeCHTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteEdgeCHTest.java index 71cd35eb383..47b9eab6cfa 100644 --- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteEdgeCHTest.java +++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteEdgeCHTest.java @@ -19,11 +19,8 @@ import com.carrotsearch.hppc.IntArrayList; import com.graphhopper.routing.ch.PrepareContractionHierarchies; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.TurnCostProvider; @@ -40,8 +37,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class AlternativeRouteEdgeCHTest { - private final FlagEncoder carFE = FlagEncoders.createCar(new PMap().putObject("turn_costs", true)); - private final EncodingManager em = EncodingManager.create(carFE); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final DecimalEncodedValue turnCostEnc = TurnCost.create("car", 1); + private final EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); public BaseGraph createTestGraph(EncodingManager tmpEM) { final BaseGraph graph = new BaseGraph.Builder(tmpEM).withTurnCosts(true).create(); @@ -60,37 +59,35 @@ public BaseGraph createTestGraph(EncodingManager tmpEM) { // has to be locally-shortest to be considered. // So we get all three alternatives. - FlagEncoder encoder = carFE; - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(10000)); - EdgeIteratorState e6_3 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 3).setDistance(10000)); - EdgeIteratorState e3_4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 10).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(10000)); + EdgeIteratorState e6_3 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 3).setDistance(10000)); + EdgeIteratorState e3_4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 10).setDistance(10000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(10000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 8).setDistance(10000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 4).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 8).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 4).setDistance(10000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 1).setDistance(10000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 9).setDistance(10000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(9, 2).setDistance(10000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 1).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 9).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(9, 2).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10000)); - EdgeIteratorState e4_11 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 11).setDistance(9000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(11, 12).setDistance(9000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(12, 10).setDistance(10000)); + EdgeIteratorState e4_11 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 11).setDistance(9000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(11, 12).setDistance(9000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(12, 10).setDistance(10000)); TurnCostStorage turnCostStorage = graph.getTurnCostStorage(); - DecimalEncodedValue carTurnCost = em.getDecimalEncodedValue(TurnCost.key(carFE.toString())); - turnCostStorage.set(carTurnCost, e3_4.getEdge(), 4, e4_11.getEdge(), Double.POSITIVE_INFINITY); - turnCostStorage.set(carTurnCost, e6_3.getEdge(), 3, e3_4.getEdge(), Double.POSITIVE_INFINITY); + turnCostStorage.set(turnCostEnc, e3_4.getEdge(), 4, e4_11.getEdge(), Double.POSITIVE_INFINITY); + turnCostStorage.set(turnCostEnc, e6_3.getEdge(), 3, e3_4.getEdge(), Double.POSITIVE_INFINITY); graph.freeze(); return graph; } private RoutingCHGraph prepareCH(BaseGraph graph) { - TurnCostProvider turnCostProvider = new DefaultTurnCostProvider(carFE, graph.getTurnCostStorage()); - CHConfig chConfig = CHConfig.edgeBased("profile", new FastestWeighting(carFE, turnCostProvider)); + TurnCostProvider turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()); + CHConfig chConfig = CHConfig.edgeBased("profile", new FastestWeighting(accessEnc, speedEnc, turnCostProvider)); PrepareContractionHierarchies contractionHierarchies = PrepareContractionHierarchies.fromGraph(graph, chConfig); PrepareContractionHierarchies.Result res = contractionHierarchies.doWork(); return RoutingCHGraphImpl.fromGraph(graph, res.getCHStorage(), res.getCHConfig()); @@ -99,8 +96,8 @@ private RoutingCHGraph prepareCH(BaseGraph graph) { @Test public void testAssumptions() { BaseGraph g = createTestGraph(em); - TurnCostProvider turnCostProvider = new DefaultTurnCostProvider(carFE, g.getTurnCostStorage()); - CHConfig chConfig = CHConfig.edgeBased("profile", new FastestWeighting(carFE, turnCostProvider)); + TurnCostProvider turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, g.getTurnCostStorage()); + CHConfig chConfig = CHConfig.edgeBased("profile", new FastestWeighting(accessEnc, speedEnc, turnCostProvider)); CHStorage chStorage = CHStorage.fromGraph(g, chConfig); RoutingCHGraph chGraph = RoutingCHGraphImpl.fromGraph(g, chStorage, chConfig); DijkstraBidirectionEdgeCHNoSOD router = new DijkstraBidirectionEdgeCHNoSOD(chGraph); diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java index febf978919f..7aa702b0420 100644 --- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java +++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java @@ -18,10 +18,8 @@ package com.graphhopper.routing; import com.carrotsearch.hppc.IntArrayList; -import com.graphhopper.routing.AlternativeRoute.AlternativeBidirSearch; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; @@ -30,6 +28,7 @@ import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; import com.graphhopper.util.GHUtility; +import com.graphhopper.util.PMap; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -48,17 +47,22 @@ private static final class Fixture { final Weighting weighting; final TraversalMode traversalMode; final BaseGraph graph; - final FlagEncoder carFE; + final BooleanEncodedValue accessEnc; + final DecimalEncodedValue speedEnc; + final DecimalEncodedValue turnCostEnc; public Fixture(TraversalMode tMode) { this.traversalMode = tMode; - carFE = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(carFE); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + turnCostEnc = TurnCost.create("car", 1); + + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).add(turnCostEnc).build(); graph = new BaseGraph.Builder(em).withTurnCosts(true).create(); TurnCostProvider turnCostProvider = tMode.isEdgeBased() - ? new DefaultTurnCostProvider(carFE, graph.getTurnCostStorage()) + ? new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()) : TurnCostProvider.NO_TURN_COST_PROVIDER; - weighting = new FastestWeighting(carFE, turnCostProvider); + weighting = new FastestWeighting(accessEnc, speedEnc, turnCostProvider); } @Override @@ -77,7 +81,7 @@ public Stream provideArguments(ExtensionContext context) th } } - public static void initTestGraph(Graph graph, FlagEncoder encoder) { + public static void initTestGraph(Graph graph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { /* 9 _/\ 1 2-3-4-10 @@ -85,7 +89,7 @@ public static void initTestGraph(Graph graph, FlagEncoder encoder) { 5--6-7---8 */ - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(1, 9).setDistance(1), graph.edge(9, 2).setDistance(1), graph.edge(2, 3).setDistance(1), @@ -114,10 +118,12 @@ public static void initTestGraph(Graph graph, FlagEncoder encoder) { @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testCalcAlternatives(Fixture f) { - initTestGraph(f.graph, f.carFE); - AlternativeRoute altDijkstra = new AlternativeRoute(f.graph, f.weighting, f.traversalMode); - altDijkstra.setMaxShareFactor(0.5); - altDijkstra.setMaxWeightFactor(2); + initTestGraph(f.graph, f.accessEnc, f.speedEnc); + PMap hints = new PMap(). + putObject("alternative_route.max_share_factor", 0.5). + putObject("alternative_route.max_weight_factor", 2). + putObject("alternative_route.max_exploration_factor", 1.3); + AlternativeRoute altDijkstra = new AlternativeRoute(f.graph, f.weighting, f.traversalMode, hints); List pathInfos = altDijkstra.calcAlternatives(5, 4); checkAlternatives(pathInfos); assertEquals(2, pathInfos.size()); @@ -143,15 +149,13 @@ public void testCalcAlternatives(Fixture f) { @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testCalcAlternatives2(Fixture f) { - initTestGraph(f.graph, f.carFE); - AlternativeRoute altDijkstra = new AlternativeRoute(f.graph, f.weighting, f.traversalMode); - altDijkstra.setMaxPaths(3); - altDijkstra.setMaxShareFactor(0.7); - altDijkstra.setMinPlateauFactor(0.15); - altDijkstra.setMaxWeightFactor(2); - // edge based traversal requires a bit more exploration than the default of 1 - altDijkstra.setMaxExplorationFactor(1.2); - + initTestGraph(f.graph, f.accessEnc, f.speedEnc); + PMap hints = new PMap().putObject("alternative_route.max_paths", 3). + putObject("alternative_route.max_share_factor", 0.7). + putObject("alternative_route.min_plateau_factor", 0.15). + putObject("alternative_route.max_weight_factor", 2). + putObject("alternative_route.max_exploration_factor", 1.8); + AlternativeRoute altDijkstra = new AlternativeRoute(f.graph, f.weighting, f.traversalMode, hints); List pathInfos = altDijkstra.calcAlternatives(5, 4); checkAlternatives(pathInfos); assertEquals(3, pathInfos.size()); @@ -179,13 +183,14 @@ private void checkAlternatives(List alternativ @ParameterizedTest @ArgumentsSource(FixtureProvider.class) - public void testDisconnectedAreas(Fixture p) { - initTestGraph(p.graph, p.carFE); + public void testDisconnectedAreas(Fixture f) { + initTestGraph(f.graph, f.accessEnc, f.speedEnc); // one single disconnected node - updateDistancesFor(p.graph, 20, 0.00, -0.01); + updateDistancesFor(f.graph, 20, 0.00, -0.01); - AlternativeBidirSearch altDijkstra = new AlternativeBidirSearch(p.graph, p.weighting, p.traversalMode, 1); + PMap hints = new PMap().putObject("alternative_route.max_exploration_factor", 1); + AlternativeRoute altDijkstra = new AlternativeRoute(f.graph, f.weighting, f.traversalMode, hints); Path path = altDijkstra.calcPath(1, 20); assertFalse(path.isFound()); diff --git a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java index 212f34651cc..769c00b088b 100644 --- a/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java +++ b/core/src/test/java/com/graphhopper/routing/CHQueryWithTurnCostsTest.java @@ -20,17 +20,13 @@ import com.carrotsearch.hppc.IntArrayList; import com.graphhopper.routing.ch.PrepareEncoder; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.storage.*; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -54,8 +50,9 @@ public class CHQueryWithTurnCostsTest { private static class Fixture { private final int maxCost = 10; - private final FlagEncoder encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", maxCost).putObject("speed_two_directions", true)); - private final EncodingManager encodingManager = EncodingManager.create(encoder); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + private final DecimalEncodedValue turnCostEnc = TurnCost.create("car", maxCost); private final BaseGraph graph; private final CHConfig chConfig; private final String algoString; @@ -64,8 +61,9 @@ private static class Fixture { public Fixture(String algoString) { this.algoString = algoString; - graph = new BaseGraph.Builder(encodingManager).create(); - chConfig = CHConfig.edgeBased("profile", new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage()))); + EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); + chConfig = CHConfig.edgeBased("profile", new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()))); } @Override @@ -85,9 +83,9 @@ private void freeze() { chBuilder = new CHStorageBuilder(chStore); } - private void addShortcut(int from, int to, int firstOrigEdge, int lastOrigEdge, int skipped1, int skipped2, double weight, boolean reverse) { + private void addShortcut(int from, int to, int firstOrigEdgeKey, int lastOrigEdgeKey, int skipped1, int skipped2, double weight, boolean reverse) { int flags = reverse ? PrepareEncoder.getScBwdDir() : PrepareEncoder.getScFwdDir(); - chBuilder.addShortcutEdgeBased(from, to, flags, weight, skipped1, skipped2, firstOrigEdge, lastOrigEdge); + chBuilder.addShortcutEdgeBased(from, to, flags, weight, skipped1, skipped2, firstOrigEdgeKey, lastOrigEdgeKey); } private void setIdentityLevels() { @@ -99,7 +97,7 @@ private void setTurnCost(int from, int via, int to, double cost) { } private void setTurnCost(EdgeIteratorState edge1, EdgeIteratorState edge2, int viaNode, double costs) { - graph.getTurnCostStorage().set(((EncodedValueLookup) encodingManager).getDecimalEncodedValue(TurnCost.key(encoder.toString())), edge1.getEdge(), viaNode, edge2.getEdge(), costs); + graph.getTurnCostStorage().set(turnCostEnc, edge1.getEdge(), viaNode, edge2.getEdge(), costs); } private void setRestriction(int from, int via, int to) { @@ -152,8 +150,8 @@ public Stream provideArguments(ExtensionContext context) { public void testFindPathWithTurnCosts_bidirected_no_shortcuts_smallGraph(Fixture f) { // some special cases where from=to, or start and target edges are the same // 1 -- 0 -- 2 - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(1, 0).setDistance(3)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(0, 2).setDistance(5)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(1, 0).setDistance(3)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(0, 2).setDistance(5)); f.setTurnCost(1, 0, 2, 3); f.freeze(); @@ -175,12 +173,12 @@ public void testFindPathWithTurnCosts_bidirected_no_shortcuts_smallGraph(Fixture @ArgumentsSource(FixtureProvider.class) public void testFindPathWithTurnCosts_bidirected_no_shortcuts(Fixture f) { // 0 -- 2 -- 4 -- 6 -- 5 -- 3 -- 1 - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(0, 2).setDistance(3)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(2, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(4, 6).setDistance(7)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(6, 5).setDistance(9)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(5, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(3, 1).setDistance(4)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(0, 2).setDistance(3)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(2, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(4, 6).setDistance(7)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(6, 5).setDistance(9)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(5, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(3, 1).setDistance(4)); f.setTurnCost(0, 2, 4, 3); f.setTurnCost(4, 6, 5, 6); f.setTurnCost(5, 6, 4, 2); @@ -214,15 +212,15 @@ public void testFindPathWithTurnCosts_loopShortcutBwdSearch(Fixture f) { // 1 2 // \ / // 0 - 7 - 8 - 4 - 6 - 5 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(7, 8).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(8, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(4, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(1, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(4, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(6, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(8, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(1, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(4, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(6, 5).setDistance(1)); f.setRestriction(8, 4, 6); f.setRestriction(8, 4, 2); f.setRestriction(1, 4, 6); @@ -231,13 +229,13 @@ public void testFindPathWithTurnCosts_loopShortcutBwdSearch(Fixture f) { f.setIdentityLevels(); // from contracting nodes 1&2 - f.addShortcut(3, 4, 3, 4, 3, 4, 2, true); - f.addShortcut(3, 4, 5, 6, 5, 6, 2, false); + f.addShortcut(3, 4, 6, 8, 3, 4, 2, true); + f.addShortcut(3, 4, 10, 12, 5, 6, 2, false); // from contracting node 3 - f.addShortcut(4, 4, 3, 6, 9, 10, 4, false); + f.addShortcut(4, 4, 6, 13, 9, 10, 4, false); // from contracting node 4 - f.addShortcut(4, 8, 2, 6, 2, 11, 5, true); - f.addShortcut(6, 8, 2, 7, 12, 7, 6, true); + f.addShortcut(4, 8, 4, 12, 2, 11, 5, true); + f.addShortcut(6, 8, 4, 14, 12, 7, 6, true); f.testPathCalculation(0, 5, 9, IntArrayList.from(0, 7, 8, 4, 1, 3, 2, 4, 6, 5)); } @@ -251,7 +249,7 @@ public void testFindPathWithTurnCosts_loopShortcutFwdSearch(Fixture f) { // 1 2 // \ / // 5 - 6 - 4 - 7 - 8 - 0 - GHUtility.setSpeed(60, 0, f.encoder, + GHUtility.setSpeed(60, 0, f.accessEnc, f.speedEnc, f.graph.edge(5, 6).setDistance(1), f.graph.edge(6, 4).setDistance(1), f.graph.edge(4, 1).setDistance(1), @@ -268,13 +266,13 @@ public void testFindPathWithTurnCosts_loopShortcutFwdSearch(Fixture f) { f.setIdentityLevels(); // from contracting nodes 1&2 - f.addShortcut(3, 4, 2, 3, 2, 3, 2, true); - f.addShortcut(3, 4, 4, 5, 4, 5, 2, false); + f.addShortcut(3, 4, 4, 6, 2, 3, 2, true); + f.addShortcut(3, 4, 8, 10, 4, 5, 2, false); // from contracting node 3 - f.addShortcut(4, 4, 2, 5, 9, 10, 4, false); + f.addShortcut(4, 4, 4, 10, 9, 10, 4, false); // from contracting node 4 - f.addShortcut(4, 6, 1, 5, 1, 11, 5, true); - f.addShortcut(6, 7, 1, 6, 12, 6, 6, false); + f.addShortcut(4, 6, 3, 10, 1, 11, 5, true); + f.addShortcut(6, 7, 2, 12, 12, 6, 6, false); f.testPathCalculation(5, 0, 9, IntArrayList.from(5, 6, 4, 1, 3, 2, 4, 7, 8, 0)); } @@ -286,10 +284,10 @@ public void testFindPathWithTurnCosts_directed_single_shortcut(Fixture f) { // /5\ /1\ // / \2/ \ // 1 0 4 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(1, 2).setDistance(4)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 0).setDistance(2)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 3).setDistance(3)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 4).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(1, 2).setDistance(4)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 0).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 3).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 4).setDistance(2)); f.setTurnCost(1, 2, 0, 5); f.setTurnCost(2, 0, 3, 2); f.setTurnCost(0, 3, 4, 1); @@ -297,7 +295,7 @@ public void testFindPathWithTurnCosts_directed_single_shortcut(Fixture f) { // only when node 0 is contracted a shortcut is added f.setIdentityLevels(); - f.addShortcut(2, 3, 1, 2, 1, 2, 7, false); + f.addShortcut(2, 3, 2, 4, 1, 2, 7, false); // when we are searching a path to the highest level node, the backward search will not expand any edges f.testPathCalculation(1, 4, 11, IntArrayList.from(1, 2, 0, 3, 4), 8); @@ -315,10 +313,10 @@ public void testFindPathWithTurnCosts_directed_single_shortcut_fwdSearchStopsQui // 0 // / \ // 1-2-s-3-4 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(1, 2).setDistance(2)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 0).setDistance(3)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 4).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 0).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 4).setDistance(3)); f.freeze(); f.setTurnCost(1, 2, 0, 2); @@ -326,7 +324,7 @@ public void testFindPathWithTurnCosts_directed_single_shortcut_fwdSearchStopsQui f.setIdentityLevels(); // from contracting node 0 - f.addShortcut(2, 3, 1, 2, 1, 2, 4, false); + f.addShortcut(2, 3, 2, 4, 1, 2, 4, false); f.testPathCalculation(1, 4, 9, IntArrayList.from(1, 2, 0, 3, 4), 6); } @@ -338,10 +336,10 @@ public void testFindPathWithTurnCosts_directed_two_shortcuts(Fixture f) { // /5\ /1\ // / \2/ \ // 2 1 4 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 3).setDistance(4)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 1).setDistance(2)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(1, 0).setDistance(3)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 4).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 3).setDistance(4)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 1).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(1, 0).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 4).setDistance(2)); f.setTurnCost(2, 3, 1, 5); f.setTurnCost(3, 1, 0, 2); f.setTurnCost(1, 0, 4, 1); @@ -349,8 +347,8 @@ public void testFindPathWithTurnCosts_directed_two_shortcuts(Fixture f) { f.setIdentityLevels(); // contraction of node 0 and 1 each yield a single shortcut - f.addShortcut(1, 4, 2, 3, 2, 3, 6, false); - f.addShortcut(3, 4, 1, 3, 1, 4, 10, false); + f.addShortcut(1, 4, 4, 6, 2, 3, 6, false); + f.addShortcut(3, 4, 2, 6, 1, 4, 10, false); // the turn costs have to be accounted for also when the shortcuts are used f.testPathCalculation(2, 4, 11, IntArrayList.from(2, 3, 1, 0, 4), 8); @@ -368,10 +366,10 @@ public void testFindPath_directConnectionIsNotTheBestPath(Fixture f) { // | | // v v // 2 -> 3 -> 1 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 2).setDistance(3)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 3).setDistance(2)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 1).setDistance(9)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 1).setDistance(50)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 2).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 3).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 1).setDistance(9)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 1).setDistance(50)); f.setTurnCost(2, 3, 1, 4); f.freeze(); @@ -389,7 +387,7 @@ public void testFindPath_upwardSearchRunsIntoTarget(Fixture f) { // | | // v v // 3 -> 4 -> 2 - GHUtility.setSpeed(60, 0, f.encoder, + GHUtility.setSpeed(60, 0, f.accessEnc, f.speedEnc, f.graph.edge(0, 1).setDistance(9), f.graph.edge(1, 5).setDistance(2), f.graph.edge(1, 3).setDistance(2), @@ -411,10 +409,10 @@ public void testFindPath_downwardSearchRunsIntoTarget(Fixture f) { // \ ^ // \ | // <-2<-3 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(1, 0).setDistance(9)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 0).setDistance(14)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 1).setDistance(2)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 2).setDistance(9)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(1, 0).setDistance(9)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 0).setDistance(14)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 1).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 2).setDistance(9)); f.freeze(); //no shortcuts @@ -430,12 +428,12 @@ public void testFindPath_incomingShortcut(Fixture f) { // | __/ // v/ // 3 -> 2 - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(0, 1).setDistance(9)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 3).setDistance(14)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 2).setDistance(9)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(0, 1).setDistance(9)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 3).setDistance(14)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 2).setDistance(9)); f.freeze(); f.setIdentityLevels(); - f.addShortcut(1, 3, 0, 1, 0, 1, 23, false); + f.addShortcut(1, 3, 1, 2, 0, 1, 23, false); f.testPathCalculation(0, 2, 23, IntArrayList.from(0, 3, 2)); } @@ -445,9 +443,9 @@ public void testFindPathWithTurnCosts_fwdBwdSearchesMeetWithUTurn(Fixture f) { // 3 // | // 0 --- 2 --- 1 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(2, 3).setDistance(2)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 1).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(2, 3).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 1).setDistance(3)); f.setRestriction(0, 2, 1); f.setTurnCost(0, 2, 3, 5); f.setTurnCost(2, 3, 2, 4); @@ -489,20 +487,20 @@ private void checkUTurnNotBeingUsed(Fixture f, boolean toLowerLevelNode) { nodeA = nodeB; nodeB = tmp; } - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(1, nodeA).setDistance(4)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 3).setDistance(4)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(nodeB, 2).setDistance(1)); - final EdgeIteratorState e3toB = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, nodeB).setDistance(2)); - final EdgeIteratorState e3toA = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(3, nodeA).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(1, nodeA).setDistance(4)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 3).setDistance(4)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(nodeB, 2).setDistance(1)); + final EdgeIteratorState e3toB = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, nodeB).setDistance(2)); + final EdgeIteratorState e3toA = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(3, nodeA).setDistance(1)); f.freeze(); f.setRestriction(0, 3, nodeB); // one shortcut when contracting node 3 f.setIdentityLevels(); if (toLowerLevelNode) { - f.addShortcut(nodeB, nodeA, e3toA.getEdge(), e3toB.getEdge(), e3toA.getEdge(), e3toB.getEdge(), 2, true); + f.addShortcut(nodeB, nodeA, e3toA.detach(true).getEdgeKey(), e3toB.getEdgeKey(), e3toA.getEdge(), e3toB.getEdge(), 2, true); } else { - f.addShortcut(nodeA, nodeB, e3toA.getEdge(), e3toB.getEdge(), e3toA.getEdge(), e3toB.getEdge(), 2, false); + f.addShortcut(nodeA, nodeB, e3toA.detach(true).getEdgeKey(), e3toB.getEdgeKey(), e3toA.getEdge(), e3toB.getEdge(), 2, false); } // without u-turns the only 'possible' path 0-3-A-3-B-2 is forbidden @@ -515,10 +513,10 @@ public void testFindPathWithTurnCosts_loop(Fixture f) { // 3\ // |/ // 0 --- 2 --- 1 - final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 2).setDistance(4)); - final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(2, 3).setDistance(1)); - final EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 2).setDistance(7)); - final EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 1).setDistance(3)); + final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 2).setDistance(4)); + final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(2, 3).setDistance(1)); + final EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 2).setDistance(7)); + final EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 1).setDistance(3)); // need to specify edges explicitly because there are two edges between nodes 2 and 3 f.setRestriction(edge1, edge4, 2); f.setTurnCost(edge1, edge2, 2, 3); @@ -542,7 +540,7 @@ public void testFindPathWithTurnCosts_multiple_bridge_nodes(Fixture f) { // 0 --- 3 --- 1 // \ / // --- 4 --- - GHUtility.setSpeed(60, 0, f.encoder, + GHUtility.setSpeed(60, 0, f.accessEnc, f.speedEnc, f.graph.edge(0, 2).setDistance(1), f.graph.edge(0, 3).setDistance(3), f.graph.edge(0, 4).setDistance(2), @@ -568,10 +566,10 @@ public void testFindPath_loopIsRecognizedAsIncomingEdge(Fixture f) { // --- // \ / // 0 -- 3 -- 2 -- 1 - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(0, 3).setDistance(1)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 3).setDistance(1)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(3, 2).setDistance(1)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 1).setDistance(1)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(0, 3).setDistance(1)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 3).setDistance(1)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(3, 2).setDistance(1)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 1).setDistance(1)); f.setRestriction(edge0, edge2, 3); f.freeze(); @@ -591,17 +589,17 @@ public void testFindPath_shortcutLoopIsRecognizedAsIncomingEdge(Fixture f) { // -0- // \ / // 3 -- 4 -- 2 -- 1 - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(3, 4).setDistance(1)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(4, 2).setDistance(1)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 0).setDistance(1)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 2).setDistance(1)); - EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(2, 1).setDistance(1)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(3, 4).setDistance(1)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(4, 2).setDistance(1)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 0).setDistance(1)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 2).setDistance(1)); + EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(2, 1).setDistance(1)); f.setRestriction(edge1, edge4, 2); f.freeze(); f.setIdentityLevels(); - // contracting node 0 yields (the only) shortcut - and its a loop - f.addShortcut(2, 2, edge2.getEdge(), edge3.getEdge(), edge2.getEdge(), edge3.getEdge(), 2, false); + // contracting node 0 yields (the only) shortcut - and it's a loop + f.addShortcut(2, 2, edge2.getEdgeKey(), edge3.getEdgeKey(), edge2.getEdge(), edge3.getEdge(), 2, false); // node 2 is the bridge node where the forward and backward searches meet (highest level). since there is a turn restriction // at node 2 we cannot go from 4 to 1 directly, but we need to take the loop at 2 first. when the backward @@ -622,19 +620,19 @@ public void testFindPathWithTurnRestriction_single_loop(Fixture f) { // | // v no right turn at 4 when coming from 3! // 2 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(4, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 1).setDistance(3)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(4, 1).setDistance(5)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(4, 2).setDistance(4)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(4, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 1).setDistance(3)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(4, 1).setDistance(5)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(4, 2).setDistance(4)); f.setRestriction(3, 4, 2); f.freeze(); f.setIdentityLevels(); // contracting node 0 - f.addShortcut(1, 4, 1, 2, 1, 2, 4, true); + f.addShortcut(1, 4, 2, 4, 1, 2, 4, true); // contracting node 1 - f.addShortcut(4, 4, 1, 3, 5, 3, 9, false); + f.addShortcut(4, 4, 2, 6, 5, 3, 9, false); f.testPathCalculation(3, 2, 15, IntArrayList.from(3, 4, 0, 1, 4, 2)); } @@ -667,7 +665,7 @@ private void runTestWithSingleLoop(Fixture f, boolean loopInFwdSearch) { // A-5->2 // | // B-7 - GHUtility.setSpeed(60, 0, f.encoder, + GHUtility.setSpeed(60, 0, f.accessEnc, f.speedEnc, f.graph.edge(4, nodeA).setDistance(1), f.graph.edge(nodeA, 5).setDistance(2), f.graph.edge(5, 2).setDistance(2), @@ -679,9 +677,9 @@ private void runTestWithSingleLoop(Fixture f, boolean loopInFwdSearch) { f.setRestriction(nodeA, 5, nodeB); f.freeze(); f.setIdentityLevels(); - f.addShortcut(3, 5, 4, 5, 4, 5, 3, false); - f.addShortcut(3, 5, 2, 3, 2, 3, 3, true); - f.addShortcut(5, 5, 2, 5, 9, 8, 6, false); + f.addShortcut(3, 5, 8, 10, 4, 5, 3, false); + f.addShortcut(3, 5, 4, 6, 2, 3, 3, true); + f.addShortcut(5, 5, 4, 10, 9, 8, 6, false); f.testPathCalculation(4, 7, 12, IntArrayList.from(4, nodeA, 5, 2, 3, 1, 5, nodeB, 7)); } @@ -699,15 +697,15 @@ public void testFindPathWithTurnRestriction_double_loop(Fixture f) { // | // | no right turn at 7 when coming from 4 and no left turn at 7 when coming from 5! // 5 - final EdgeIteratorState e0to1 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(0, 1).setDistance(2)); - final EdgeIteratorState e1to6 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(1, 6).setDistance(1)); - final EdgeIteratorState e0to6 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(0, 6).setDistance(4)); - final EdgeIteratorState e2to6 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(2, 6).setDistance(5)); - final EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(2, 3).setDistance(3)); - final EdgeIteratorState e3to6 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(3, 6).setDistance(2)); - final EdgeIteratorState e6to7 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(7, 6).setDistance(1)); - final EdgeIteratorState e4to7 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(7, 4).setDistance(3)); - final EdgeIteratorState e5to7 = GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(7, 5).setDistance(2)); + final EdgeIteratorState e0to1 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(0, 1).setDistance(2)); + final EdgeIteratorState e1to6 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(1, 6).setDistance(1)); + final EdgeIteratorState e0to6 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(0, 6).setDistance(4)); + final EdgeIteratorState e2to6 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(2, 6).setDistance(5)); + final EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(2, 3).setDistance(3)); + final EdgeIteratorState e3to6 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(3, 6).setDistance(2)); + final EdgeIteratorState e6to7 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(7, 6).setDistance(1)); + final EdgeIteratorState e4to7 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(7, 4).setDistance(3)); + final EdgeIteratorState e5to7 = GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(7, 5).setDistance(2)); f.setRestriction(e6to7, e1to6, 6); f.setRestriction(e6to7, e2to6, 6); @@ -722,15 +720,15 @@ public void testFindPathWithTurnRestriction_double_loop(Fixture f) { f.setIdentityLevels(); // contracting node 0,1,2,3 - f.addShortcut(1, 6, 2, 0, 2, 0, 6, true); - f.addShortcut(3, 6, 3, 4, 3, 4, 8, true); - f.addShortcut(6, 6, 2, 1, 9, 1, 7, false); - f.addShortcut(6, 6, 3, 5, 10, 5, 10, false); + f.addShortcut(1, 6, 4, 0, 2, 0, 6, true); + f.addShortcut(3, 6, 6, 8, 3, 4, 8, true); + f.addShortcut(6, 6, 4, 2, 9, 1, 7, false); + f.addShortcut(6, 6, 6, 10, 10, 5, 10, false); // contracting node 4 and 5 yields no shortcuts // contracting node 6 --> three shortcuts to account for double loop (we nest shortcuts inside each other) - f.addShortcut(6, 7, 6, 1, 6, 11, 8, true); - f.addShortcut(6, 7, 6, 5, 13, 12, 18, true); - f.addShortcut(7, 7, 6, 6, 14, 6, 19, false); + f.addShortcut(6, 7, 12, 2, 6, 11, 8, true); + f.addShortcut(6, 7, 12, 10, 13, 12, 18, true); + f.addShortcut(7, 7, 12, 12, 14, 6, 19, false); f.testPathCalculation(4, 5, 24, IntArrayList.from(4, 7, 6, 0, 1, 6, 2, 3, 6, 7, 5)); f.testPathCalculation(5, 4, 24, IntArrayList.from(5, 7, 6, 0, 1, 6, 2, 3, 6, 7, 4)); @@ -750,29 +748,29 @@ public void testFindPathWithTurnRestriction_two_different_loops(Fixture f) { // | // v no right turn at 6 when coming from 3! // 2 - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(0, 1).setDistance(2)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(1, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(5, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(5, 4).setDistance(5)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(5, 6).setDistance(3)); - GHUtility.setSpeed(60, true, true, f.encoder, f.graph.edge(6, 4).setDistance(4)); - - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(3, 6).setDistance(3)); - GHUtility.setSpeed(60, true, false, f.encoder, f.graph.edge(6, 2).setDistance(4)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(0, 1).setDistance(2)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(1, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(5, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(5, 4).setDistance(5)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(5, 6).setDistance(3)); + GHUtility.setSpeed(60, true, true, f.accessEnc, f.speedEnc, f.graph.edge(6, 4).setDistance(4)); + + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(3, 6).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.accessEnc, f.speedEnc, f.graph.edge(6, 2).setDistance(4)); f.setRestriction(3, 6, 2); f.freeze(); f.setIdentityLevels(); // contracting node 0 - f.addShortcut(1, 5, 2, 0, 2, 0, 3, true); + f.addShortcut(1, 5, 4, 0, 2, 0, 3, true); // contracting node 1 - f.addShortcut(5, 5, 2, 1, 8, 1, 4, false); + f.addShortcut(5, 5, 4, 2, 8, 1, 4, false); // contracting node 2 & 3 does not yield any shortcuts // contracting node 4 - f.addShortcut(5, 6, 3, 5, 3, 5, 9, false); + f.addShortcut(5, 6, 6, 11, 3, 5, 9, false); // contracting node 5 --> two shortcuts to account for loop (we nest shortcuts inside each other) - f.addShortcut(5, 6, 4, 1, 4, 9, 7, true); - f.addShortcut(6, 6, 4, 4, 11, 4, 10, false); + f.addShortcut(5, 6, 9, 2, 4, 9, 7, true); + f.addShortcut(6, 6, 9, 8, 11, 4, 10, false); // contracting node 6 --> no more shortcuts diff --git a/core/src/test/java/com/graphhopper/routing/DefaultBidirPathExtractorTest.java b/core/src/test/java/com/graphhopper/routing/DefaultBidirPathExtractorTest.java index 0a29d0b6b78..df84341d82a 100644 --- a/core/src/test/java/com/graphhopper/routing/DefaultBidirPathExtractorTest.java +++ b/core/src/test/java/com/graphhopper/routing/DefaultBidirPathExtractorTest.java @@ -18,18 +18,14 @@ package com.graphhopper.routing; import com.carrotsearch.hppc.IntArrayList; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; import com.graphhopper.storage.TurnCostStorage; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,20 +35,22 @@ * @author easbar */ public class DefaultBidirPathExtractorTest { - private final FlagEncoder carEncoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", 10)); - private final EncodingManager encodingManager = EncodingManager.create(carEncoder); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final DecimalEncodedValue turnCostEnc = TurnCost.create("car", 10); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); BaseGraph createGraph() { - return new BaseGraph.Builder(encodingManager).create(); + return new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); } @Test public void testExtract() { Graph graph = createGraph(); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); SPTEntry fwdEntry = new SPTEntry(0, 2, 0, new SPTEntry(1, 10)); SPTEntry bwdEntry = new SPTEntry(2, 0); - Path p = DefaultBidirPathExtractor.extractPath(graph, new FastestWeighting(carEncoder), fwdEntry, bwdEntry, 0); + Path p = DefaultBidirPathExtractor.extractPath(graph, new FastestWeighting(accessEnc, speedEnc), fwdEntry, bwdEntry, 0); assertEquals(IntArrayList.from(1, 2), p.calcNodes()); assertEquals(10, p.getDistance(), 1e-4); } @@ -61,18 +59,17 @@ public void testExtract() { public void testExtract2() { // 1->2->3 Graph graph = createGraph(); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(1, 2).setDistance(10)); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(2, 3).setDistance(20)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(20)); // add some turn costs at node 2 where fwd&bwd searches meet. these costs have to be included in the // weight and the time of the path TurnCostStorage turnCostStorage = graph.getTurnCostStorage(); - DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(carEncoder.toString())); turnCostStorage.set(turnCostEnc, 0, 2, 1, 5); SPTEntry fwdEntry = new SPTEntry(0, 2, 0.6, new SPTEntry(1, 0)); SPTEntry bwdEntry = new SPTEntry(1, 2, 1.2, new SPTEntry(3, 0)); - Path p = DefaultBidirPathExtractor.extractPath(graph, new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, turnCostStorage)), fwdEntry, bwdEntry, 0); + Path p = DefaultBidirPathExtractor.extractPath(graph, new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, turnCostStorage)), fwdEntry, bwdEntry, 0); p.setWeight(5 + 1.8); assertEquals(IntArrayList.from(1, 2, 3), p.calcNodes()); diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java index 2836edf61ac..0810d958ab9 100644 --- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java +++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java @@ -20,13 +20,14 @@ import com.carrotsearch.hppc.IntArrayList; import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory; import com.graphhopper.routing.ch.PrepareContractionHierarchies; +import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.storage.*; -import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; @@ -45,44 +46,56 @@ public class DijkstraBidirectionCHTest { private final EncodingManager encodingManager; - private final FlagEncoder carEncoder; - private final FlagEncoder bike2Encoder; - private final FlagEncoder motorCycleEncoder; + private final BooleanEncodedValue carAccessEnc; + private final DecimalEncodedValue carSpeedEnc; + private final BooleanEncodedValue bike2AccessEnc; + private final DecimalEncodedValue bike2SpeedEnc; + private final BooleanEncodedValue motorcycleAccessEnc; + private final DecimalEncodedValue motorcycleSpeedEnc; public DijkstraBidirectionCHTest() { - encodingManager = EncodingManager.create("car,foot,bike2,motorcycle"); - carEncoder = encodingManager.getEncoder("car"); - bike2Encoder = encodingManager.getEncoder("bike2"); - motorCycleEncoder = encodingManager.getEncoder("motorcycle"); + carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + bike2AccessEnc = new SimpleBooleanEncodedValue("bike2_access", true); + bike2SpeedEnc = new DecimalEncodedValueImpl("bike2_speed", 4, 2, true); + motorcycleAccessEnc = new SimpleBooleanEncodedValue("motorcycle_access", true); + motorcycleSpeedEnc = new DecimalEncodedValueImpl("motorcycle_speed", 5, 5, true); + encodingManager = EncodingManager.start() + .add(carAccessEnc).add(carSpeedEnc) + .add(bike2AccessEnc).add(bike2SpeedEnc) + .add(motorcycleAccessEnc).add(motorcycleSpeedEnc) + .build(); } @Test public void testBaseGraph() { BaseGraph graph = createGHStorage(); - RoutingAlgorithmTest.initDirectedAndDiffSpeed(graph, carEncoder); + RoutingAlgorithmTest.initDirectedAndDiffSpeed(graph, carAccessEnc, carSpeedEnc); // do CH preparation for car - ShortestWeighting weighting = new ShortestWeighting(carEncoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carSpeedEnc); prepareCH(graph, CHConfig.nodeBased(weighting.getName(), weighting)); // use base graph for solving normal Dijkstra Path p1 = new RoutingAlgorithmFactorySimple().createAlgo(graph, weighting, new AlgorithmOptions()).calcPath(0, 3); assertEquals(IntArrayList.from(0, 1, 5, 2, 3), p1.calcNodes()); assertEquals(402.30, p1.getDistance(), 1e-2, p1.toString()); - assertEquals(144829, p1.getTime(), p1.toString()); + assertEquals(144830, p1.getTime(), p1.toString()); } @Test public void testBaseGraphMultipleVehicles() { - EncodingManager em = EncodingManager.create("foot,car"); - FlagEncoder footEncoder = em.getEncoder("foot"); - FlagEncoder carEncoder = em.getEncoder("car"); - FastestWeighting footWeighting = new FastestWeighting(footEncoder); - FastestWeighting carWeighting = new FastestWeighting(carEncoder); + SimpleBooleanEncodedValue footAccessEnc = new SimpleBooleanEncodedValue("foot_access", true); + DecimalEncodedValueImpl footSpeedEnc = new DecimalEncodedValueImpl("foot_speed", 4, 1, false); + SimpleBooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + DecimalEncodedValueImpl carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(footAccessEnc).add(footSpeedEnc).add(carAccessEnc).add(carSpeedEnc).build(); + FastestWeighting footWeighting = new FastestWeighting(footAccessEnc, footSpeedEnc); + FastestWeighting carWeighting = new FastestWeighting(carAccessEnc, carSpeedEnc); CHConfig carConfig = CHConfig.nodeBased("p_car", carWeighting); BaseGraph g = new BaseGraph.Builder(em).create(); - RoutingAlgorithmTest.initFootVsCar(carEncoder, footEncoder, g); + RoutingAlgorithmTest.initFootVsCar(carAccessEnc, carSpeedEnc, footAccessEnc, footSpeedEnc, g); // do CH preparation for car RoutingCHGraph chGraph = prepareCH(g, carConfig); @@ -116,7 +129,7 @@ public void testBaseGraphMultipleVehicles() { @Test public void testStallingNodesReducesNumberOfVisitedNodes() { BaseGraph graph = createGHStorage(); - GHUtility.setSpeed(60, 0, carEncoder, + GHUtility.setSpeed(60, 0, carAccessEnc, carSpeedEnc, graph.edge(8, 9).setDistance(100), graph.edge(8, 3).setDistance(2), graph.edge(8, 5).setDistance(1), @@ -126,13 +139,13 @@ public void testStallingNodesReducesNumberOfVisitedNodes() { graph.edge(1, 8).setDistance(1), graph.edge(2, 3).setDistance(3)); for (int i = 3; i < 7; ++i) { - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(i, i + 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, carAccessEnc, carSpeedEnc, graph.edge(i, i + 1).setDistance(1)); } - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(9, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(3, 9).setDistance(200)); + GHUtility.setSpeed(60, true, false, carAccessEnc, carSpeedEnc, graph.edge(9, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, carAccessEnc, carSpeedEnc, graph.edge(3, 9).setDistance(200)); graph.freeze(); - ShortestWeighting weighting = new ShortestWeighting(carEncoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carSpeedEnc); CHConfig chConfig = CHConfig.nodeBased(weighting.getName(), weighting); CHStorage store = CHStorage.fromGraph(graph, chConfig); @@ -162,8 +175,8 @@ public void testStallingNodesReducesNumberOfVisitedNodes() { // \--<---| @Test public void testDirectionDependentSpeedFwdSearch() { - runTestWithDirectionDependentEdgeSpeed(10, 20, 0, 2, IntArrayList.from(0, 1, 2), motorCycleEncoder); - runTestWithDirectionDependentEdgeSpeed(10, 20, 0, 2, IntArrayList.from(0, 1, 2), bike2Encoder); + runTestWithDirectionDependentEdgeSpeed(10, 20, 0, 2, IntArrayList.from(0, 1, 2), motorcycleAccessEnc, motorcycleSpeedEnc); + runTestWithDirectionDependentEdgeSpeed(10, 20, 0, 2, IntArrayList.from(0, 1, 2), bike2AccessEnc, bike2SpeedEnc); } // s(0)--fast->1--t(2) @@ -172,19 +185,16 @@ public void testDirectionDependentSpeedFwdSearch() { // \--<---| @Test public void testDirectionDependentSpeedBwdSearch() { - runTestWithDirectionDependentEdgeSpeed(20, 10, 2, 0, IntArrayList.from(2, 1, 0), motorCycleEncoder); - runTestWithDirectionDependentEdgeSpeed(20, 10, 2, 0, IntArrayList.from(2, 1, 0), bike2Encoder); + runTestWithDirectionDependentEdgeSpeed(20, 10, 2, 0, IntArrayList.from(2, 1, 0), motorcycleAccessEnc, motorcycleSpeedEnc); + runTestWithDirectionDependentEdgeSpeed(20, 10, 2, 0, IntArrayList.from(2, 1, 0), bike2AccessEnc, bike2SpeedEnc); } - private void runTestWithDirectionDependentEdgeSpeed(double speed, double revSpeed, int from, int to, IntArrayList expectedPath, FlagEncoder encoder) { + private void runTestWithDirectionDependentEdgeSpeed(double speed, double revSpeed, int from, int to, IntArrayList expectedPath, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { BaseGraph graph = createGHStorage(); - EdgeIteratorState edge = GHUtility.setSpeed(encoder.getMaxSpeed() / 2, true, true, encoder, graph.edge(0, 1).setDistance(2)); - DecimalEncodedValue avSpeedEnc = encodingManager.getDecimalEncodedValue(EncodingManager.getKey(encoder, "average_speed")); - edge.set(avSpeedEnc, speed, revSpeed); - - GHUtility.setSpeed(encoder.getMaxSpeed() / 2, true, true, encoder, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(speed, revSpeed, accessEnc, speedEnc, graph.edge(0, 1).setDistance(2)); + GHUtility.setSpeed(20, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); graph.freeze(); - FastestWeighting weighting = new FastestWeighting(encoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased(weighting.getName(), weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); new CHStorageBuilder(chStore).setIdentityLevels(); diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java index 958cd39fe5b..fe3d0339b70 100644 --- a/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java +++ b/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java @@ -18,8 +18,11 @@ package com.graphhopper.routing; import com.carrotsearch.hppc.IntArrayList; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.routing.weighting.Weighting; @@ -40,17 +43,19 @@ */ public class DijkstraOneToManyTest { + private final BooleanEncodedValue accessEnc; + private final DecimalEncodedValue speedEnc; private final EncodingManager encodingManager; - private final FlagEncoder encoder; - private Weighting defaultWeighting; + private final Weighting defaultWeighting; public DijkstraOneToManyTest() { - encodingManager = EncodingManager.create("car"); - encoder = encodingManager.getEncoder("car"); - defaultWeighting = new ShortestWeighting(encoder); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + defaultWeighting = new ShortestWeighting(accessEnc, speedEnc); } - private static void initGraphWeightLimit(Graph graph, FlagEncoder encoder) { + private static void initGraphWeightLimit(Graph graph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { // 0----1 // / | // 7-- | @@ -59,7 +64,7 @@ private static void initGraphWeightLimit(Graph graph, FlagEncoder encoder) { // | | | // 4---3---2 - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1), graph.edge(1, 2).setDistance(1), graph.edge(3, 2).setDistance(1), @@ -87,7 +92,7 @@ public void testIssue182() { @Test public void testIssue239_and362() { BaseGraph graph = createGHStorage(); - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1), graph.edge(1, 2).setDistance(1), graph.edge(2, 0).setDistance(1), @@ -106,7 +111,7 @@ public void testIssue239_and362() { @Test public void testUseCache() { BaseGraph graph = createGHStorage(); - initTestStorage(graph, encoder); + initTestStorage(graph, accessEnc, speedEnc); RoutingAlgorithm algo = createAlgo(graph); Path p = algo.calcPath(0, 4); assertEquals(IntArrayList.from(0, 4), p.calcNodes()); @@ -125,7 +130,7 @@ private void initGraph(Graph graph) { // | / // 7-10---- // \-8 - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1), graph.edge(1, 2).setDistance(1), graph.edge(2, 3).setDistance(1), @@ -139,7 +144,7 @@ private void initGraph(Graph graph) { @Test public void testWeightLimit_issue380() { BaseGraph graph = createGHStorage(); - initGraphWeightLimit(graph, encoder); + initGraphWeightLimit(graph, accessEnc, speedEnc); DijkstraOneToMany algo = createAlgo(graph); algo.setWeightLimit(3); @@ -156,7 +161,7 @@ public void testWeightLimit_issue380() { @Test public void testUseCacheZeroPath_issue707() { BaseGraph graph = createGHStorage(); - initTestStorage(graph, encoder); + initTestStorage(graph, accessEnc, speedEnc); RoutingAlgorithm algo = createAlgo(graph); Path p = algo.calcPath(0, 0); diff --git a/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java b/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java index b026a033d68..4428f5751d2 100644 --- a/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java +++ b/core/src/test/java/com/graphhopper/routing/DirectedBidirectionalDijkstraTest.java @@ -2,10 +2,11 @@ import com.carrotsearch.hppc.IntArrayList; import com.carrotsearch.hppc.IntHashSet; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.QueryGraph; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.AvoidEdgesWeighting; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; @@ -18,7 +19,6 @@ import com.graphhopper.storage.index.Snap; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; @@ -41,32 +41,33 @@ public class DirectedBidirectionalDijkstraTest { private TurnCostStorage turnCostStorage; private int maxTurnCosts; private BaseGraph graph; - private FlagEncoder encoder; - private EncodingManager encodingManager; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; private Weighting weighting; private DecimalEncodedValue turnCostEnc; @BeforeEach public void setup() { maxTurnCosts = 10; - encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", maxTurnCosts)); - encodingManager = EncodingManager.create(encoder); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + turnCostEnc = TurnCost.create("car", maxTurnCosts); + EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); turnCostStorage = graph.getTurnCostStorage(); weighting = createWeighting(Weighting.INFINITE_U_TURN_COSTS); - turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(encoder.toString())); } private Weighting createWeighting(int uTurnCosts) { - return new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, uTurnCosts)); + return new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, turnCostStorage, uTurnCosts)); } @Test public void connectionNotFound() { // nodes 0 and 2 are not connected // 0 -> 1 2 -> 3 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); Path path = calcPath(0, 3, 0, 1); assertNotFound(path); @@ -74,7 +75,7 @@ public void connectionNotFound() { @Test public void singleEdge() { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); // source edge does not exist -> no path assertNotFound(calcPath(0, 1, 5, 0)); @@ -93,8 +94,8 @@ public void singleEdge() { @Test public void simpleGraph() { // 0 -> 1 -> 2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); // source edge does not exist -> no path assertNotFound(calcPath(0, 2, 5, 0)); @@ -118,9 +119,9 @@ public void sourceEqualsTarget() { // 0 - 1 // \ | // - 2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); assertPath(calcPath(0, 0, 0, 1), 0.18, 3, 180, nodes(0, 1, 2, 0)); assertPath(calcPath(0, 0, 1, 0), 0.18, 3, 180, nodes(0, 2, 1, 0)); // without restrictions the weight should be zero @@ -136,15 +137,15 @@ public void restrictedEdges() { // 0 = 1 - 2 - 3 = 4 // \ | / // - 5 - 6 - 7 - - int costlySource = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(5)).getEdge(); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(1)); - int costlyTarget = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(5)).getEdge(); - int cheapSource = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 5).setDistance(1)).getEdge(); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(1)); - int cheapTarget = GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 4).setDistance(1)).getEdge(); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 6).setDistance(1)); + int costlySource = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(5)).getEdge(); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + int costlyTarget = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(5)).getEdge(); + int cheapSource = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 5).setDistance(1)).getEdge(); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(1)); + int cheapTarget = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 4).setDistance(1)).getEdge(); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 6).setDistance(1)); assertPath(calcPath(0, 4, cheapSource, cheapTarget), 0.24, 4, 240, nodes(0, 5, 6, 7, 4)); assertPath(calcPath(0, 4, cheapSource, costlyTarget), 0.54, 9, 540, nodes(0, 5, 6, 2, 3, 4)); @@ -160,10 +161,10 @@ public void notConnectedDueToRestrictions() { // \ / // - 3 - // we cannot go from 0 to 2 if we enforce north-south or south-north - int sourceNorth = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)).getEdge(); - int sourceSouth = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 3).setDistance(2)).getEdge(); - int targetNorth = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(3)).getEdge(); - int targetSouth = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 2).setDistance(4)).getEdge(); + int sourceNorth = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)).getEdge(); + int sourceSouth = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 3).setDistance(2)).getEdge(); + int targetNorth = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(3)).getEdge(); + int targetSouth = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 2).setDistance(4)).getEdge(); assertPath(calcPath(0, 2, sourceNorth, targetNorth), 0.24, 4, 240, nodes(0, 1, 2)); assertNotFound(calcPath(0, 2, sourceNorth, targetSouth)); @@ -176,11 +177,11 @@ public void restrictions_one_ways() { // 0 <- 1 <- 2 // \ | / // >--3--> - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); assertPath(calcPath(0, 2, 0, 2), 0.12, 2, 120, nodes(0, 3, 2)); assertNotFound(calcPath(0, 2, 1, 2)); @@ -197,15 +198,15 @@ public void forcingDirectionDoesNotMeanWeCannotUseEdgeAtAll() { // 2 - 3 // | | // 5 - 4 - int north = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 0).setDistance(1)).getEdge(); - int south = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)).getEdge(); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 6).setDistance(1)); - int targetEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 7).setDistance(1)).getEdge(); + int north = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 0).setDistance(1)).getEdge(); + int south = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)).getEdge(); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 6).setDistance(1)); + int targetEdge = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(1)).getEdge(); assertPath(calcPath(1, 7, north, targetEdge), 0.18, 3, 180, nodes(1, 0, 6, 7)); assertPath(calcPath(1, 7, south, targetEdge), 0.54, 9, 540, nodes(1, 2, 5, 4, 3, 2, 1, 0, 6, 7)); } @@ -215,13 +216,13 @@ public void directedCircle() { // 0---6--1 -> 2 // | / // 5 <- 4 <- 3 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 0).setDistance(1)); assertPath(calcPath(6, 0, 1, 6), 0.36, 6, 360, nodes(6, 1, 2, 3, 4, 5, 0)); } @@ -234,7 +235,7 @@ public void directedRouting() { // | / \ | // 8 = 7 6 = 5 EdgeIteratorState rightNorth, rightSouth, leftSouth, leftNorth; - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1), graph.edge(1, 2).setDistance(1), graph.edge(2, 3).setDistance(1), @@ -274,10 +275,10 @@ public void directedRouting() { public void enforceLoopEdge() { // o o // 0 - 1 - 2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 0).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 2).setDistance(1)); assertPath(calcPath(0, 2, ANY_EDGE, ANY_EDGE), 0.12, 2, 120, nodes(0, 1, 2)); assertPath(calcPath(0, 2, 1, 2), 0.12, 2, 120, nodes(0, 1, 2)); @@ -290,9 +291,9 @@ public void enforceLoopEdge() { @Test public void sourceAndTargetAreNeighbors() { // 0-1-2-3 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(100)); assertPath(calcPath(1, 2, ANY_EDGE, ANY_EDGE), 6, 100, 6000, nodes(1, 2)); assertPath(calcPath(1, 2, 1, ANY_EDGE), 6, 100, 6000, nodes(1, 2)); assertPath(calcPath(1, 2, ANY_EDGE, 1), 6, 100, 6000, nodes(1, 2)); @@ -315,13 +316,13 @@ public void worksWithTurnCosts() { // 0 - 1 - 2 // | | | // 3 - 4 - 5 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 2).setDistance(1)); setRestriction(0, 3, 4); setTurnCost(4, 5, 2, 6); @@ -344,17 +345,17 @@ public void finiteUTurnCosts() { // 0 -- 1 -- 6 // | | // 7 -- 8 -- 9 - int right0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)).getEdge(); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 2).setDistance(1000)); - int left6 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 6).setDistance(10)).getEdge(); - int left0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 7).setDistance(10)).getEdge(); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 8).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 9).setDistance(10)); - int right6 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(9, 6).setDistance(10)).getEdge(); + int right0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)).getEdge(); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 2).setDistance(1000)); + int left6 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 6).setDistance(10)).getEdge(); + int left0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 7).setDistance(10)).getEdge(); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 8).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 9).setDistance(10)); + int right6 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(9, 6).setDistance(10)).getEdge(); // enforce p-turn (using the loop in clockwise direction) setRestriction(0, 1, 6); @@ -391,8 +392,8 @@ private void compare_with_dijkstra(Weighting w) { Random rnd = new Random(seed); int numNodes = 100; GHUtility.buildRandomGraph(graph, rnd, numNodes, 2.2, true, true, - encoder.getAccessEnc(), encoder.getAverageSpeedEnc(), null, 0.7, 0.8, 0.8); - GHUtility.addRandomTurnCosts(graph, seed, encodingManager, encoder, maxTurnCosts, turnCostStorage); + accessEnc, speedEnc, null, 0.7, 0.8, 0.8); + GHUtility.addRandomTurnCosts(graph, seed, accessEnc, turnCostEnc, maxTurnCosts, turnCostStorage); long numStrictViolations = 0; for (int i = 0; i < numQueries; i++) { @@ -421,13 +422,13 @@ public void blockArea() { // 0 - 1 - 2 - 3 // | | // 4 --- 5 --- 6 - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 4).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 3).setDistance(100)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 4).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 3).setDistance(100)); // usually we would take the direct route assertPath(calcPath(0, 3, ANY_EDGE, ANY_EDGE), 1.8, 30, 1800, nodes(0, 1, 2, 3)); @@ -466,12 +467,12 @@ public void directedRouting_noUTurnAtVirtualEdge() { // 0 -- 1 -> 2 // | | // 5 <- 4 <- 3 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 0).setDistance(1)); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 1, 0); na.setNode(1, 1, 1); @@ -495,7 +496,7 @@ public void directedRouting_noUTurnAtVirtualEdge() { EdgeIteratorState virtualEdge = GHUtility.getEdge(queryGraph, 6, 1); int outEdge = virtualEdge.getEdge(); - BidirRoutingAlgorithm algo = createAlgo(queryGraph, weighting); + EdgeToEdgeRoutingAlgorithm algo = createAlgo(queryGraph, weighting); Path path = algo.calcPath(6, 0, outEdge, ANY_EDGE); assertEquals(nodes(6, 1, 2, 3, 4, 5, 0), path.calcNodes()); assertEquals(5 + virtualEdge.getDistance(), path.getDistance(), 1.e-3); @@ -506,11 +507,11 @@ private Path calcPath(int source, int target, int sourceOutEdge, int targetInEdg } private Path calcPath(int source, int target, int sourceOutEdge, int targetInEdge, Weighting w) { - BidirRoutingAlgorithm algo = createAlgo(graph, w); + EdgeToEdgeRoutingAlgorithm algo = createAlgo(graph, w); return algo.calcPath(source, target, sourceOutEdge, targetInEdge); } - private BidirRoutingAlgorithm createAlgo(Graph graph, Weighting weighting) { + private EdgeToEdgeRoutingAlgorithm createAlgo(Graph graph, Weighting weighting) { return new DijkstraBidirectionRef(graph, weighting, TraversalMode.EDGE_BASED); } diff --git a/core/src/test/java/com/graphhopper/routing/DirectedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/DirectedRoutingTest.java index bce784dfc89..7ba667267fb 100644 --- a/core/src/test/java/com/graphhopper/routing/DirectedRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/DirectedRoutingTest.java @@ -20,13 +20,17 @@ import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory; import com.graphhopper.routing.ch.PrepareContractionHierarchies; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.lm.LMConfig; import com.graphhopper.routing.lm.LMRoutingAlgorithmFactory; import com.graphhopper.routing.lm.LandmarkStorage; import com.graphhopper.routing.lm.PrepareLandmarks; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.querygraph.QueryRoutingCHGraph; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.subnetwork.PrepareRoutingSubnetworks; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; @@ -37,6 +41,8 @@ import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.GHUtility; import com.graphhopper.util.PMap; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -46,6 +52,7 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.stream.Stream; @@ -57,10 +64,11 @@ import static com.graphhopper.util.Parameters.Algorithms.ASTAR_BI; import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI; import static com.graphhopper.util.Parameters.Routing.ALGORITHM; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** - * This test makes sure the different bidirectional routing algorithms correctly implement restrictions of the source/ + * This test makes sure the different routing algorithms correctly implement restrictions of the source/ * target edges, by comparing with {@link DijkstraBidirectionRef} * * @author easbar @@ -77,12 +85,12 @@ private static class Fixture { private final boolean prepareLM; private final Directory dir; private final BaseGraph graph; - private final CHConfig chConfig; - private final LMConfig lmConfig; - private final FlagEncoder encoder; + private final BooleanEncodedValue accessEnc; + private final DecimalEncodedValue speedEnc; + private final DecimalEncodedValue turnCostEnc; private final TurnCostStorage turnCostStorage; private final int maxTurnCosts; - private final Weighting weighting; + private Weighting weighting; private final EncodingManager encodingManager; private RoutingCHGraph routingCHGraph; private LandmarkStorage lm; @@ -98,14 +106,12 @@ public Fixture(Algo algo, int uTurnCosts, boolean prepareCH, boolean prepareLM) // todo: this test only works with speedTwoDirections=false (as long as loops are enabled), otherwise it will // fail sometimes for edge-based algorithms, #1631, but maybe we can should disable different fwd/bwd speeds // only for loops instead? - encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", maxTurnCosts)); - encodingManager = EncodingManager.create(encoder); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + turnCostEnc = TurnCost.create("car", maxTurnCosts); + encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).add(Subnetwork.create("c2")).build(); graph = new BaseGraph.Builder(encodingManager).setDir(dir).withTurnCosts(true).create(); turnCostStorage = graph.getTurnCostStorage(); - weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, uTurnCosts)); - chConfig = CHConfig.edgeBased("p1", weighting); - // important: for LM preparation we need to use a weighting without turn costs #1960 - lmConfig = new LMConfig("c2", new FastestWeighting(encoder)); } @Override @@ -115,15 +121,25 @@ public String toString() { private void preProcessGraph() { graph.freeze(); + weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, turnCostStorage, uTurnCosts)); if (!prepareCH && !prepareLM) { return; } if (prepareCH) { + CHConfig chConfig = CHConfig.edgeBased("p1", weighting); PrepareContractionHierarchies pch = PrepareContractionHierarchies.fromGraph(graph, chConfig); PrepareContractionHierarchies.Result res = pch.doWork(); routingCHGraph = RoutingCHGraphImpl.fromGraph(graph, res.getCHStorage(), res.getCHConfig()); } if (prepareLM) { + // important: for LM preparation we need to use a weighting without turn costs #1960 + LMConfig lmConfig = new LMConfig("c2", new FastestWeighting(accessEnc, speedEnc)); + // we need the subnetwork EV for LM + PrepareRoutingSubnetworks preparation = new PrepareRoutingSubnetworks(graph, + Arrays.asList(new PrepareRoutingSubnetworks.PrepareJob(encodingManager.getBooleanEncodedValue(Subnetwork.key("c2")), lmConfig.getWeighting()))); + preparation.setMinNetworkSize(0); + preparation.doWork(); + PrepareLandmarks prepare = new PrepareLandmarks(dir, graph, encodingManager, lmConfig, 16); prepare.setMaximumWeight(1000); prepare.doWork(); @@ -131,13 +147,15 @@ private void preProcessGraph() { } } - private BidirRoutingAlgorithm createAlgo() { + private EdgeToEdgeRoutingAlgorithm createAlgo() { return createAlgo(graph); } - private BidirRoutingAlgorithm createAlgo(Graph graph) { + private EdgeToEdgeRoutingAlgorithm createAlgo(Graph graph) { switch (algo) { - case ASTAR: + case ASTAR_UNI_BEELINE: + return new AStar(graph, graph.wrapWeighting(weighting), TraversalMode.EDGE_BASED); + case ASTAR_BI_BEELINE: return new AStarBidirection(graph, graph.wrapWeighting(weighting), TraversalMode.EDGE_BASED); case CH_DIJKSTRA: { CHRoutingAlgorithmFactory algoFactory = graph instanceof QueryGraph @@ -152,7 +170,7 @@ private BidirRoutingAlgorithm createAlgo(Graph graph) { return algoFactory.createAlgo(new PMap().putObject(ALGORITHM, ASTAR_BI)); } case LM: - return (BidirRoutingAlgorithm) new LMRoutingAlgorithmFactory(lm).createAlgo(graph, weighting, new AlgorithmOptions().setAlgorithm(ASTAR_BI).setTraversalMode(TraversalMode.EDGE_BASED)); + return (EdgeToEdgeRoutingAlgorithm) new LMRoutingAlgorithmFactory(lm).createAlgo(graph, weighting, new AlgorithmOptions().setAlgorithm(ASTAR_BI).setTraversalMode(TraversalMode.EDGE_BASED)); default: throw new IllegalArgumentException("unknown algo " + algo); } @@ -167,12 +185,14 @@ private static class FixtureProvider implements ArgumentsProvider { @Override public Stream provideArguments(ExtensionContext context) { return Stream.of( - new Fixture(Algo.ASTAR, INFINITE_U_TURN_COSTS, false, false), + new Fixture(Algo.ASTAR_UNI_BEELINE, INFINITE_U_TURN_COSTS, false, false), + new Fixture(Algo.ASTAR_BI_BEELINE, INFINITE_U_TURN_COSTS, false, false), new Fixture(Algo.CH_ASTAR, INFINITE_U_TURN_COSTS, true, false), new Fixture(Algo.CH_DIJKSTRA, INFINITE_U_TURN_COSTS, true, false), // todo: LM+directed still fails sometimes, #1971, // new Fixture(Algo.LM, INFINITE_U_TURN_COSTS, false, true), - new Fixture(Algo.ASTAR, 40, false, false), + new Fixture(Algo.ASTAR_UNI_BEELINE, 40, false, false), + new Fixture(Algo.ASTAR_BI_BEELINE, 40, false, false), new Fixture(Algo.CH_ASTAR, 40, true, false), new Fixture(Algo.CH_DIJKSTRA, 40, true, false) // todo: LM+directed still fails sometimes, #1971, @@ -189,7 +209,8 @@ public Stream provideArguments(ExtensionContext context) { } private enum Algo { - ASTAR, + ASTAR_UNI_BEELINE, + ASTAR_BI_BEELINE, CH_ASTAR, CH_DIJKSTRA, LM @@ -202,8 +223,8 @@ public void randomGraph(Fixture f) { final int numQueries = 50; Random rnd = new Random(seed); GHUtility.buildRandomGraph(f.graph, rnd, 100, 2.2, true, true, - f.encoder.getAccessEnc(), f.encoder.getAverageSpeedEnc(), null, 0.7, 0.8, 0.8); - GHUtility.addRandomTurnCosts(f.graph, seed, f.encodingManager, f.encoder, f.maxTurnCosts, f.turnCostStorage); + f.accessEnc, f.speedEnc, null, 0.7, 0.8, 0.8); + GHUtility.addRandomTurnCosts(f.graph, seed, f.accessEnc, f.turnCostEnc, f.maxTurnCosts, f.turnCostStorage); // GHUtility.printGraphForUnitTest(f.graph, f.encoder); f.preProcessGraph(); List strictViolations = new ArrayList<>(); @@ -244,8 +265,8 @@ public void randomGraph_withQueryGraph(Fixture f) { double pOffset = 0; Random rnd = new Random(seed); GHUtility.buildRandomGraph(f.graph, rnd, 50, 2.2, true, true, - f.encoder.getAccessEnc(), f.encoder.getAverageSpeedEnc(), null, 0.7, 0.8, pOffset); - GHUtility.addRandomTurnCosts(f.graph, seed, f.encodingManager, f.encoder, f.maxTurnCosts, f.turnCostStorage); + f.accessEnc, f.speedEnc, null, 0.7, 0.8, pOffset); + GHUtility.addRandomTurnCosts(f.graph, seed, f.accessEnc, f.turnCostEnc, f.maxTurnCosts, f.turnCostStorage); // GHUtility.printGraphForUnitTest(graph, encoder); f.preProcessGraph(); LocationIndexTree index = new LocationIndexTree(f.graph, f.dir); @@ -279,6 +300,52 @@ public void randomGraph_withQueryGraph(Fixture f) { } } + @Disabled("todo: fix this, #1971") + @Test + public void issue_2581() { + Fixture f = new Fixture(Algo.LM, 40, false, true); + // this test failed with 'forward and backward entries must have same adjacent nodes' before #2581 was fixed. + // but it still fails with a wrong shortest path weight, probably because of #1971. + NodeAccess na = f.graph.getNodeAccess(); + na.setNode(0, 49.406624, 9.703301); + na.setNode(1, 49.404040, 9.704504); + na.setNode(2, 49.407601, 9.700407); + na.setNode(3, 49.406038, 9.700309); + na.setNode(4, 49.400086, 9.705911); + na.setNode(5, 49.405893, 9.704811); + na.setNode(6, 49.409435, 9.701510); + na.setNode(7, 49.407531, 9.701966); + // 3-0=1-2=7-5 + // | + // 4 + BooleanEncodedValue accessEnc = f.accessEnc; + DecimalEncodedValue speedEnc = f.speedEnc; + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, f.graph.edge(0, 1).setDistance(300.186000)); // edgeId=0 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, f.graph.edge(0, 4).setDistance(751.113000)); // edgeId=1 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, f.graph.edge(7, 2).setDistance(113.102000)); // edgeId=2 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, f.graph.edge(3, 0).setDistance(226.030000)); // edgeId=3 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, f.graph.edge(1, 2).setDistance(494.601000)); // edgeId=4 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, f.graph.edge(7, 2).setDistance(113.102000)); // edgeId=5 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, f.graph.edge(5, 7).setDistance(274.848000)); // edgeId=6 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, f.graph.edge(0, 1).setDistance(300.186000)); // edgeId=7 + f.preProcessGraph(); + LocationIndexTree index = new LocationIndexTree(f.graph, f.dir); + index.prepareIndex(); + Snap snap1 = index.findClosest(49.40513869516064, 9.703482698430037, EdgeFilter.ALL_EDGES); + Snap snap2 = index.findClosest(49.40650971100665, 9.704468799032508, EdgeFilter.ALL_EDGES); + List snaps = Arrays.asList(snap1, snap2); + QueryGraph queryGraph = QueryGraph.create(f.graph, snaps); + int source = snaps.get(0).getClosestNode(); + int target = snaps.get(1).getClosestNode(); + int sourceOutEdge = 8; + int targetInEdge = 11; + Path refPath = new DijkstraBidirectionRef(queryGraph, ((Graph) queryGraph).wrapWeighting(f.weighting), TraversalMode.EDGE_BASED) + .calcPath(source, target, sourceOutEdge, targetInEdge); + Path path = f.createAlgo(queryGraph) + .calcPath(source, target, sourceOutEdge, targetInEdge); + assertTrue(comparePaths(refPath, path, source, target, false, -1).isEmpty()); + } + private List comparePaths(Path refPath, Path path, int source, int target, boolean checkNodes, long seed) { List strictViolations = new ArrayList<>(); double refWeight = refPath.getWeight(); diff --git a/core/src/test/java/com/graphhopper/routing/DirectionResolverOnQueryGraphTest.java b/core/src/test/java/com/graphhopper/routing/DirectionResolverOnQueryGraphTest.java index 372ce470e91..7d35788bc2f 100644 --- a/core/src/test/java/com/graphhopper/routing/DirectionResolverOnQueryGraphTest.java +++ b/core/src/test/java/com/graphhopper/routing/DirectionResolverOnQueryGraphTest.java @@ -19,8 +19,13 @@ import com.carrotsearch.hppc.IntArrayList; import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.querygraph.QueryGraph; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.RAMDirectory; @@ -48,14 +53,17 @@ public class DirectionResolverOnQueryGraphTest { private QueryGraph queryGraph; private NodeAccess na; - private FlagEncoder encoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; private BaseGraph graph; private LocationIndexTree locationIndex; @BeforeEach public void setup() { - encoder = FlagEncoders.createCar(); - graph = new BaseGraph.Builder(EncodingManager.create(encoder)).create(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + graph = new BaseGraph.Builder(em).create(); na = graph.getNodeAccess(); } @@ -294,7 +302,7 @@ private void addNode(int nodeId, double lat, double lon) { } private EdgeIteratorState addEdge(int from, int to, boolean bothDirections) { - return GHUtility.setSpeed(60, true, bothDirections, encoder, graph.edge(from, to).setDistance(1)); + return GHUtility.setSpeed(60, true, bothDirections, accessEnc, speedEnc, graph.edge(from, to).setDistance(1)); } private void init() { @@ -349,7 +357,7 @@ private DirectionResolverResult restrictedDirection(ExpectedResult restriction) } private int findEdge(int from, int to) { - EdgeExplorer explorer = queryGraph.createEdgeExplorer(AccessFilter.outEdges(encoder.getAccessEnc())); + EdgeExplorer explorer = queryGraph.createEdgeExplorer(AccessFilter.outEdges(accessEnc)); EdgeIterator iter = explorer.setBaseNode(from); while (iter.next()) { if (iter.getAdjNode() == to) { @@ -360,7 +368,6 @@ private int findEdge(int from, int to) { } private boolean isAccessible(EdgeIteratorState edge, boolean reverse) { - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); return reverse ? edge.getReverse(accessEnc) : edge.get(accessEnc); } diff --git a/core/src/test/java/com/graphhopper/routing/DirectionResolverTest.java b/core/src/test/java/com/graphhopper/routing/DirectionResolverTest.java index 1510d7f2cc1..9149e5cf5d7 100644 --- a/core/src/test/java/com/graphhopper/routing/DirectionResolverTest.java +++ b/core/src/test/java/com/graphhopper/routing/DirectionResolverTest.java @@ -18,11 +18,12 @@ package com.graphhopper.routing; import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.AccessFilter; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.util.EdgeExplorer; @@ -43,14 +44,17 @@ * @see DirectionResolverOnQueryGraphTest for tests that include direction resolving for virtual nodes and edges */ public class DirectionResolverTest { - private FlagEncoder encoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; private BaseGraph graph; private NodeAccess na; @BeforeEach public void setup() { - encoder = FlagEncoders.createCar(); - graph = new BaseGraph.Builder(EncodingManager.create(encoder)).create(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + graph = new BaseGraph.Builder(em).create(); na = graph.getNodeAccess(); } @@ -70,7 +74,7 @@ public void isolated_nodes_blocked_edge() { addNode(0, 0, 0); addNode(1, 0.1, 0.1); // with edges without access flags (blocked edges) - graph.edge(0, 1).set(encoder.getAccessEnc(), false, false); + graph.edge(0, 1).set(accessEnc, false, false); checkResult(0, impossible()); checkResult(1, impossible()); @@ -382,11 +386,10 @@ private void addNode(int nodeId, double lat, double lon) { } private EdgeIteratorState addEdge(int from, int to, boolean bothDirections) { - return GHUtility.setSpeed(60, true, bothDirections, encoder, graph.edge(from, to).setDistance(1)); + return GHUtility.setSpeed(60, true, bothDirections, accessEnc, speedEnc, graph.edge(from, to).setDistance(1)); } private boolean isAccessible(EdgeIteratorState edge, boolean reverse) { - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); return reverse ? edge.getReverse(accessEnc) : edge.get(accessEnc); } @@ -400,7 +403,7 @@ private void checkResult(int node, double lat, double lon, DirectionResolverResu } private int edge(int from, int to) { - EdgeExplorer explorer = graph.createEdgeExplorer(AccessFilter.outEdges(encoder.getAccessEnc())); + EdgeExplorer explorer = graph.createEdgeExplorer(AccessFilter.outEdges(accessEnc)); EdgeIterator iter = explorer.setBaseNode(from); while (iter.next()) { if (iter.getAdjNode() == to) { diff --git a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java index c3b4f59cbd6..104586a4d04 100644 --- a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java +++ b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java @@ -19,11 +19,8 @@ import com.carrotsearch.hppc.IntArrayList; import com.carrotsearch.hppc.cursors.IntCursor; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; @@ -33,7 +30,6 @@ import com.graphhopper.storage.TurnCostStorage; import com.graphhopper.util.GHUtility; import com.graphhopper.util.Helper; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -57,7 +53,8 @@ */ public class EdgeBasedRoutingAlgorithmTest { private static final Logger LOGGER = LoggerFactory.getLogger(EdgeBasedRoutingAlgorithmTest.class); - private FlagEncoder carEncoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; private DecimalEncodedValue turnCostEnc; private TurnCostStorage tcs; @@ -82,23 +79,23 @@ public Stream provideArguments(ExtensionContext context) { // | | | // 5--6--7 private void initGraph(Graph graph) { - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(0, 1).setDistance(3)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(0, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(2, 5).setDistance(0.5)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(4, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 5).setDistance(0.5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(1)); } private EncodingManager createEncodingManager(boolean restrictedOnly) { - carEncoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", restrictedOnly ? 1 : 3)); - EncodingManager em = EncodingManager.create(carEncoder); - turnCostEnc = getTurnCostEnc(carEncoder); - return em; + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + turnCostEnc = TurnCost.create("car", restrictedOnly ? 1 : 3); + return EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); } public Path calcPath(Graph g, int from, int to, String algoStr) { @@ -113,15 +110,11 @@ public RoutingAlgorithm createAlgo(Graph g, Weighting weighting, String algoStr, } private BaseGraph createStorage(EncodingManager em) { - BaseGraph graph = new BaseGraph.Builder(em).create(); + BaseGraph graph = new BaseGraph.Builder(em).withTurnCosts(true).create(); tcs = graph.getTurnCostStorage(); return graph; } - private DecimalEncodedValue getTurnCostEnc(FlagEncoder encoder) { - return encoder.getDecimalEncodedValue(TurnCost.key(encoder.toString())); - } - private void initTurnRestrictions(BaseGraph g) { // only forward from 2-3 to 3-4 => limit 2,3->3,6 and 2,3->3,1 setTurnRestriction(g, 2, 3, 6); @@ -152,7 +145,7 @@ private Weighting createWeighting() { } private Weighting createWeighting(int uTurnCosts) { - return new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, tcs, uTurnCosts)); + return new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, tcs, uTurnCosts)); } @ParameterizedTest @@ -164,8 +157,8 @@ public void testRandomGraph(String algoStr) { EncodingManager em = createEncodingManager(false); BaseGraph g = createStorage(em); GHUtility.buildRandomGraph(g, rnd, 50, 2.2, true, true, - carEncoder.getAccessEnc(), carEncoder.getAverageSpeedEnc(), null, 0.8, 0.8, 0.8); - GHUtility.addRandomTurnCosts(g, seed, em, carEncoder, 3, tcs); + accessEnc, speedEnc, null, 0.8, 0.8, 0.8); + GHUtility.addRandomTurnCosts(g, seed, accessEnc, turnCostEnc, 3, tcs); g.freeze(); int numPathsNotFound = 0; // todo: reduce redundancy with RandomCHRoutingTest @@ -235,12 +228,12 @@ public void testLoop_issue1592(String algoStr) { // 4-3 // | // 1o - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(0, 6).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(6, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(0, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(4, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(4, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 1).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 6).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 1).setDistance(10)); setTurnRestriction(graph, 0, 4, 3); Path p = calcPath(graph, 0, 3, algoStr); @@ -255,10 +248,10 @@ public void testTurnCosts_timeCalculation(String algoStr) { BaseGraph graph = createStorage(createEncodingManager(false)); final int distance = 100; final int turnCosts = 2; - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(0, 1).setDistance(distance)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 2).setDistance(distance)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(2, 3).setDistance(distance)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 4).setDistance(distance)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(distance)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(distance)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(distance)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(distance)); setTurnCost(graph, turnCosts, 1, 2, 3); { @@ -286,7 +279,7 @@ private void assertDistTimeWeight(Path path, int numEdges, double distPerEdge, d } private void blockNode3(BaseGraph g) { - // Totally block this node (all 9 turn relations) + // Totally block this node (all 9 turn restrictions) setTurnRestriction(g, 2, 3, 1); setTurnRestriction(g, 2, 3, 4); setTurnRestriction(g, 4, 3, 1); @@ -359,11 +352,11 @@ public void uTurnCostAtMeetingNode(String algoStr) { // | // 0 -> 1 -> 2 -> 4 -> 5 BaseGraph g = createStorage(createEncodingManager(false)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(0, 1).setDistance(10)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(1, 2).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 3).setDistance(10)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(2, 4).setDistance(10)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(4, 5).setDistance(10)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(10)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(2, 4).setDistance(10)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(4, 5).setDistance(10)); // cannot go straight at node 2 setTurnRestriction(g, 1, 2, 4); @@ -413,7 +406,7 @@ public void testTurnCostsBug_991(String algoStr) { setTurnCost(g, 2, 5, 6, 3); setTurnCost(g, 1, 6, 7, 4); - FastestWeighting weighting = new FastestWeighting(carEncoder, new DefaultTurnCostProvider(carEncoder, tcs) { + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, tcs) { @Override public double calcTurnWeight(int edgeFrom, int nodeVia, int edgeTo) { if (edgeFrom >= 0) @@ -437,11 +430,11 @@ public void testLoopEdge(String algoStr) { // \| // 0 final BaseGraph graph = createStorage(createEncodingManager(false)); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(3, 2).setDistance(188)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 0).setDistance(182)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(4, 2).setDistance(690)); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(2, 2).setDistance(121)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(2, 0).setDistance(132)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(188)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 0).setDistance(182)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 2).setDistance(690)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 2).setDistance(121)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 0).setDistance(132)); setTurnRestriction(graph, 2, 2, 0); setTurnRestriction(graph, 3, 2, 4); @@ -460,12 +453,12 @@ public void testDoubleLoopPTurn(String algoStr) { // | // 5 final BaseGraph graph = createStorage(createEncodingManager(false)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(4, 4).setDistance(4)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 4).setDistance(5)); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(5, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 4).setDistance(4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 4).setDistance(1)); setTurnRestriction(graph, 1, 4, 5); Path p = calcPath(graph, 0, 5, algoStr); diff --git a/core/src/test/java/com/graphhopper/routing/HeadingResolverTest.java b/core/src/test/java/com/graphhopper/routing/HeadingResolverTest.java index 1814a084aa1..8689b05e2a6 100644 --- a/core/src/test/java/com/graphhopper/routing/HeadingResolverTest.java +++ b/core/src/test/java/com/graphhopper/routing/HeadingResolverTest.java @@ -19,10 +19,12 @@ package com.graphhopper.routing; import com.carrotsearch.hppc.IntArrayList; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.index.Snap; @@ -43,8 +45,9 @@ public void straightEdges() { // 7 -- 8 --- 3 // /|\ // 6 5 4 - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 49.5073, 1.5545); @@ -57,14 +60,14 @@ public void straightEdges() { na.setNode(7, 48.8611, 1.2194); na.setNode(8, 48.8538, 2.3950); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 0).setDistance(10)); // edge 0 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 1).setDistance(10)); // edge 1 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 2).setDistance(10)); // edge 2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 3).setDistance(10)); // edge 3 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 4).setDistance(10)); // edge 4 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 5).setDistance(10)); // edge 5 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 6).setDistance(10)); // edge 6 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 7).setDistance(10)); // edge 7 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 0).setDistance(10)); // edge 0 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 1).setDistance(10)); // edge 1 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 2).setDistance(10)); // edge 2 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 3).setDistance(10)); // edge 3 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 4).setDistance(10)); // edge 4 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 5).setDistance(10)); // edge 5 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 6).setDistance(10)); // edge 6 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 7).setDistance(10)); // edge 7 HeadingResolver resolver = new HeadingResolver(graph); // using default tolerance @@ -85,16 +88,17 @@ public void curvyEdge() { // 1 -| // |- 0 -| // |- 2 - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); NodeAccess na = graph.getNodeAccess(); na.setNode(1, 0.01, 0.00); na.setNode(0, 0.00, 0.00); na.setNode(2, -0.01, 0.00); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)). + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)). setWayGeometry(Helper.createPointList(0.00, 0.01, 0.01, 0.01)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2).setDistance(10)). + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2).setDistance(10)). setWayGeometry(Helper.createPointList(0.00, -0.01, -0.01, -0.01)); HeadingResolver resolver = new HeadingResolver(graph); resolver.setTolerance(120); @@ -108,14 +112,15 @@ public void curvyEdge() { public void withQueryGraph() { // 2 // 0 -x- 1 - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 48.8611, 1.2194); na.setNode(1, 48.8538, 2.3950); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); Snap snap = createSnap(edge, 48.859, 2.00, 0); QueryGraph queryGraph = QueryGraph.create(graph, snap); HeadingResolver resolver = new HeadingResolver(queryGraph); diff --git a/core/src/test/java/com/graphhopper/routing/HeadingRoutingTest.java b/core/src/test/java/com/graphhopper/routing/HeadingRoutingTest.java index b04a398ae61..607d10bfe0f 100644 --- a/core/src/test/java/com/graphhopper/routing/HeadingRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/HeadingRoutingTest.java @@ -22,10 +22,8 @@ import com.graphhopper.GHResponse; import com.graphhopper.ResponsePath; import com.graphhopper.config.Profile; -import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; import com.graphhopper.storage.NodeAccess; @@ -56,9 +54,10 @@ class HeadingRoutingTest { @Test public void headingTest1() { // Test enforce start direction - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(Subnetwork.create("profile")).build(); - BaseGraph graph = createSquareGraph(encodingManager); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start in middle of edge 4-5 @@ -79,9 +78,10 @@ public void headingTest1() { @Test public void headingTest2() { // Test enforce south start direction and east end direction - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(Subnetwork.create("profile")).build(); - BaseGraph graph = createSquareGraph(encodingManager); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start in middle of edge 4-5 @@ -106,9 +106,10 @@ public void headingTest2() { @Test public void headingTest3() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(Subnetwork.create("profile")).build(); - BaseGraph graph = createSquareGraph(encodingManager); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start in middle of edge 4-5 @@ -125,15 +126,16 @@ public void headingTest3() { setPathDetails(Collections.singletonList("edge_key")); GHResponse response = router.route(req); assertFalse(response.hasErrors()); - assertArrayEquals(new int[]{4, 5, 6, 7, 8, 3, 2}, calcNodes(graph, response.getAll().get(0))); + assertArrayEquals(new int[]{4, 5, 6, 7, 7, 8, 3, 2}, calcNodes(graph, response.getAll().get(0))); } @Test public void headingTest4() { // Test straight via routing - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(Subnetwork.create("profile")).build(); - BaseGraph graph = createSquareGraph(encodingManager); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start in middle of edge 4-5 @@ -150,23 +152,24 @@ public void headingTest4() { GHResponse response = router.route(req); assertFalse(response.hasErrors()); assertEquals(1, response.getAll().size()); - assertArrayEquals(new int[]{5, 4, 3, 8, 1, 2, 3}, calcNodes(graph, response.getAll().get(0))); + assertArrayEquals(new int[]{5, 4, 3, 3, 8, 1, 2, 3}, calcNodes(graph, response.getAll().get(0))); } @Test public void headingTest5() { // Test independence of previous enforcement for subsequent paths - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(Subnetwork.create("profile")).build(); - BaseGraph graph = createSquareGraph(encodingManager); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start in middle of edge 4-5 GHPoint start = new GHPoint(0.0015, 0.002); + // First go south and then come from west to via-point at 7-6. Then go back over previously punished (11)-4 edge + GHPoint via = new GHPoint(0.000, 0.0015); // End at middle of edge 2-3 GHPoint end = new GHPoint(0.002, 0.0005); - // First go south and than come from west to via-point at 7-6. Then go back over previously punished (11)-4 edge - GHPoint via = new GHPoint(0.000, 0.0015); GHRequest req = new GHRequest(). setPoints(Arrays.asList(start, via, end)). setHeadings(Arrays.asList(0., 90., Double.NaN)). @@ -175,14 +178,15 @@ public void headingTest5() { req.putHint(Parameters.Routing.PASS_THROUGH, true); GHResponse response = router.route(req); assertFalse(response.hasErrors()); - assertArrayEquals(new int[]{5, 4, 3, 8, 7, 6, 5, 4, 3, 2}, calcNodes(graph, response.getAll().get(0))); + assertArrayEquals(new int[]{5, 4, 3, 8, 7, 7, 6, 5, 4, 3, 2}, calcNodes(graph, response.getBest())); } @Test public void testHeadingWithSnapFilter() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(Subnetwork.create("profile")).build(); - BaseGraph graph = createSquareGraphWithTunnel(encodingManager); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + BaseGraph graph = createSquareGraphWithTunnel(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start at 8 (slightly north to make it independent on some edge ordering and always use 8-3 or 3-8 as fallback) GHPoint start = new GHPoint(0.0011, 0.001); @@ -242,9 +246,10 @@ public void testHeadingWithSnapFilter() { @Test public void testHeadingWithSnapFilter2() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(Subnetwork.create("profile")).build(); - BaseGraph graph = createSquareGraphWithTunnel(encodingManager); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + BaseGraph graph = createSquareGraphWithTunnel(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // Start at 8 (slightly east to snap to edge 1->5 per default) GHPoint start = new GHPoint(0.001, 0.0011); @@ -275,9 +280,10 @@ public void testHeadingWithSnapFilter2() { @Test public void headingTest6() { // Test if snaps at tower nodes are ignored - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(Subnetwork.create("profile")).build(); - BaseGraph graph = createSquareGraph(encodingManager); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("profile")).build(); + BaseGraph graph = createSquareGraph(encodingManager, accessEnc, speedEnc); Router router = createRouter(graph, encodingManager); // QueryPoints directly on TowerNodes @@ -304,10 +310,8 @@ private Router createRouter(BaseGraph graph, EncodingManager encodingManager) { new DefaultWeightingFactory(graph.getBaseGraph(), encodingManager), Collections.emptyMap(), Collections.emptyMap()); } - private BaseGraph createSquareGraph(EncodingManager encodingManager) { + private BaseGraph createSquareGraph(EncodingManager encodingManager, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - FlagEncoder carEncoder = encodingManager.getEncoder("car"); - // 2---3---4 // | | | // 1---8---5 @@ -324,27 +328,25 @@ private BaseGraph createSquareGraph(EncodingManager encodingManager) { na.setNode(7, 0.000, 0.001); na.setNode(8, 0.001, 0.001); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 1).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 2).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 3).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 4).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(4, 5).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(5, 6).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(6, 7).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(7, 0).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 6).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(6, 7).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 0).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 8).setDistance(110)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 8).setDistance(110)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(5, 8).setDistance(110)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(7, 8).setDistance(110)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 8).setDistance(110)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 8).setDistance(110)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 8).setDistance(110)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 8).setDistance(110)); return g; } - private BaseGraph createSquareGraphWithTunnel(EncodingManager encodingManager) { + private BaseGraph createSquareGraphWithTunnel(EncodingManager encodingManager, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - FlagEncoder carEncoder = encodingManager.getEncoder("car"); - // 2----3---4 // | | | // 1->- 8 >-5 (edge 1->5 is not connected to 8) @@ -361,18 +363,18 @@ private BaseGraph createSquareGraphWithTunnel(EncodingManager encodingManager) { na.setNode(7, 0.000, 0.001); na.setNode(8, 0.001, 0.001); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 1).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 2).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 3).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 4).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(4, 5).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(5, 6).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(6, 7).setDistance(100)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(7, 0).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 6).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(6, 7).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 0).setDistance(100)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(1, 5).setDistance(110)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 8).setDistance(110)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(7, 8).setDistance(110)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(1, 5).setDistance(110)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 8).setDistance(110)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 8).setDistance(110)); return g; } @@ -381,12 +383,10 @@ private int[] calcNodes(Graph graph, ResponsePath responsePath) { List edgeKeys = responsePath.getPathDetails().get("edge_key"); int[] result = new int[edgeKeys.size() + 1]; for (int i = 0; i < edgeKeys.size(); i++) { - int edgeKey = (int) edgeKeys.get(i).getValue(); - int edgeId = edgeKey / 2; - EdgeIteratorState edgeIteratorState = graph.getEdgeIteratorState(edgeId, Integer.MIN_VALUE); - result[i] = edgeKey % 2 == 0 ? edgeIteratorState.getBaseNode() : edgeIteratorState.getAdjNode(); - if (i == edgeKeys.size() - 1) - result[edgeKeys.size()] = edgeKey % 2 == 0 ? edgeIteratorState.getAdjNode() : edgeIteratorState.getBaseNode(); + EdgeIteratorState edgeIteratorState = graph.getEdgeIteratorStateForKey((int) edgeKeys.get(i).getValue()); + result[i] = edgeIteratorState.getBaseNode(); + // last entry needs an additional node: + if (i == edgeKeys.size() - 1) result[edgeKeys.size()] = edgeIteratorState.getAdjNode(); } return result; } diff --git a/core/src/test/java/com/graphhopper/routing/PathTest.java b/core/src/test/java/com/graphhopper/routing/PathTest.java index 86951d568b3..1afde7dd194 100644 --- a/core/src/test/java/com/graphhopper/routing/PathTest.java +++ b/core/src/test/java/com/graphhopper/routing/PathTest.java @@ -19,8 +19,6 @@ import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.ShortestWeighting; @@ -36,6 +34,8 @@ import java.util.*; +import static com.graphhopper.search.KVStorage.KeyValue.STREET_NAME; +import static com.graphhopper.search.KVStorage.KeyValue.createKV; import static com.graphhopper.storage.AbstractGraphStorageTester.assertPList; import static com.graphhopper.util.Parameters.Details.*; import static org.junit.jupiter.api.Assertions.*; @@ -44,14 +44,17 @@ * @author Peter Karich */ public class PathTest { - private final FlagEncoder encoder = FlagEncoders.createCar(); - private final EncodingManager carManager = EncodingManager.create(encoder); - private final BooleanEncodedValue carAccessEnc = encoder.getAccessEnc(); - private final DecimalEncodedValue carAvSpeedEnv = encoder.getAverageSpeedEnc(); - private final EncodingManager mixedEncoders = EncodingManager.create(FlagEncoders.createCar(), FlagEncoders.createFoot()); + private final BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue carAvSpeedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final EncodingManager carManager = EncodingManager.start().add(carAccessEnc).add(carAvSpeedEnc).build(); + private final BooleanEncodedValue mixedCarAccessEnc = new SimpleBooleanEncodedValue("mixed_car_access", true); + private final DecimalEncodedValue mixedCarSpeedEnc = new DecimalEncodedValueImpl("mixed_car_speed", 5, 5, false); + private final BooleanEncodedValue mixedFootAccessEnc = new SimpleBooleanEncodedValue("mixed_foot_access", true); + private final DecimalEncodedValue mixedFootSpeedEnc = new DecimalEncodedValueImpl("mixed_foot_speed", 4, 1, false); + private final EncodingManager mixedEncodingManager = EncodingManager.start().add(mixedCarAccessEnc).add(mixedCarSpeedEnc).add(mixedFootAccessEnc).add(mixedFootSpeedEnc).build(); private final TranslationMap trMap = TranslationMapTest.SINGLETON; private final Translation tr = trMap.getWithFallBack(Locale.US); - private final RoundaboutGraph roundaboutGraph = new RoundaboutGraph(mixedEncoders); + private final RoundaboutGraph roundaboutGraph = new RoundaboutGraph(); private final Graph pathDetailGraph = generatePathDetailsGraph(); @Test @@ -71,14 +74,14 @@ public void testWayList() { na.setNode(1, 1.0, 0.1); na.setNode(2, 2.0, 0.1); - EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 10.0); + EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 10.0); edge1.setWayGeometry(Helper.createPointList(8, 1, 9, 1)); - EdgeIteratorState edge2 = g.edge(2, 1).setDistance(2000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 50.0); + EdgeIteratorState edge2 = g.edge(2, 1).setDistance(2000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); edge2.setWayGeometry(Helper.createPointList(11, 1, 10, 1)); SPTEntry e1 = new SPTEntry(edge2.getEdge(), 2, 1, new SPTEntry(edge1.getEdge(), 1, 1, new SPTEntry(0, 1))); - FastestWeighting weighting = new FastestWeighting(encoder); + FastestWeighting weighting = new FastestWeighting(carAccessEnc, carAvSpeedEnc); Path path = extractPath(g, weighting, e1); // 0-1-2 assertPList(Helper.createPointList(0, 0.1, 8, 1, 9, 1, 1, 0.1, 10, 1, 11, 1, 2, 0.1), path.calcPoints()); @@ -102,9 +105,9 @@ public void testWayList() { assertEquals(path.calcPoints().size() - 1, acc); // force minor change for instructions - edge2.setName("2"); + edge2.setKeyValues(createKV(STREET_NAME, "2")); na.setNode(3, 1.0, 1.0); - g.edge(1, 3).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 10.0); + g.edge(1, 3).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 10.0); e1 = new SPTEntry(edge2.getEdge(), 2, 1, new SPTEntry(edge1.getEdge(), 1, 1, @@ -167,22 +170,22 @@ public void testFindInstruction() { na.setNode(4, 7.5, 0.25); na.setNode(5, 5.0, 1.0); - EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 50.0); + EdgeIteratorState edge1 = g.edge(0, 1).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); edge1.setWayGeometry(Helper.createPointList()); - edge1.setName("Street 1"); - EdgeIteratorState edge2 = g.edge(1, 2).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 50.0); + edge1.setKeyValues(createKV(STREET_NAME, "Street 1")); + EdgeIteratorState edge2 = g.edge(1, 2).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); edge2.setWayGeometry(Helper.createPointList()); - edge2.setName("Street 2"); - EdgeIteratorState edge3 = g.edge(2, 3).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 50.0); + edge2.setKeyValues(createKV(STREET_NAME, "Street 2")); + EdgeIteratorState edge3 = g.edge(2, 3).setDistance(1000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); edge3.setWayGeometry(Helper.createPointList()); - edge3.setName("Street 3"); - EdgeIteratorState edge4 = g.edge(3, 4).setDistance(500).set(carAccessEnc, true, true).set(carAvSpeedEnv, 50.0); + edge3.setKeyValues(createKV(STREET_NAME, "Street 3")); + EdgeIteratorState edge4 = g.edge(3, 4).setDistance(500).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); edge4.setWayGeometry(Helper.createPointList()); - edge4.setName("Street 4"); + edge4.setKeyValues(createKV(STREET_NAME, "Street 4")); - g.edge(1, 5).setDistance(10000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 50.0); - g.edge(2, 5).setDistance(10000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 50.0); - g.edge(3, 5).setDistance(100000).set(carAccessEnc, true, true).set(carAvSpeedEnv, 50.0); + g.edge(1, 5).setDistance(10000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); + g.edge(2, 5).setDistance(10000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); + g.edge(3, 5).setDistance(100000).set(carAccessEnc, true, true).set(carAvSpeedEnc, 50.0); SPTEntry e1 = new SPTEntry(edge4.getEdge(), 4, 1, @@ -191,7 +194,7 @@ public void testFindInstruction() { new SPTEntry(edge1.getEdge(), 1, 1, new SPTEntry(0, 1) )))); - FastestWeighting weighting = new FastestWeighting(encoder); + FastestWeighting weighting = new FastestWeighting(carAccessEnc, carAvSpeedEnc); Path path = extractPath(g, weighting, e1); InstructionList il = InstructionsFromEdges.calcInstructions(path, path.graph, weighting, carManager, tr); @@ -207,48 +210,51 @@ public void testFindInstruction() { * Test roundabout instructions for different profiles */ @Test - public void testCalcInstructionsRoundabout() { - for (FlagEncoder encoder : mixedEncoders.fetchEdgeEncoders()) { - ShortestWeighting weighting = new ShortestWeighting(encoder); - Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) - .calcPath(1, 8); - assertTrue(p.isFound()); - assertEquals("[1, 2, 3, 4, 5, 8]", p.calcNodes().toString()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); - // Test instructions - List tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("continue onto MainStreet 1 2", - "At roundabout, take exit 3 onto 5-8", - "arrive at destination"), - tmpList); - // Test Radian - double delta = roundaboutGraph.getAngle(1, 2, 5, 8); - RoundaboutInstruction instr = (RoundaboutInstruction) wayList.get(1); - assertEquals(delta, instr.getTurnAngle(), 0.01); - - // case of continuing a street through a roundabout - p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED). - calcPath(1, 7); - wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); - tmpList = getTurnDescriptions(wayList); - assertEquals(Arrays.asList("continue onto MainStreet 1 2", - "At roundabout, take exit 2 onto MainStreet 4 7", - "arrive at destination"), - tmpList); - // Test Radian - delta = roundaboutGraph.getAngle(1, 2, 4, 7); - instr = (RoundaboutInstruction) wayList.get(1); - assertEquals(delta, instr.getTurnAngle(), 0.01); - } + void testCalcInstructionsRoundabout() { + calcInstructionsRoundabout(mixedCarAccessEnc, mixedCarSpeedEnc); + calcInstructionsRoundabout(mixedFootAccessEnc, mixedFootSpeedEnc); + } + + public void calcInstructionsRoundabout(BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + ShortestWeighting weighting = new ShortestWeighting(accessEnc, speedEnc); + Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) + .calcPath(1, 8); + assertTrue(p.isFound()); + assertEquals("[1, 2, 3, 4, 5, 8]", p.calcNodes().toString()); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); + // Test instructions + List tmpList = getTurnDescriptions(wayList); + assertEquals(Arrays.asList("continue onto MainStreet 1 2", + "At roundabout, take exit 3 onto 5-8", + "arrive at destination"), + tmpList); + // Test Radian + double delta = roundaboutGraph.getAngle(1, 2, 5, 8); + RoundaboutInstruction instr = (RoundaboutInstruction) wayList.get(1); + assertEquals(delta, instr.getTurnAngle(), 0.01); + + // case of continuing a street through a roundabout + p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED). + calcPath(1, 7); + wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); + tmpList = getTurnDescriptions(wayList); + assertEquals(Arrays.asList("continue onto MainStreet 1 2", + "At roundabout, take exit 2 onto MainStreet 4 7", + "arrive at destination"), + tmpList); + // Test Radian + delta = roundaboutGraph.getAngle(1, 2, 4, 7); + instr = (RoundaboutInstruction) wayList.get(1); + assertEquals(delta, instr.getTurnAngle(), 0.01); } @Test public void testCalcInstructionsRoundaboutBegin() { - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(2, 8); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); List tmpList = getTurnDescriptions(wayList); assertEquals(Arrays.asList("At roundabout, take exit 3 onto 5-8", "arrive at destination"), @@ -258,11 +264,11 @@ public void testCalcInstructionsRoundaboutBegin() { @Test public void testCalcInstructionsRoundaboutDirectExit() { roundaboutGraph.inverse3to9(); - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(6, 8); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); List tmpList = getTurnDescriptions(wayList); assertEquals(Arrays.asList("continue onto 3-6", "At roundabout, take exit 3 onto 5-8", @@ -273,12 +279,12 @@ public void testCalcInstructionsRoundaboutDirectExit() { @Test public void testCalcAverageSpeedDetails() { - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0); + Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); assertTrue(details.size() == 1); List averageSpeedDetails = details.get(AVERAGE_SPEED); @@ -297,11 +303,11 @@ public void testCalcAverageSpeedDetails() { @Test public void testCalcAverageSpeedDetailsWithShortDistances_issue1848() { - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 6); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0); + Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); assertTrue(details.size() == 1); List averageSpeedDetails = details.get(AVERAGE_SPEED); assertEquals(4, averageSpeedDetails.size()); @@ -310,7 +316,7 @@ public void testCalcAverageSpeedDetailsWithShortDistances_issue1848() { p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(6, 1); assertTrue(p.isFound()); details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0); + Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, pathDetailGraph); assertTrue(details.size() == 1); averageSpeedDetails = details.get(AVERAGE_SPEED); assertEquals(5, averageSpeedDetails.size()); @@ -319,12 +325,12 @@ public void testCalcAverageSpeedDetailsWithShortDistances_issue1848() { @Test public void testCalcStreetNameDetails() { - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(STREET_NAME), new PathDetailsBuilderFactory(), 0); + Arrays.asList(STREET_NAME), new PathDetailsBuilderFactory(), 0, pathDetailGraph); assertTrue(details.size() == 1); List streetNameDetails = details.get(STREET_NAME); @@ -345,12 +351,12 @@ public void testCalcStreetNameDetails() { @Test public void testCalcEdgeIdDetails() { - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(EDGE_ID), new PathDetailsBuilderFactory(), 0); + Arrays.asList(EDGE_ID), new PathDetailsBuilderFactory(), 0, pathDetailGraph); assertTrue(details.size() == 1); List edgeIdDetails = details.get(EDGE_ID); @@ -370,12 +376,12 @@ public void testCalcEdgeIdDetails() { @Test public void testCalcEdgeKeyDetailsForward() { - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(EDGE_KEY), new PathDetailsBuilderFactory(), 0); + Arrays.asList(EDGE_KEY), new PathDetailsBuilderFactory(), 0, pathDetailGraph); List edgeKeyDetails = details.get(EDGE_KEY); assertEquals(4, edgeKeyDetails.size()); @@ -387,12 +393,12 @@ public void testCalcEdgeKeyDetailsForward() { @Test public void testCalcEdgeKeyDetailsBackward() { - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(5, 1); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(EDGE_KEY), new PathDetailsBuilderFactory(), 0); + Arrays.asList(EDGE_KEY), new PathDetailsBuilderFactory(), 0, pathDetailGraph); List edgeKeyDetails = details.get(EDGE_KEY); assertEquals(4, edgeKeyDetails.size()); @@ -404,12 +410,12 @@ public void testCalcEdgeKeyDetailsBackward() { @Test public void testCalcTimeDetails() { - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(TIME), new PathDetailsBuilderFactory(), 0); + Arrays.asList(TIME), new PathDetailsBuilderFactory(), 0, pathDetailGraph); assertTrue(details.size() == 1); List timeDetails = details.get(TIME); @@ -428,12 +434,12 @@ public void testCalcTimeDetails() { @Test public void testCalcDistanceDetails() { - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); assertTrue(p.isFound()); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(DISTANCE), new PathDetailsBuilderFactory(), 0); + Arrays.asList(DISTANCE), new PathDetailsBuilderFactory(), 0, pathDetailGraph); assertTrue(details.size() == 1); List distanceDetails = details.get(DISTANCE); @@ -443,17 +449,46 @@ public void testCalcDistanceDetails() { assertEquals(5D, distanceDetails.get(3).getValue()); } + @Test + public void testCalcIntersectionDetails() { + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); + Path p = new Dijkstra(pathDetailGraph, weighting, TraversalMode.NODE_BASED).calcPath(1, 5); + assertTrue(p.isFound()); + + Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, + Arrays.asList(INTERSECTION), new PathDetailsBuilderFactory(), 0, pathDetailGraph); + assertTrue(details.size() == 1); + + List intersectionDetails = details.get(INTERSECTION); + assertEquals(4, intersectionDetails.size()); + + Map intersectionMap = new HashMap<>(); + intersectionMap.put("out", 0); + intersectionMap.put("entries", Arrays.asList(true)); + intersectionMap.put("bearings", Arrays.asList(90)); + + assertEquals(intersectionMap, intersectionDetails.get(0).getValue()); + + intersectionMap.clear(); + intersectionMap.put("out", 0); + intersectionMap.put("in", 1); + intersectionMap.put("entries", Arrays.asList(true, false)); + intersectionMap.put("bearings", Arrays.asList(90, 270)); + + assertEquals(intersectionMap, intersectionDetails.get(1).getValue()); + } + /** * case with one edge being not an exit */ @Test public void testCalcInstructionsRoundabout2() { roundaboutGraph.inverse3to6(); - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 8); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); List tmpList = getTurnDescriptions(wayList); assertEquals(Arrays.asList("continue onto MainStreet 1 2", "At roundabout, take exit 2 onto 5-8", @@ -494,34 +529,34 @@ public void testCalcInstructionsRoundaboutIssue353() { na.setNode(10, 52.5135, 13.348); na.setNode(11, 52.514, 13.347); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(5)).setName("MainStreet 2 1"); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 11).setDistance(5)).setName("MainStreet 1 11"); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(2, 1).setDistance(5)).setKeyValues(createKV(STREET_NAME, "MainStreet 2 1")); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(1, 11).setDistance(5)).setKeyValues(createKV(STREET_NAME, "MainStreet 1 11")); // roundabout EdgeIteratorState tmpEdge; - tmpEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 9).setDistance(2)).setName("3-9"); + tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(3, 9).setDistance(2)).setKeyValues(createKV(STREET_NAME, "3-9")); BooleanEncodedValue carManagerRoundabout = carManager.getBooleanEncodedValue(Roundabout.KEY); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(9, 10).setDistance(2)).setName("9-10"); + tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(9, 10).setDistance(2)).setKeyValues(createKV(STREET_NAME, "9-10")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 10).setDistance(2)).setName("6-10"); + tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(6, 10).setDistance(2)).setKeyValues(createKV(STREET_NAME, "6-10")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(10, 1).setDistance(2)).setName("10-1"); + tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(10, 1).setDistance(2)).setKeyValues(createKV(STREET_NAME, "10-1")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(5)).setName("2-3"); + tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(3, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "2-3")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 3).setDistance(5)).setName("3-4"); + tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(4, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "3-4")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 4).setDistance(5)).setName("4-5"); + tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(5, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "4-5")); tmpEdge.set(carManagerRoundabout, true); - tmpEdge = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 5).setDistance(5)).setName("5-2"); + tmpEdge = GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(2, 5).setDistance(5)).setKeyValues(createKV(STREET_NAME, "5-2")); tmpEdge.set(carManagerRoundabout, true); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 7).setDistance(5)).setName("MainStreet 4 7"); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 8).setDistance(5)).setName("5-8"); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 6).setDistance(5)).setName("3-6"); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(4, 7).setDistance(5)).setKeyValues(createKV(STREET_NAME, "MainStreet 4 7")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(5, 8).setDistance(5)).setKeyValues(createKV(STREET_NAME, "5-8")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(3, 6).setDistance(5)).setKeyValues(createKV(STREET_NAME, "3-6")); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) .calcPath(6, 11); assertTrue(p.isFound()); @@ -535,11 +570,11 @@ public void testCalcInstructionsRoundaboutIssue353() { @Test public void testCalcInstructionsRoundaboutClockwise() { roundaboutGraph.setRoundabout(true); - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 8); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); List tmpList = getTurnDescriptions(wayList); assertEquals(Arrays.asList("continue onto MainStreet 1 2", "At roundabout, take exit 1 onto 5-8", @@ -554,11 +589,11 @@ public void testCalcInstructionsRoundaboutClockwise() { @Test public void testCalcInstructionsIgnoreContinue() { // Follow a couple of straight edges, including a name change - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(4, 11); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); // Contain only start and finish instruction, no CONTINUE assertEquals(2, wayList.size()); @@ -567,11 +602,11 @@ public void testCalcInstructionsIgnoreContinue() { @Test public void testCalcInstructionsIgnoreTurnIfNoAlternative() { // The street turns left, but there is not turn - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(10, 12); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); // Contain only start and finish instruction assertEquals(2, wayList.size()); @@ -595,11 +630,11 @@ public void testCalcInstructionForForkWithSameName() { na.setNode(3, 48.982611, 13.121012); na.setNode(4, 48.982336, 13.121002); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(5)).setName("Regener Weg"); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(5)).setName("Regener Weg"); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(5)); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Regener Weg")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Regener Weg")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 3).setDistance(5)); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -628,11 +663,11 @@ public void testCalcInstructionForMotorwayFork() { EnumEncodedValue roadClassEnc = carManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BooleanEncodedValue roadClassLinkEnc = carManager.getBooleanEncodedValue(RoadClassLink.KEY); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(5)).setName("A 8").set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(5)).setName("A 8").set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(5)).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, true); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, false); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 3).setDistance(5)).set(roadClassEnc, RoadClass.MOTORWAY).set(roadClassLinkEnc, true); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -656,11 +691,11 @@ public void testCalcInstructionsEnterMotorway() { na.setNode(3, 48.630558, 9.459851); na.setNode(4, 48.63054, 9.459406); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(5)).setName("A 8"); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(5)).setName("A 8"); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 2).setDistance(5)).setName("A 8"); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, graph.edge(4, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED) .calcPath(4, 3); assertTrue(p.isFound()); @@ -685,11 +720,11 @@ public void testCalcInstructionsMotorwayJunction() { na.setNode(3, 48.706805, 9.162995); na.setNode(4, 48.706705, 9.16329); - GHUtility.setSpeed(60, true, false, encoder, g.edge(1, 2).setDistance(5)).setName("A 8"); - GHUtility.setSpeed(60, true, false, encoder, g.edge(2, 3).setDistance(5)).setName("A 8"); - GHUtility.setSpeed(60, true, false, encoder, g.edge(2, 4).setDistance(5)).setName("A 8"); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "A 8")); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 3); assertTrue(p.isFound()); @@ -715,11 +750,11 @@ public void testCalcInstructionsOntoOneway() { na.setNode(3, -33.824415, 151.188177); na.setNode(4, -33.824437, 151.187925); - GHUtility.setSpeed(60, true, false, encoder, g.edge(1, 2).setDistance(5)).setName("Pacific Highway"); - GHUtility.setSpeed(60, true, false, encoder, g.edge(2, 3).setDistance(5)).setName("Pacific Highway"); - GHUtility.setSpeed(60, true, true, encoder, g.edge(4, 2).setDistance(5)).setName("Greenwich Road"); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Pacific Highway")); + GHUtility.setSpeed(60, true, false, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Pacific Highway")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(4, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Greenwich Road")); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(4, 3); assertTrue(p.isFound()); @@ -750,11 +785,11 @@ public void testCalcInstructionIssue1047() { EnumEncodedValue roadClassEnc = carManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BooleanEncodedValue roadClassLinkEnc = carManager.getBooleanEncodedValue(RoadClassLink.KEY); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(5)).setName("B 156").set(roadClassEnc, RoadClass.PRIMARY).set(roadClassLinkEnc, false); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 4).setDistance(5)).setName("S 108").set(roadClassEnc, RoadClass.SECONDARY).set(roadClassLinkEnc, false); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(5)).setName("B 156").set(roadClassEnc, RoadClass.PRIMARY).set(roadClassLinkEnc, false); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "B 156")).set(roadClassEnc, RoadClass.PRIMARY).set(roadClassLinkEnc, false); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "S 108")).set(roadClassEnc, RoadClass.SECONDARY).set(roadClassLinkEnc, false); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "B 156")).set(roadClassEnc, RoadClass.PRIMARY).set(roadClassLinkEnc, false); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -784,11 +819,11 @@ public void testCalcInstructionContinueLeavingStreet() { na.setNode(3, 48.982611, 13.121012); na.setNode(4, 48.982565, 13.121002); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(5)).setName("Regener Weg"); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(5)).setName("Regener Weg"); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Regener Weg")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 4).setDistance(5)); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Regener Weg")); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -814,11 +849,11 @@ public void testCalcInstructionSlightTurn() { na.setNode(3, 48.412034, 15.599411); na.setNode(4, 48.411927, 15.599197); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(5)).setName("Stöhrgasse"); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 4).setDistance(5)).setName("Stöhrgasse"); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Stöhrgasse")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Stöhrgasse")); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(4, 1); assertTrue(p.isFound()); @@ -847,16 +882,16 @@ public void testUTurnLeft() { na.setNode(6, 48.402422, 9.996067); na.setNode(7, 48.402604, 9.994962); - GHUtility.setSpeed(60, 0, encoder, - g.edge(1, 2).setDistance(5).setName("Olgastraße"), - g.edge(2, 3).setDistance(5).setName("Olgastraße"), - g.edge(6, 5).setDistance(5).setName("Olgastraße"), - g.edge(5, 4).setDistance(5).setName("Olgastraße")); - GHUtility.setSpeed(60, 60, encoder, - g.edge(2, 5).setDistance(5).setName("Neithardtstraße"), - g.edge(5, 7).setDistance(5).setName("Neithardtstraße")); + GHUtility.setSpeed(60, 0, carAccessEnc, carAvSpeedEnc, + g.edge(1, 2).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")), + g.edge(2, 3).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")), + g.edge(6, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße")), + g.edge(5, 4).setDistance(5).setKeyValues(createKV(STREET_NAME, "Olgastraße"))); + GHUtility.setSpeed(60, 60, carAccessEnc, carAvSpeedEnc, + g.edge(2, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "Neithardtstraße")), + g.edge(5, 7).setDistance(5).setKeyValues(createKV(STREET_NAME, "Neithardtstraße"))); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 4); assertTrue(p.isFound()); @@ -885,16 +920,16 @@ public void testUTurnRight() { na.setNode(6, -33.885692, 151.181445); na.setNode(7, -33.885692, 151.181445); - GHUtility.setSpeed(60, 0, encoder, - g.edge(1, 2).setDistance(5).setName("Parramatta Road"), - g.edge(2, 3).setDistance(5).setName("Parramatta Road"), - g.edge(4, 5).setDistance(5).setName("Parramatta Road"), - g.edge(5, 6).setDistance(5).setName("Parramatta Road")); - GHUtility.setSpeed(60, 60, encoder, - g.edge(2, 5).setDistance(5).setName("Larkin Street"), - g.edge(5, 7).setDistance(5).setName("Larkin Street")); + GHUtility.setSpeed(60, 0, carAccessEnc, carAvSpeedEnc, + g.edge(1, 2).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")), + g.edge(2, 3).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")), + g.edge(4, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road")), + g.edge(5, 6).setDistance(5).setKeyValues(createKV(STREET_NAME, "Parramatta Road"))); + GHUtility.setSpeed(60, 60, carAccessEnc, carAvSpeedEnc, + g.edge(2, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "Larkin Street")), + g.edge(5, 7).setDistance(5).setKeyValues(createKV(STREET_NAME, "Larkin Street"))); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 6); assertTrue(p.isFound()); @@ -907,11 +942,11 @@ public void testUTurnRight() { @Test public void testCalcInstructionsForTurn() { // The street turns left, but there is not turn - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(11, 13); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); // Contain start, turn, and finish instruction assertEquals(3, wayList.size()); @@ -921,12 +956,12 @@ public void testCalcInstructionsForTurn() { @Test public void testCalcInstructionsForSlightTurnWithOtherSlightTurn() { - // Test for a fork with two sligh turns. Since there are two sligh turns, show the turn instruction - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + // Test for a fork with two slight turns. Since there are two slight turns, show the turn instruction + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(12, 16); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); // Contain start, turn, and finish instruction assertEquals(3, wayList.size()); @@ -949,11 +984,11 @@ public void testCalcInstructionsForSlightTurnOntoDifferentStreet() { na.setNode(3, 48.764149, 8.678926); na.setNode(4, 48.764085, 8.679183); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 3).setDistance(5)).setName("Talstraße, K 4313"); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(5)).setName("Calmbacher Straße, K 4312"); - GHUtility.setSpeed(60, true, true, encoder, g.edge(3, 4).setDistance(5)).setName("Calmbacher Straße, K 4312"); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(1, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Talstraße, K 4313")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Calmbacher Straße, K 4312")); + GHUtility.setSpeed(60, true, true, carAccessEnc, carAvSpeedEnc, g.edge(3, 4).setDistance(5)).setKeyValues(createKV(STREET_NAME, "Calmbacher Straße, K 4312")); - ShortestWeighting weighting = new ShortestWeighting(encoder); + ShortestWeighting weighting = new ShortestWeighting(carAccessEnc, carAvSpeedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED) .calcPath(1, 2); assertTrue(p.isFound()); @@ -966,11 +1001,11 @@ public void testCalcInstructionsForSlightTurnOntoDifferentStreet() { @Test public void testIgnoreInstructionsForSlightTurnWithOtherTurn() { // Test for a fork with one sligh turn and one actual turn. We are going along the slight turn. No turn instruction needed in this case - ShortestWeighting weighting = new ShortestWeighting(mixedEncoders.getEncoder("car")); + ShortestWeighting weighting = new ShortestWeighting(mixedCarAccessEnc, mixedCarSpeedEnc); Path p = new Dijkstra(roundaboutGraph.g, weighting, TraversalMode.NODE_BASED) .calcPath(16, 19); assertTrue(p.isFound()); - InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncoders, tr); + InstructionList wayList = InstructionsFromEdges.calcInstructions(p, p.graph, weighting, mixedEncodingManager, tr); // Contain start, and finish instruction assertEquals(2, wayList.size()); @@ -995,25 +1030,23 @@ private Graph generatePathDetailsGraph() { na.setNode(5, 52.516, 13.3452); na.setNode(6, 52.516, 13.344); - GHUtility.setSpeed(45, true, true, encoder, graph.edge(1, 2).setDistance(5)).setName("1-2"); - GHUtility.setSpeed(45, true, true, encoder, graph.edge(4, 5).setDistance(5)).setName("4-5"); - GHUtility.setSpeed(90, true, true, encoder, graph.edge(2, 3).setDistance(5)).setName("2-3"); - GHUtility.setSpeed(9, true, true, encoder, graph.edge(3, 4).setDistance(10)).setName("3-4"); - GHUtility.setSpeed(9, true, true, encoder, graph.edge(5, 6).setDistance(0.01)).setName("3-4"); + GHUtility.setSpeed(45, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(1, 2).setDistance(5)).setKeyValues(createKV(STREET_NAME, "1-2")); + GHUtility.setSpeed(45, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(4, 5).setDistance(5)).setKeyValues(createKV(STREET_NAME, "4-5")); + GHUtility.setSpeed(90, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(2, 3).setDistance(5)).setKeyValues(createKV(STREET_NAME, "2-3")); + GHUtility.setSpeed(9, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(3, 4).setDistance(10)).setKeyValues(createKV(STREET_NAME, "3-4")); + GHUtility.setSpeed(9, true, true, carAccessEnc, carAvSpeedEnc, graph.edge(5, 6).setDistance(0.001)).setKeyValues(createKV(STREET_NAME, "3-4")); return graph; } - private static class RoundaboutGraph { + private class RoundaboutGraph { final BaseGraph g; final NodeAccess na; final EdgeIteratorState edge3to6, edge3to9; - final EncodingManager em; boolean clockwise = false; List roundaboutEdges = new LinkedList<>(); - private RoundaboutGraph(EncodingManager em) { - this.em = em; - g = new BaseGraph.Builder(em).create(); + private RoundaboutGraph() { + g = new BaseGraph.Builder(mixedEncodingManager).create(); na = g.getNodeAccess(); // 18 // 8 14 | @@ -1047,20 +1080,20 @@ private RoundaboutGraph(EncodingManager em) { na.setNode(19, 52.515, 13.368); // roundabout - roundaboutEdges.add(g.edge(3, 2).setDistance(5).setName("2-3")); - roundaboutEdges.add(g.edge(4, 3).setDistance(5).setName("3-4")); - roundaboutEdges.add(g.edge(5, 4).setDistance(5).setName("4-5")); - roundaboutEdges.add(g.edge(2, 5).setDistance(5).setName("5-2")); + roundaboutEdges.add(g.edge(3, 2).setDistance(5).setKeyValues(createKV(STREET_NAME, "2-3"))); + roundaboutEdges.add(g.edge(4, 3).setDistance(5).setKeyValues(createKV(STREET_NAME, "3-4"))); + roundaboutEdges.add(g.edge(5, 4).setDistance(5).setKeyValues(createKV(STREET_NAME, "4-5"))); + roundaboutEdges.add(g.edge(2, 5).setDistance(5).setKeyValues(createKV(STREET_NAME, "5-2"))); List bothDir = new ArrayList<>(); List oneDir = new ArrayList<>(roundaboutEdges); - bothDir.add(g.edge(1, 2).setDistance(5).setName("MainStreet 1 2")); - bothDir.add(g.edge(4, 7).setDistance(5).setName("MainStreet 4 7")); - bothDir.add(g.edge(5, 8).setDistance(5).setName("5-8")); + bothDir.add(g.edge(1, 2).setDistance(5).setKeyValues(createKV(STREET_NAME, "MainStreet 1 2"))); + bothDir.add(g.edge(4, 7).setDistance(5).setKeyValues(createKV(STREET_NAME, "MainStreet 4 7"))); + bothDir.add(g.edge(5, 8).setDistance(5).setKeyValues(createKV(STREET_NAME, "5-8"))); - bothDir.add(edge3to6 = g.edge(3, 6).setDistance(5).setName("3-6")); - oneDir.add(edge3to9 = g.edge(3, 9).setDistance(5).setName("3-9")); + bothDir.add(edge3to6 = g.edge(3, 6).setDistance(5).setKeyValues(createKV(STREET_NAME, "3-6"))); + oneDir.add(edge3to9 = g.edge(3, 9).setDistance(5).setKeyValues(createKV(STREET_NAME, "3-9"))); bothDir.add(g.edge(7, 10).setDistance(5)); bothDir.add(g.edge(10, 11).setDistance(5)); @@ -1073,40 +1106,36 @@ private RoundaboutGraph(EncodingManager em) { bothDir.add(g.edge(17, 18).setDistance(5)); bothDir.add(g.edge(17, 19).setDistance(5)); - for (FlagEncoder encoder : em.fetchEdgeEncoders()) { - double speed = encoder.getMaxSpeed() / 2; - GHUtility.setSpeed(speed, speed, encoder, bothDir); - GHUtility.setSpeed(speed, 0, encoder, oneDir); + for (EdgeIteratorState edge : bothDir) { + GHUtility.setSpeed(70, 70, mixedCarAccessEnc, mixedCarSpeedEnc, edge); + GHUtility.setSpeed(7, 7, mixedFootAccessEnc, mixedFootSpeedEnc, edge); + } + for (EdgeIteratorState edge : oneDir) { + GHUtility.setSpeed(70, 0, mixedCarAccessEnc, mixedCarSpeedEnc, edge); + GHUtility.setSpeed(7, 0, mixedFootAccessEnc, mixedFootSpeedEnc, edge); } - setRoundabout(clockwise); inverse3to9(); } public void setRoundabout(boolean clockwise) { - BooleanEncodedValue mixedRoundabout = em.getBooleanEncodedValue(Roundabout.KEY); - for (FlagEncoder encoder : em.fetchEdgeEncoders()) { - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - for (EdgeIteratorState edge : roundaboutEdges) { - edge.set(accessEnc, clockwise).setReverse(accessEnc, !clockwise); - edge.set(mixedRoundabout, true); - } + BooleanEncodedValue mixedRoundabout = mixedEncodingManager.getBooleanEncodedValue(Roundabout.KEY); + for (EdgeIteratorState edge : roundaboutEdges) { + edge.set(mixedCarAccessEnc, clockwise).setReverse(mixedCarAccessEnc, !clockwise); + edge.set(mixedFootAccessEnc, clockwise).setReverse(mixedFootAccessEnc, !clockwise); + edge.set(mixedRoundabout, true); } this.clockwise = clockwise; } public void inverse3to9() { - for (FlagEncoder encoder : em.fetchEdgeEncoders()) { - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - edge3to9.set(accessEnc, !edge3to9.get(accessEnc)).setReverse(accessEnc, false); - } + edge3to9.set(mixedCarAccessEnc, !edge3to9.get(mixedCarAccessEnc)).setReverse(mixedCarAccessEnc, false); + edge3to9.set(mixedFootAccessEnc, !edge3to9.get(mixedFootAccessEnc)).setReverse(mixedFootAccessEnc, false); } public void inverse3to6() { - for (FlagEncoder encoder : em.fetchEdgeEncoders()) { - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - edge3to6.set(accessEnc, !edge3to6.get(accessEnc)).setReverse(accessEnc, true); - } + edge3to6.set(mixedCarAccessEnc, !edge3to6.get(mixedCarAccessEnc)).setReverse(mixedCarAccessEnc, true); + edge3to6.set(mixedFootAccessEnc, !edge3to6.get(mixedFootAccessEnc)).setReverse(mixedFootAccessEnc, true); } private double getAngle(int n1, int n2, int n3, int n4) { diff --git a/core/src/test/java/com/graphhopper/routing/PriorityRoutingTest.java b/core/src/test/java/com/graphhopper/routing/PriorityRoutingTest.java index e91480ca35a..15c1bb9873e 100644 --- a/core/src/test/java/com/graphhopper/routing/PriorityRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/PriorityRoutingTest.java @@ -20,11 +20,10 @@ import com.carrotsearch.hppc.IntArrayList; import com.graphhopper.json.Statement; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadClass; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.PriorityWeighting; import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.routing.weighting.custom.CustomModelParser; @@ -43,8 +42,10 @@ public class PriorityRoutingTest { @Test void testMaxPriority() { - FlagEncoder encoder = FlagEncoders.createBike(); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", false); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, false); + DecimalEncodedValue priorityEnc = new DecimalEncodedValueImpl("priority", 4, PriorityCode.getFactor(1), false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).add(priorityEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 48.0, 11.0); @@ -55,16 +56,17 @@ void testMaxPriority() { na.setNode(5, 48.2, 11.1); // 0 - 1 - 2 - 3 // \- 4 - 5 -/ + double speed = speedEnc.getNextStorableValue(30); double dist1 = 0; - dist1 += maxSpeedEdge(em, graph, 0, 1, encoder, 1.0).getDistance(); - dist1 += maxSpeedEdge(em, graph, 1, 2, encoder, 1.0).getDistance(); - dist1 += maxSpeedEdge(em, graph, 2, 3, encoder, 1.0).getDistance(); + dist1 += addEdge(em, graph, 0, 1, 1.0, accessEnc, speedEnc, priorityEnc, speed).getDistance(); + dist1 += addEdge(em, graph, 1, 2, 1.0, accessEnc, speedEnc, priorityEnc, speed).getDistance(); + dist1 += addEdge(em, graph, 2, 3, 1.0, accessEnc, speedEnc, priorityEnc, speed).getDistance(); final double maxPrio = PriorityCode.getFactor(PriorityCode.BEST.getValue()); double dist2 = 0; - dist2 += maxSpeedEdge(em, graph, 0, 4, encoder, maxPrio).getDistance(); - dist2 += maxSpeedEdge(em, graph, 4, 5, encoder, maxPrio).getDistance(); - dist2 += maxSpeedEdge(em, graph, 5, 3, encoder, maxPrio).getDistance(); + dist2 += addEdge(em, graph, 0, 4, maxPrio, accessEnc, speedEnc, priorityEnc, speed).getDistance(); + dist2 += addEdge(em, graph, 4, 5, maxPrio, accessEnc, speedEnc, priorityEnc, speed).getDistance(); + dist2 += addEdge(em, graph, 5, 3, maxPrio, accessEnc, speedEnc, priorityEnc, speed).getDistance(); // the routes 0-1-2-3 and 0-4-5-3 have similar distances (and use max speed everywhere) // ... but the shorter route 0-1-2-3 has smaller priority @@ -73,7 +75,7 @@ void testMaxPriority() { // A* and Dijkstra should yield the same path (the max priority must be taken into account by weighting.getMinWeight) { - PriorityWeighting weighting = new PriorityWeighting(encoder, new PMap(), TurnCostProvider.NO_TURN_COST_PROVIDER); + PriorityWeighting weighting = new PriorityWeighting(accessEnc, speedEnc, priorityEnc, null, new PMap(), TurnCostProvider.NO_TURN_COST_PROVIDER); Path pathDijkstra = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); Path pathAStar = new AStar(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); assertEquals(pathDijkstra.calcNodes(), pathAStar.calcNodes()); @@ -82,7 +84,9 @@ void testMaxPriority() { { CustomModel customModel = new CustomModel(); - CustomWeighting weighting = CustomModelParser.createWeighting(encoder, em, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); + CustomWeighting weighting = CustomModelParser.createWeighting(accessEnc, + speedEnc, priorityEnc, em, + TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); Path pathDijkstra = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); Path pathAStar = new AStar(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); assertEquals(pathDijkstra.calcNodes(), pathAStar.calcNodes()); @@ -92,8 +96,9 @@ void testMaxPriority() { { CustomModel customModel = new CustomModel(); // now we even increase the priority in the custom model, which also needs to be accounted for in weighting.getMinWeight - customModel.addToPriority(Statement.If("road_class == MOTORWAY", Statement.Op.MULTIPLY, 3)); - CustomWeighting weighting = CustomModelParser.createWeighting(encoder, em, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); + customModel.addToPriority(Statement.If("road_class == MOTORWAY", Statement.Op.MULTIPLY, "3")); + CustomWeighting weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, + priorityEnc, em, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); Path pathDijkstra = new Dijkstra(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); Path pathAStar = new AStar(graph, weighting, TraversalMode.NODE_BASED).calcPath(0, 3); assertEquals(pathDijkstra.calcNodes(), pathAStar.calcNodes()); @@ -101,14 +106,11 @@ void testMaxPriority() { } } - private EdgeIteratorState maxSpeedEdge(EncodingManager em, BaseGraph graph, int p, int q, FlagEncoder encoder, double prio) { - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - DecimalEncodedValue speedEnc = encoder.getAverageSpeedEnc(); - DecimalEncodedValue priorityEnc = em.getDecimalEncodedValue(EncodingManager.getKey(encoder, "priority")); + private EdgeIteratorState addEdge(EncodingManager em, BaseGraph graph, int p, int q, double prio, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, DecimalEncodedValue priorityEnc, double speed) { EnumEncodedValue roadClassEnc = em.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); return graph.edge(p, q) .set(accessEnc, true) - .set(speedEnc, encoder.getMaxSpeed()) + .set(speedEnc, speed) .set(priorityEnc, prio) .set(roadClassEnc, RoadClass.MOTORWAY) .setDistance(calcDist(graph, p, q)); diff --git a/core/src/test/java/com/graphhopper/routing/ProfileResolverTest.java b/core/src/test/java/com/graphhopper/routing/ProfileResolverTest.java deleted file mode 100644 index 0ede45cc403..00000000000 --- a/core/src/test/java/com/graphhopper/routing/ProfileResolverTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.graphhopper.routing; - -import com.graphhopper.config.CHProfile; -import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; -import com.graphhopper.routing.ch.CHProfileSelectorTest; -import com.graphhopper.routing.lm.LMProfileSelectorTest; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.util.PMap; -import com.graphhopper.util.Parameters; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * So far this test is only testing the profile selection in the absence of CH/LM profiles. For CH/LM profile selection - * - * @see CHProfileSelectorTest - * @see LMProfileSelectorTest - */ -public class ProfileResolverTest { - @Test - public void defaultVehicle() { - ProfileResolver profileResolver = new ProfileResolver( - EncodingManager.create("car,foot,bike"), - Arrays.asList( - new Profile("my_bike").setVehicle("bike"), - new Profile("your_car").setVehicle("car") - ), - Collections.emptyList(), Collections.emptyList()); - // without specifying the vehicle we get an error, because there are multiple matches - assertMultiMatchError(profileResolver, new PMap(), "There are multiple profiles matching your request"); - // use vehicle to specify profile - assertEquals("your_car", profileResolver.resolveProfile(new PMap().putObject("vehicle", "car")).getName()); - assertEquals("my_bike", profileResolver.resolveProfile(new PMap().putObject("vehicle", "bike")).getName()); - } - - @Test - public void defaultWeighting() { - ProfileResolver profileResolver = new ProfileResolver( - EncodingManager.create("bike,car,foot"), - Arrays.asList( - new Profile("fast_bike").setVehicle("bike").setWeighting("fastest"), - new Profile("short_bike").setVehicle("bike").setWeighting("shortest") - ), - Collections.emptyList(), Collections.emptyList()); - // without specifying the weighting we get an error, because there are multiple matches - assertMultiMatchError(profileResolver, new PMap(), "There are multiple profiles matching your request"); - // use weighting to specify profile - assertEquals("short_bike", profileResolver.resolveProfile(new PMap().putObject("weighting", "shortest")).getName()); - assertEquals("fast_bike", profileResolver.resolveProfile(new PMap().putObject("weighting", "fastest")).getName()); - } - - @Test - public void missingProfiles() { - ProfileResolver profileResolver = new ProfileResolver( - EncodingManager.create("car,bike"), - Arrays.asList( - new Profile("fast_bike").setVehicle("bike").setWeighting("fastest"), - new Profile("short_bike").setVehicle("bike").setWeighting("shortest") - ), - Collections.emptyList(), Collections.emptyList()); - // there is a car encoder but no associated profile - assertProfileNotFound(profileResolver, new PMap().putObject("vehicle", "car")); - // if we do not specify a vehicle or weighting we even have multiple matches - assertMultiMatchError(profileResolver, new PMap(), "There are multiple profiles matching your request"); - // if we specify the weighting its clear which profile we want - assertEquals("short_bike", profileResolver.resolveProfile(new PMap().putObject("weighting", "shortest")).getName()); - // setting the vehicle to bike is not enough - assertMultiMatchError(profileResolver, new PMap().putObject("vehicle", "bike"), "There are multiple profiles matching your request"); - // if we set the weighting as well it works - assertEquals("fast_bike", profileResolver.resolveProfile(new PMap().putObject("vehicle", "bike").putObject("weighting", "fastest")).getName()); - assertEquals("short_bike", profileResolver.resolveProfile(new PMap().putObject("vehicle", "bike").putObject("weighting", "shortest")).getName()); - } - - @Test - public void edgeBasedAndTurnCosts() { - ProfileResolver profileResolver = new ProfileResolver( - EncodingManager.create("foot"), - Collections.singletonList(new Profile("profile").setVehicle("foot").setWeighting("fastest")), - Collections.emptyList(), Collections.emptyList()); - - assertProfileNotFound(profileResolver, new PMap().putObject(Parameters.Routing.EDGE_BASED, true)); - assertEquals("profile", profileResolver.resolveProfile(new PMap()).getName()); - assertEquals("profile", profileResolver.resolveProfile(new PMap().putObject(Parameters.Routing.EDGE_BASED, false)).getName()); - } - - @Test - public void defaultVehicleAllAlgos() { - final String profile1 = "foot_profile"; - final String profile2 = "car_profile"; - final String vehicle1 = "foot"; - final String vehicle2 = "car"; - final String weighting = "shortest"; - - ProfileResolver profileResolver = new ProfileResolver( - EncodingManager.create(vehicle1 + "," + vehicle2), - Arrays.asList( - new Profile(profile1).setVehicle(vehicle1).setWeighting(weighting), - new Profile(profile2).setVehicle(vehicle2).setWeighting(weighting) - ), - Arrays.asList(new CHProfile(profile1), new CHProfile(profile2)), - Arrays.asList(new LMProfile(profile1), new LMProfile(profile2)) - ); - // when we do not specify vehicle/weighting, we get an error because there are multiple matches - PMap hints = new PMap(); - assertMultiMatchError(profileResolver, hints, "There are multiple CH profiles matching your request"); - assertMultiMatchError(profileResolver, hints.putObject(Parameters.CH.DISABLE, true), "There are multiple LM profiles matching your request"); - assertMultiMatchError(profileResolver, hints.putObject(Parameters.Landmark.DISABLE, true), "There are multiple profiles matching your request"); - - // using the weighting is not enough, because its the same for both profiles - hints = new PMap().putObject("weighting", "shortest"); - assertMultiMatchError(profileResolver, hints, "There are multiple CH profiles matching your request"); - assertMultiMatchError(profileResolver, hints.putObject(Parameters.CH.DISABLE, true), "There are multiple LM profiles matching your request"); - assertMultiMatchError(profileResolver, hints.putObject(Parameters.Landmark.DISABLE, true), "There are multiple profiles matching your request"); - - // using the vehicle to select one of the profiles works - hints = new PMap().putObject("vehicle", vehicle1); - assertEquals(profile1, profileResolver.resolveProfile(hints).getName()); - assertEquals(profile1, profileResolver.resolveProfile(hints.putObject(Parameters.CH.DISABLE, true)).getName()); - assertEquals(profile1, profileResolver.resolveProfile(hints.putObject(Parameters.Landmark.DISABLE, true)).getName()); - } - - private void assertMultiMatchError(ProfileResolver profileResolver, PMap hints, String... expectedErrors) { - if (expectedErrors.length == 0) { - throw new IllegalArgumentException("there must be at least one expected error"); - } - try { - profileResolver.resolveProfile(hints); - fail(); - } catch (IllegalArgumentException e) { - for (String expectedError : expectedErrors) { - assertTrue(e.getMessage().contains(expectedError), e.getMessage()); - } - } - } - - private void assertProfileNotFound(ProfileResolver profileResolver, PMap hints) { - try { - profileResolver.resolveProfile(hints); - fail(); - } catch (IllegalArgumentException e) { - assertTrue(e.getMessage().contains("Cannot find matching profile for your request"), e.getMessage()); - } - } - -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/QueryRoutingCHGraphTest.java b/core/src/test/java/com/graphhopper/routing/QueryRoutingCHGraphTest.java index 7c2e6532c65..a556844f224 100644 --- a/core/src/test/java/com/graphhopper/routing/QueryRoutingCHGraphTest.java +++ b/core/src/test/java/com/graphhopper/routing/QueryRoutingCHGraphTest.java @@ -19,13 +19,10 @@ package com.graphhopper.routing; import com.graphhopper.routing.ch.PrepareEncoder; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.querygraph.QueryRoutingCHGraph; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.storage.*; @@ -33,7 +30,6 @@ import com.graphhopper.util.DistancePlaneProjection; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -44,7 +40,9 @@ import static org.junit.jupiter.api.Assertions.*; class QueryRoutingCHGraphTest { - private FlagEncoder encoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; + private DecimalEncodedValue turnCostEnc; private EncodingManager encodingManager; private FastestWeighting weighting; private BaseGraph graph; @@ -52,18 +50,20 @@ class QueryRoutingCHGraphTest { @BeforeEach public void setup() { - encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", 5).putObject("speed_two_directions", true)); - encodingManager = EncodingManager.create(encoder); - graph = new BaseGraph.Builder(encodingManager).create(); - weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage())); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + turnCostEnc = TurnCost.create("car", 5); + encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); + weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); na = graph.getNodeAccess(); } @Test public void basic() { // 0-1-2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); graph.freeze(); assertEquals(2, graph.getEdges()); @@ -100,8 +100,8 @@ public void basic() { public void withShortcuts() { // 0-1-2 // \-/ - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); graph.freeze(); assertEquals(2, graph.getEdges()); @@ -111,7 +111,7 @@ public void withShortcuts() { CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); chBuilder.setIdentityLevels(); - chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1, 0, 1); + chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1, 0, 2); QueryGraph queryGraph = QueryGraph.create(graph, Collections.emptyList()); QueryRoutingCHGraph queryCHGraph = new QueryRoutingCHGraph(routingCHGraph, queryGraph); @@ -221,7 +221,7 @@ public void withVirtualEdgesAndShortcuts() { CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); chBuilder.setIdentityLevels(); - chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1, 0, 1); + chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1, 0, 2); Snap snap = new Snap(50.00, 10.05); snap.setClosestEdge(edge); @@ -266,7 +266,7 @@ public void withVirtualEdgesAndShortcuts() { @Test public void getBaseGraph() { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); graph.freeze(); CHConfig chConfig = CHConfig.edgeBased("x", weighting); @@ -297,7 +297,7 @@ public void getEdgeIteratorState() { CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); chBuilder.setIdentityLevels(); - chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1, 0, 1); + chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1, 0, 2); Snap snap = new Snap(50.00, 10.05); snap.setClosestEdge(edge); @@ -370,7 +370,7 @@ public void getWeight() { na.setNode(2, 50.00, 10.20); EdgeIteratorState edge = addEdge(graph, 0, 1) // use different speeds for the two directions - .set(encoder.getAverageSpeedEnc(), 90, 30); + .set(speedEnc, 90, 30); addEdge(graph, 1, 2); graph.freeze(); @@ -380,7 +380,7 @@ public void getWeight() { CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); chBuilder.setIdentityLevels(); - chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScDirMask(), 20, 0, 1, 0, 1); + chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScDirMask(), 20, 0, 1, 0, 2); // without query graph RoutingCHEdgeIterator iter = routingCHGraph.createOutEdgeExplorer().setBaseNode(0); @@ -471,8 +471,8 @@ public void getWeight_withAccess() { // we set the access flags, but do use direction dependent speeds to make sure we are testing whether or not the // access flags are respected and the weight calculation does not simply rely on the speed, see this forum issue // https://discuss.graphhopper.com/t/speed-and-access-when-setbothdirections-true-false/5695 - edge.set(encoder.getAccessEnc(), true, false); - edge.set(encoder.getAverageSpeedEnc(), 60, 60); + edge.set(accessEnc, true, false); + edge.set(speedEnc, 60, 60); graph.freeze(); CHConfig chConfig = CHConfig.edgeBased("x", weighting); @@ -550,7 +550,6 @@ public void getTurnCost() { na.setNode(2, 50.00, 10.20); EdgeIteratorState edge1 = addEdge(graph, 0, 1); EdgeIteratorState edge2 = addEdge(graph, 1, 2); - DecimalEncodedValue turnCostEnc = encodingManager.getDecimalEncodedValue(TurnCost.key(encoder.toString())); graph.getTurnCostStorage().set(turnCostEnc, 0, 1, 1, 5); graph.freeze(); @@ -560,7 +559,7 @@ public void getTurnCost() { CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); chBuilder.setIdentityLevels(); - chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1, 0, 1); + chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 20, 0, 1, 0, 2); // without virtual nodes assertEquals(5, routingCHGraph.getTurnWeight(0, 1, 1)); @@ -695,7 +694,7 @@ private void assertEnd(RoutingCHEdgeIterator outIter) { private EdgeIteratorState addEdge(Graph graph, int from, int to) { NodeAccess na = graph.getNodeAccess(); double dist = DistancePlaneProjection.DIST_PLANE.calcDist(na.getLat(from), na.getLon(from), na.getLat(to), na.getLon(to)); - return GHUtility.setSpeed(60, true, true, encoder, graph.edge(from, to).setDistance(dist)); + return GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(from, to).setDistance(dist)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java index dc7cc4962a1..e1f361e0536 100644 --- a/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/RandomCHRoutingTest.java @@ -2,10 +2,12 @@ import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory; import com.graphhopper.routing.ch.PrepareContractionHierarchies; -import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.querygraph.QueryRoutingCHGraph; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; @@ -43,28 +45,29 @@ private static final class Fixture { private final TraversalMode traversalMode; private final int maxTurnCosts; private final int uTurnCosts; - private final Directory dir; - private final FlagEncoder encoder; - private final EncodingManager encodingManager; + private final BooleanEncodedValue accessEnc; + private final DecimalEncodedValue speedEnc; + private final DecimalEncodedValue turnCostEnc; private Weighting weighting; - private BaseGraph graph; + private final BaseGraph graph; private CHConfig chConfig; Fixture(TraversalMode traversalMode, int uTurnCosts) { this.traversalMode = traversalMode; this.maxTurnCosts = 10; this.uTurnCosts = uTurnCosts; - dir = new RAMDirectory(); - encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", maxTurnCosts)); - encodingManager = EncodingManager.create(encoder); - graph = new BaseGraph.Builder(encodingManager).create(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + turnCostEnc = TurnCost.create("car", maxTurnCosts); + EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); } void freeze() { graph.freeze(); chConfig = traversalMode.isEdgeBased() - ? CHConfig.edgeBased("p", new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage(), uTurnCosts))) - : CHConfig.nodeBased("p", new FastestWeighting(encoder)); + ? CHConfig.edgeBased("p", new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), uTurnCosts))) + : CHConfig.nodeBased("p", new FastestWeighting(accessEnc, speedEnc)); weighting = chConfig.getWeighting(); } @@ -103,9 +106,9 @@ public void random(Fixture f) { // the same as taking the direct edge! double pOffset = 0; GHUtility.buildRandomGraph(f.graph, rnd, numNodes, 2.5, true, true, - f.encoder.getAccessEnc(), f.encoder.getAverageSpeedEnc(), null, 0.7, 0.9, pOffset); + f.accessEnc, f.speedEnc, null, 0.7, 0.9, pOffset); if (f.traversalMode.isEdgeBased()) { - GHUtility.addRandomTurnCosts(f.graph, seed, f.encodingManager, f.encoder, f.maxTurnCosts, f.graph.getTurnCostStorage()); + GHUtility.addRandomTurnCosts(f.graph, seed, f.accessEnc, f.turnCostEnc, f.maxTurnCosts, f.graph.getTurnCostStorage()); } runRandomTest(f, rnd, 20); } @@ -115,7 +118,7 @@ public void random(Fixture f) { public void issue1574_1(Fixture f) { assumeFalse(f.traversalMode.isEdgeBased()); Random rnd = new Random(9348906923700L); - buildRandomGraphLegacy(f.graph, f.encoder, rnd, 50, 2.5, false, true, 0.9); + buildRandomGraphLegacy(f.graph, f.accessEnc, f.speedEnc, rnd, 50, 2.5, false, true, 0.9); runRandomTest(f, rnd, 20); } @@ -124,7 +127,7 @@ public void issue1574_1(Fixture f) { public void issue1574_2(Fixture f) { assumeFalse(f.traversalMode.isEdgeBased()); Random rnd = new Random(10093639220394L); - buildRandomGraphLegacy(f.graph, f.encoder, rnd, 50, 2.5, false, true, 0.9); + buildRandomGraphLegacy(f.graph, f.accessEnc, f.speedEnc, rnd, 50, 2.5, false, true, 0.9); runRandomTest(f, rnd, 20); } @@ -133,7 +136,7 @@ public void issue1574_2(Fixture f) { public void issue1582(Fixture f) { assumeFalse(f.traversalMode.isEdgeBased()); Random rnd = new Random(4111485945982L); - buildRandomGraphLegacy(f.graph, f.encoder, rnd, 10, 2.5, false, true, 0.9); + buildRandomGraphLegacy(f.graph, f.accessEnc, f.speedEnc, rnd, 10, 2.5, false, true, 0.9); runRandomTest(f, rnd, 100); } @@ -142,7 +145,7 @@ public void issue1582(Fixture f) { public void issue1583(Fixture f) { assumeFalse(f.traversalMode.isEdgeBased()); Random rnd = new Random(10785899964423L); - buildRandomGraphLegacy(f.graph, f.encoder, rnd, 50, 2.5, true, true, 0.9); + buildRandomGraphLegacy(f.graph, f.accessEnc, f.speedEnc, rnd, 50, 2.5, true, true, 0.9); runRandomTest(f, rnd, 20); } @@ -153,13 +156,13 @@ public void issue1593(Fixture f) { long seed = 60643479675316L; Random rnd = new Random(seed); GHUtility.buildRandomGraph(f.graph, rnd, 50, 2.5, true, true, - f.encoder.getAccessEnc(), f.encoder.getAverageSpeedEnc(), null, 0.7, 0.9, 0.0); - GHUtility.addRandomTurnCosts(f.graph, seed, f.encodingManager, f.encoder, f.maxTurnCosts, f.graph.getTurnCostStorage()); + f.accessEnc, f.speedEnc, null, 0.7, 0.9, 0.0); + GHUtility.addRandomTurnCosts(f.graph, seed, f.accessEnc, f.turnCostEnc, f.maxTurnCosts, f.graph.getTurnCostStorage()); runRandomTest(f, rnd, 20); } private void runRandomTest(Fixture f, Random rnd, int numVirtualNodes) { - LocationIndexTree locationIndex = new LocationIndexTree(f.graph, f.dir); + LocationIndexTree locationIndex = new LocationIndexTree(f.graph, f.graph.getDirectory()); locationIndex.prepareIndex(); f.freeze(); @@ -225,7 +228,7 @@ private void runRandomTest(Fixture f, Random rnd, int numVirtualNodes) { * More or less does the same as {@link GHUtility#buildRandomGraph}, but since some special seeds * are used in a few tests above this code is kept here. Do not use it for new tests. */ - private void buildRandomGraphLegacy(Graph graph, FlagEncoder encoder, Random random, int numNodes, double meanDegree, boolean allowLoops, boolean allowZeroDistance, double pBothDir) { + private void buildRandomGraphLegacy(Graph graph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, Random random, int numNodes, double meanDegree, boolean allowLoops, boolean allowZeroDistance, double pBothDir) { for (int i = 0; i < numNodes; ++i) { double lat = 49.4 + (random.nextDouble() * 0.0001); double lon = 9.7 + (random.nextDouble() * 0.0001); @@ -251,10 +254,9 @@ private void buildRandomGraphLegacy(Graph graph, FlagEncoder encoder, Random ran maxDist = Math.max(maxDist, distance); // using bidirectional edges will increase mean degree of graph above given value boolean bothDirections = random.nextDouble() < pBothDir; - EdgeIteratorState edge = GHUtility.setSpeed(60, true, bothDirections, encoder, graph.edge(from, to).setDistance(distance)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, bothDirections, accessEnc, speedEnc, graph.edge(from, to).setDistance(distance)); double fwdSpeed = 10 + random.nextDouble() * 120; double bwdSpeed = 10 + random.nextDouble() * 120; - DecimalEncodedValue speedEnc = encoder.getAverageSpeedEnc(); edge.set(speedEnc, fwdSpeed); if (speedEnc.isStoreTwoDirections()) edge.setReverse(speedEnc, bwdSpeed); diff --git a/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java index aa34b1b3eba..1e4ab6f5669 100644 --- a/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/RandomizedRoutingTest.java @@ -18,15 +18,15 @@ package com.graphhopper.routing; -import com.carrotsearch.hppc.IntArrayList; -import com.carrotsearch.hppc.IntIndexedContainer; import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory; import com.graphhopper.routing.ch.PrepareContractionHierarchies; -import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.lm.*; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.querygraph.QueryRoutingCHGraph; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; @@ -43,7 +43,6 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.function.Supplier; @@ -70,39 +69,58 @@ public class RandomizedRoutingTest { private static class FixtureProvider implements ArgumentsProvider { @Override public Stream provideArguments(ExtensionContext context) { - return Stream.>of( - () -> new Fixture(Algo.DIJKSTRA, false, false, NODE_BASED), - () -> new Fixture(Algo.ASTAR_UNIDIR, false, false, NODE_BASED), - () -> new Fixture(Algo.ASTAR_BIDIR, false, false, NODE_BASED), - () -> new Fixture(Algo.CH_ASTAR, true, false, NODE_BASED), - () -> new Fixture(Algo.CH_DIJKSTRA, true, false, NODE_BASED), - () -> new Fixture(Algo.LM_UNIDIR, false, true, NODE_BASED), - () -> new Fixture(Algo.LM_BIDIR, false, true, NODE_BASED), - () -> new Fixture(Algo.DIJKSTRA, false, false, EDGE_BASED), - () -> new Fixture(Algo.ASTAR_UNIDIR, false, false, EDGE_BASED), - () -> new Fixture(Algo.ASTAR_BIDIR, false, false, EDGE_BASED), - () -> new Fixture(Algo.CH_ASTAR, true, false, EDGE_BASED), - () -> new Fixture(Algo.CH_DIJKSTRA, true, false, EDGE_BASED), - () -> new Fixture(Algo.LM_UNIDIR, false, true, EDGE_BASED), - () -> new Fixture(Algo.LM_BIDIR, false, true, EDGE_BASED), - () -> new Fixture(Algo.PERFECT_ASTAR, false, false, NODE_BASED) + return Stream.of( + FixtureSupplier.create(Algo.DIJKSTRA, false, false, NODE_BASED), + FixtureSupplier.create(Algo.ASTAR_UNIDIR, false, false, NODE_BASED), + FixtureSupplier.create(Algo.ASTAR_BIDIR, false, false, NODE_BASED), + FixtureSupplier.create(Algo.CH_ASTAR, true, false, NODE_BASED), + FixtureSupplier.create(Algo.CH_DIJKSTRA, true, false, NODE_BASED), + FixtureSupplier.create(Algo.LM_UNIDIR, false, true, NODE_BASED), + FixtureSupplier.create(Algo.LM_BIDIR, false, true, NODE_BASED), + FixtureSupplier.create(Algo.DIJKSTRA, false, false, EDGE_BASED), + FixtureSupplier.create(Algo.ASTAR_UNIDIR, false, false, EDGE_BASED), + FixtureSupplier.create(Algo.ASTAR_BIDIR, false, false, EDGE_BASED), + FixtureSupplier.create(Algo.CH_ASTAR, true, false, EDGE_BASED), + FixtureSupplier.create(Algo.CH_DIJKSTRA, true, false, EDGE_BASED), + FixtureSupplier.create(Algo.LM_UNIDIR, false, true, EDGE_BASED), + FixtureSupplier.create(Algo.LM_BIDIR, false, true, EDGE_BASED), + FixtureSupplier.create(Algo.PERFECT_ASTAR, false, false, NODE_BASED) ).map(Arguments::of); } } + private static class FixtureSupplier { + private final Supplier supplier; + private final String name; + + static FixtureSupplier create(Algo algo, boolean prepareCH, boolean prepareLM, TraversalMode traversalMode) { + return new FixtureSupplier(() -> new Fixture(algo, prepareCH, prepareLM, traversalMode), algo.toString()); + } + + public FixtureSupplier(Supplier supplier, String name) { + this.supplier = supplier; + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + private static class Fixture { private final Algo algo; private final boolean prepareCH; private final boolean prepareLM; private final TraversalMode traversalMode; - private final Directory dir; private final BaseGraph graph; - private final List chConfigs; - private final LMConfig lmConfig; - private final FlagEncoder encoder; + private final BooleanEncodedValue accessEnc; + private final DecimalEncodedValue speedEnc; + private final DecimalEncodedValue turnCostEnc; private final TurnCostStorage turnCostStorage; private final int maxTurnCosts; - private final Weighting weighting; + private Weighting weighting; private final EncodingManager encodingManager; private RoutingCHGraph routingCHGraph; private LandmarkStorage lm; @@ -113,23 +131,17 @@ private static class Fixture { this.prepareLM = prepareLM; this.traversalMode = traversalMode; maxTurnCosts = 10; - dir = new RAMDirectory(); // todo: this test only works with speedTwoDirections=false (as long as loops are enabled), otherwise it will // fail sometimes for edge-based algorithms, #1631, but maybe we can should disable different fwd/bwd speeds // only for loops instead? - encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", maxTurnCosts)); - encodingManager = new EncodingManager.Builder().add(encoder).add(Subnetwork.create("car")).build(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + turnCostEnc = TurnCost.create("car", maxTurnCosts); + encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).add(Subnetwork.create("car")).build(); graph = new BaseGraph.Builder(encodingManager) - .setDir(dir) + .withTurnCosts(true) .create(); turnCostStorage = graph.getTurnCostStorage(); - chConfigs = Arrays.asList( - CHConfig.nodeBased("p1", new FastestWeighting(encoder)), - CHConfig.edgeBased("p2", new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage()))) - ); - // important: for LM preparation we need to use a weighting without turn costs #1960 - lmConfig = new LMConfig("car", chConfigs.get(0).getWeighting()); - weighting = traversalMode.isEdgeBased() ? chConfigs.get(1).getWeighting() : chConfigs.get(0).getWeighting(); } @Override @@ -139,14 +151,19 @@ public String toString() { private void preProcessGraph() { graph.freeze(); + weighting = traversalMode.isEdgeBased() + ? new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())) + : new FastestWeighting(accessEnc, speedEnc); if (prepareCH) { - CHConfig chConfig = !traversalMode.isEdgeBased() ? chConfigs.get(0) : chConfigs.get(1); + CHConfig chConfig = traversalMode.isEdgeBased() ? CHConfig.edgeBased("p", weighting) : CHConfig.nodeBased("p", weighting); PrepareContractionHierarchies pch = PrepareContractionHierarchies.fromGraph(graph, chConfig); PrepareContractionHierarchies.Result res = pch.doWork(); routingCHGraph = RoutingCHGraphImpl.fromGraph(graph, res.getCHStorage(), res.getCHConfig()); } if (prepareLM) { - PrepareLandmarks prepare = new PrepareLandmarks(dir, graph, encodingManager, lmConfig, 16); + // important: for LM preparation we need to use a weighting without turn costs #1960 + LMConfig lmConfig = new LMConfig("car", new FastestWeighting(accessEnc, speedEnc)); + PrepareLandmarks prepare = new PrepareLandmarks(graph.getDirectory(), graph, encodingManager, lmConfig, 16); prepare.setMaximumWeight(10000); prepare.doWork(); lm = prepare.getLandmarkStorage(); @@ -191,90 +208,6 @@ private RoutingAlgorithm createAlgo(Graph graph) { } } - private List comparePaths(Path refPath, Path path, int source, int target, long seed) { - List strictViolations = new ArrayList<>(); - double refWeight = refPath.getWeight(); - double weight = path.getWeight(); - if (Math.abs(refWeight - weight) > 1.e-2) { - LOGGER.warn("expected: " + refPath.calcNodes()); - LOGGER.warn("given: " + path.calcNodes()); - LOGGER.warn("seed: " + seed); - fail("wrong weight: " + source + "->" + target + "\nexpected: " + refWeight + "\ngiven: " + weight + "\nseed: " + seed); - } - if (Math.abs(path.getDistance() - refPath.getDistance()) > 1.e-1) { - strictViolations.add("wrong distance " + source + "->" + target + ", expected: " + refPath.getDistance() + ", given: " + path.getDistance()); - } - if (Math.abs(path.getTime() - refPath.getTime()) > 50) { - strictViolations.add("wrong time " + source + "->" + target + ", expected: " + refPath.getTime() + ", given: " + path.getTime()); - } - IntIndexedContainer refNodes = refPath.calcNodes(); - IntIndexedContainer pathNodes = path.calcNodes(); - if (!refNodes.equals(pathNodes)) { - // sometimes paths are only different because of a zero weight loop. we do not consider these as strict - // violations, see: #1864 - boolean isStrictViolation = !ArrayUtil.withoutConsecutiveDuplicates(refNodes).equals(ArrayUtil.withoutConsecutiveDuplicates(pathNodes)); - // sometimes there are paths including an edge a-c that has the same distance as the two edges a-b-c. in this - // case both options are valid best paths. we only check for this most simple and frequent case here... - if (pathsEqualExceptOneEdge(refNodes, pathNodes)) - isStrictViolation = false; - if (isStrictViolation) - strictViolations.add("wrong nodes " + source + "->" + target + "\nexpected: " + refNodes + "\ngiven: " + pathNodes); - } - return strictViolations; - } - - /** - * Sometimes the graph can contain edges like this: - * A--C - * \-B| - * where A-C is the same distance as A-B-C. In this case the shortest path is not well defined in terms of nodes. - * This method checks if two node-paths are equal except for such an edge. - */ - private boolean pathsEqualExceptOneEdge(IntIndexedContainer p1, IntIndexedContainer p2) { - if (p1.equals(p2)) - throw new IllegalArgumentException("paths are equal"); - if (Math.abs(p1.size() - p2.size()) != 1) - return false; - IntIndexedContainer shorterPath = p1.size() < p2.size() ? p1 : p2; - IntIndexedContainer longerPath = p1.size() < p2.size() ? p2 : p1; - if (shorterPath.size() < 2) - return false; - IntArrayList indicesWithDifferentNodes = new IntArrayList(); - for (int i = 1; i < shorterPath.size(); i++) { - if (shorterPath.get(i - indicesWithDifferentNodes.size()) != longerPath.get(i)) { - indicesWithDifferentNodes.add(i); - } - } - if (indicesWithDifferentNodes.size() != 1) - return false; - int b = indicesWithDifferentNodes.get(0); - int a = b - 1; - int c = b + 1; - assert shorterPath.get(a) == longerPath.get(a); - assert shorterPath.get(b) != longerPath.get(b); - if (shorterPath.get(b) != longerPath.get(c)) - return false; - double distABC = getMinDist(longerPath.get(a), longerPath.get(b)) + getMinDist(longerPath.get(b), longerPath.get(c)); - - double distAC = getMinDist(shorterPath.get(a), longerPath.get(c)); - if (Math.abs(distABC - distAC) > 0.1) - return false; - LOGGER.info("Distance " + shorterPath.get(a) + "-" + longerPath.get(c) + " is the same as distance " + - longerPath.get(a) + "-" + longerPath.get(b) + "-" + longerPath.get(c) + " -> there are multiple possibilities " + - "for shortest paths"); - return true; - } - - private double getMinDist(int p, int q) { - EdgeExplorer explorer = graph.createEdgeExplorer(); - EdgeIterator iter = explorer.setBaseNode(p); - double distance = Double.MAX_VALUE; - while (iter.next()) - if (iter.getAdjNode() == q) - distance = Math.min(distance, iter.getDistance()); - return distance; - } - } private enum Algo { @@ -297,15 +230,15 @@ public Stream provideArguments(ExtensionContext context) { @ParameterizedTest @ArgumentsSource(RepeatedFixtureProvider.class) - public void randomGraph(Supplier fixtureSupplier) { - Fixture f = fixtureSupplier.get(); + public void randomGraph(FixtureSupplier fixtureSupplier) { + Fixture f = fixtureSupplier.supplier.get(); final long seed = System.nanoTime(); final int numQueries = 50; Random rnd = new Random(seed); GHUtility.buildRandomGraph(f.graph, rnd, 100, 2.2, true, true, - f.encoder.getAccessEnc(), f.encoder.getAverageSpeedEnc(), null, 0.7, 0.8, 0.8); - GHUtility.addRandomTurnCosts(f.graph, seed, f.encodingManager, f.encoder, f.maxTurnCosts, f.turnCostStorage); -// GHUtility.printGraphForUnitTest(f.graph, f.encoder); + f.accessEnc, f.speedEnc, null, 0.7, 0.8, 0.8); + GHUtility.addRandomTurnCosts(f.graph, seed, f.accessEnc, f.turnCostEnc, f.maxTurnCosts, f.turnCostStorage); +// GHUtility.printGraphForUnitTest(f.graph, f.accessEnc, f.speedEnc); f.preProcessGraph(); List strictViolations = new ArrayList<>(); for (int i = 0; i < numQueries; i++) { @@ -316,7 +249,7 @@ public void randomGraph(Supplier fixtureSupplier) { .calcPath(source, target); Path path = f.createAlgo() .calcPath(source, target); - strictViolations.addAll(f.comparePaths(refPath, path, source, target, seed)); + strictViolations.addAll(GHUtility.comparePaths(refPath, path, source, target, seed)); } if (strictViolations.size() > 3) { for (String strictViolation : strictViolations) { @@ -331,8 +264,8 @@ public void randomGraph(Supplier fixtureSupplier) { */ @ParameterizedTest @ArgumentsSource(RepeatedFixtureProvider.class) - public void randomGraph_withQueryGraph(Supplier fixtureSupplier) { - Fixture f = fixtureSupplier.get(); + public void randomGraph_withQueryGraph(FixtureSupplier fixtureSupplier) { + Fixture f = fixtureSupplier.supplier.get(); final long seed = System.nanoTime(); final int numQueries = 50; @@ -341,11 +274,11 @@ public void randomGraph_withQueryGraph(Supplier fixtureSupplier) { double pOffset = 0; Random rnd = new Random(seed); GHUtility.buildRandomGraph(f.graph, rnd, 50, 2.2, true, true, - f.encoder.getAccessEnc(), f.encoder.getAverageSpeedEnc(), null, 0.7, 0.8, pOffset); - GHUtility.addRandomTurnCosts(f.graph, seed, f.encodingManager, f.encoder, f.maxTurnCosts, f.turnCostStorage); -// GHUtility.printGraphForUnitTest(f.graph, f.encoder); + f.accessEnc, f.speedEnc, null, 0.7, 0.8, pOffset); + GHUtility.addRandomTurnCosts(f.graph, seed, f.accessEnc, f.turnCostEnc, f.maxTurnCosts, f.turnCostStorage); +// GHUtility.printGraphForUnitTest(f.graph, f.accessEnc, f.speedEnc); f.preProcessGraph(); - LocationIndexTree index = new LocationIndexTree(f.graph, f.dir); + LocationIndexTree index = new LocationIndexTree(f.graph, f.graph.getDirectory()); index.prepareIndex(); List strictViolations = new ArrayList<>(); for (int i = 0; i < numQueries; i++) { @@ -357,7 +290,7 @@ public void randomGraph_withQueryGraph(Supplier fixtureSupplier) { Path refPath = new DijkstraBidirectionRef(queryGraph, queryGraph.wrapWeighting(f.weighting), f.traversalMode).calcPath(source, target); Path path = f.createAlgo(queryGraph).calcPath(source, target); - strictViolations.addAll(f.comparePaths(refPath, path, source, target, seed)); + strictViolations.addAll(GHUtility.comparePaths(refPath, path, source, target, seed)); } // we do not do a strict check because there can be ambiguity, for example when there are zero weight loops. // however, when there are too many deviations we fail diff --git a/core/src/test/java/com/graphhopper/routing/RoundTripRoutingTest.java b/core/src/test/java/com/graphhopper/routing/RoundTripRoutingTest.java index 589f968ad74..f0b41076a6c 100644 --- a/core/src/test/java/com/graphhopper/routing/RoundTripRoutingTest.java +++ b/core/src/test/java/com/graphhopper/routing/RoundTripRoutingTest.java @@ -18,8 +18,15 @@ package com.graphhopper.routing; import com.carrotsearch.hppc.IntArrayList; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.querygraph.QueryGraph; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.FiniteWeightFilter; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; @@ -46,9 +53,10 @@ * @author Peter Karich */ public class RoundTripRoutingTest { - private final FlagEncoder carFE = FlagEncoders.createCar(); - private final EncodingManager em = EncodingManager.create(carFE); - private final Weighting fastestWeighting = new FastestWeighting(carFE); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + private final Weighting fastestWeighting = new FastestWeighting(accessEnc, speedEnc); // TODO private final TraversalMode tMode = TraversalMode.EDGE_BASED; private final TraversalMode tMode = TraversalMode.NODE_BASED; private final GHPoint ghPoint1 = new GHPoint(0, 0); @@ -122,7 +130,7 @@ public void testCalcRoundTrip() { private BaseGraph createTestGraph() { BaseGraph graph = new BaseGraph.Builder(em).withTurnCosts(true).create(); - AlternativeRouteTest.initTestGraph(graph, carFE); + AlternativeRouteTest.initTestGraph(graph, accessEnc, speedEnc); return graph; } @@ -135,7 +143,7 @@ private BaseGraph createSquareGraph() { // |-1 0 1 BaseGraph graph = new BaseGraph.Builder(em).create(); for (int i = 0; i < 8; ++i) { - GHUtility.setSpeed(60, true, true, carFE, graph.edge(i, (i + 1) % 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(i, (i + 1) % 8).setDistance(1)); } updateDistancesFor(graph, 0, 1, -1); updateDistancesFor(graph, 1, 1, 0); diff --git a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java index ea4fb78d6c8..865446d7ebd 100644 --- a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java +++ b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmTest.java @@ -23,11 +23,12 @@ import com.graphhopper.routing.ch.PrepareContractionHierarchies; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.querygraph.QueryRoutingCHGraph; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.ShortestWeighting; @@ -87,9 +88,12 @@ public class RoutingAlgorithmTest { private static class Fixture { private final EncodingManager encodingManager; - private final FlagEncoder carEncoder; - private final FlagEncoder footEncoder; - private final FlagEncoder bike2Encoder; + private final BooleanEncodedValue carAccessEnc; + private final BooleanEncodedValue footAccessEnc; + private final BooleanEncodedValue bike2AccessEnc; + private final DecimalEncodedValue carSpeedEnc; + private final DecimalEncodedValue footSpeedEnc; + private final DecimalEncodedValue bike2SpeedEnc; private final PathCalculator pathCalculator; private final TraversalMode traversalMode; private final Weighting defaultWeighting; @@ -98,13 +102,18 @@ private static class Fixture { public Fixture(PathCalculator pathCalculator, TraversalMode traversalMode) { this.pathCalculator = pathCalculator; this.traversalMode = traversalMode; - // vehicles used in this test - encodingManager = EncodingManager.create("car,foot,bike2"); - carEncoder = encodingManager.getEncoder("car"); - footEncoder = encodingManager.getEncoder("foot"); - bike2Encoder = encodingManager.getEncoder("bike2"); + carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + footAccessEnc = new SimpleBooleanEncodedValue("foot_access", true); + bike2AccessEnc = new SimpleBooleanEncodedValue("bike2_access", true); + carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + footSpeedEnc = new DecimalEncodedValueImpl("foot_speed", 4, 1, false); + bike2SpeedEnc = new DecimalEncodedValueImpl("bike2_speed", 4, 2, true); + encodingManager = EncodingManager.start() + .add(carAccessEnc).add(carSpeedEnc) + .add(footAccessEnc).add(footSpeedEnc) + .add(bike2AccessEnc).add(bike2SpeedEnc).build(); // most tests use the default weighting, but this can be chosen for each test separately - defaultWeighting = new ShortestWeighting(carEncoder); + defaultWeighting = new ShortestWeighting(carAccessEnc, carSpeedEnc); // most tests do not limit the number of visited nodes, but this can be chosen for each test separately defaultMaxVisitedNodes = Integer.MAX_VALUE; } @@ -226,7 +235,7 @@ public Stream provideArguments(ExtensionContext context) th @ArgumentsSource(FixtureProvider.class) public void testCalcShortestPath(Fixture f) { BaseGraph graph = f.createGHStorage(); - initTestStorage(graph, f.carEncoder); + initTestStorage(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, 0, 7); assertEquals(nodes(0, 4, 5, 7), p.calcNodes(), p.toString()); assertEquals(62.1, p.getDistance(), .1, p.toString()); @@ -237,8 +246,8 @@ public void testCalcShortestPath(Fixture f) { public void testCalcShortestPath_sourceEqualsTarget(Fixture f) { // 0-1-2 BaseGraph graph = f.createGHStorage(); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(1, 2).setDistance(2)); Path p = f.calcPath(graph, 0, 0); assertPathFromEqualsTo(p, 0); @@ -251,11 +260,11 @@ public void testSimpleAlternative(Fixture f) { // | | // 3--4 BaseGraph graph = f.createGHStorage(); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(0, 2).setDistance(9)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(2, 1).setDistance(2)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(2, 3).setDistance(11)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(3, 4).setDistance(6)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(4, 1).setDistance(9)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 2).setDistance(9)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(2, 1).setDistance(2)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(2, 3).setDistance(11)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(3, 4).setDistance(6)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(4, 1).setDistance(9)); Path p = f.calcPath(graph, 0, 4); assertEquals(20, p.getDistance(), 1e-4, p.toString()); assertEquals(nodes(0, 2, 1, 4), p.calcNodes()); @@ -266,10 +275,10 @@ public void testSimpleAlternative(Fixture f) { public void testBidirectionalLinear(Fixture f) { //3--2--1--4--5 BaseGraph graph = f.createGHStorage(); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(2, 1).setDistance(2)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(2, 3).setDistance(11)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(5, 4).setDistance(6)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(4, 1).setDistance(9)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(2, 1).setDistance(2)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(2, 3).setDistance(11)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(5, 4).setDistance(6)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(4, 1).setDistance(9)); Path p = f.calcPath(graph, 3, 5); assertEquals(28, p.getDistance(), 1e-4, p.toString()); assertEquals(nodes(3, 2, 1, 4, 5), p.calcNodes()); @@ -279,19 +288,19 @@ public void testBidirectionalLinear(Fixture f) { @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testCalcFastestPath(Fixture f) { - FastestWeighting fastestWeighting = new FastestWeighting(f.carEncoder); + FastestWeighting fastestWeighting = new FastestWeighting(f.carAccessEnc, f.carSpeedEnc); BaseGraph graph = f.createGHStorage(false); - initDirectedAndDiffSpeed(graph, f.carEncoder); + initDirectedAndDiffSpeed(graph, f.carAccessEnc, f.carSpeedEnc); Path p1 = f.calcPath(graph, f.defaultWeighting, 0, 3); assertEquals(nodes(0, 1, 5, 2, 3), p1.calcNodes()); assertEquals(402.3, p1.getDistance(), .1, p1.toString()); - assertEquals(144829, p1.getTime(), p1.toString()); + assertEquals(144830, p1.getTime(), p1.toString()); Path p2 = f.calcPath(graph, fastestWeighting, 0, 3); assertEquals(nodes(0, 4, 6, 7, 5, 3), p2.calcNodes()); assertEquals(1261.7, p2.getDistance(), 0.1, p2.toString()); - assertEquals(111439, p2.getTime(), p2.toString()); + assertEquals(111441, p2.getTime(), p2.toString()); } // 0-1-2-3 @@ -299,27 +308,27 @@ public void testCalcFastestPath(Fixture f) { // 4-5-- | // |/ \--7 // 6----/ - static void initDirectedAndDiffSpeed(Graph graph, FlagEncoder enc) { - GHUtility.setSpeed(10, true, false, enc, graph.edge(0, 1)); - GHUtility.setSpeed(100, true, false, enc, graph.edge(0, 4)); + static void initDirectedAndDiffSpeed(Graph graph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, graph.edge(0, 1)); + GHUtility.setSpeed(100, true, false, accessEnc, speedEnc, graph.edge(0, 4)); - GHUtility.setSpeed(10, true, true, enc, graph.edge(1, 4)); - GHUtility.setSpeed(10, true, true, enc, graph.edge(1, 5)); - EdgeIteratorState edge12 = GHUtility.setSpeed(10, true, true, enc, graph.edge(1, 2)); + GHUtility.setSpeed(10, true, true, accessEnc, speedEnc, graph.edge(1, 4)); + GHUtility.setSpeed(10, true, true, accessEnc, speedEnc, graph.edge(1, 5)); + EdgeIteratorState edge12 = GHUtility.setSpeed(10, true, true, accessEnc, speedEnc, graph.edge(1, 2)); - GHUtility.setSpeed(10, true, false, enc, graph.edge(5, 2)); - GHUtility.setSpeed(10, true, false, enc, graph.edge(2, 3)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, graph.edge(5, 2)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, graph.edge(2, 3)); - EdgeIteratorState edge53 = GHUtility.setSpeed(20, true, false, enc, graph.edge(5, 3)); - GHUtility.setSpeed(10, true, false, enc, graph.edge(3, 7)); + EdgeIteratorState edge53 = GHUtility.setSpeed(20, true, false, accessEnc, speedEnc, graph.edge(5, 3)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, graph.edge(3, 7)); - GHUtility.setSpeed(100, true, false, enc, graph.edge(4, 6)); - GHUtility.setSpeed(10, true, false, enc, graph.edge(5, 4)); + GHUtility.setSpeed(100, true, false, accessEnc, speedEnc, graph.edge(4, 6)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, graph.edge(5, 4)); - GHUtility.setSpeed(10, true, false, enc, graph.edge(5, 6)); - GHUtility.setSpeed(100, true, false, enc, graph.edge(7, 5)); + GHUtility.setSpeed(10, true, false, accessEnc, speedEnc, graph.edge(5, 6)); + GHUtility.setSpeed(100, true, false, accessEnc, speedEnc, graph.edge(7, 5)); - GHUtility.setSpeed(100, true, true, enc, graph.edge(6, 7)); + GHUtility.setSpeed(100, true, true, accessEnc, speedEnc, graph.edge(6, 7)); updateDistancesFor(graph, 0, 0.002, 0); updateDistancesFor(graph, 1, 0.002, 0.001); @@ -337,74 +346,75 @@ static void initDirectedAndDiffSpeed(Graph graph, FlagEncoder enc) { @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testCalcFootPath(Fixture f) { - ShortestWeighting shortestWeighting = new ShortestWeighting(f.footEncoder); + ShortestWeighting shortestWeighting = new ShortestWeighting(f.footAccessEnc, f.footSpeedEnc); BaseGraph graph = f.createGHStorage(false); - initFootVsCar(f.carEncoder, f.footEncoder, graph); + initFootVsCar(f.carAccessEnc, f.carSpeedEnc, f.footAccessEnc, f.footSpeedEnc, graph); Path p1 = f.calcPath(graph, shortestWeighting, 0, 7); assertEquals(17000, p1.getDistance(), 1e-6, p1.toString()); assertEquals(12240 * 1000, p1.getTime(), p1.toString()); assertEquals(nodes(0, 4, 5, 7), p1.calcNodes()); } - static void initFootVsCar(FlagEncoder carEncoder, FlagEncoder footEncoder, Graph graph) { + static void initFootVsCar(BooleanEncodedValue carAccessEnc, DecimalEncodedValue carSpeedEnc, + BooleanEncodedValue footAccessEnc, DecimalEncodedValue footSpeedEnc, Graph graph) { EdgeIteratorState edge = graph.edge(0, 1).setDistance(7000); - GHUtility.setSpeed(5, true, true, footEncoder, edge); - GHUtility.setSpeed(10, true, false, carEncoder, edge); + GHUtility.setSpeed(5, true, true, footAccessEnc, footSpeedEnc, edge); + GHUtility.setSpeed(10, true, false, carAccessEnc, carSpeedEnc, edge); edge = graph.edge(0, 4).setDistance(5000); - GHUtility.setSpeed(5, true, true, footEncoder, edge); - GHUtility.setSpeed(20, true, false, carEncoder, edge); + GHUtility.setSpeed(5, true, true, footAccessEnc, footSpeedEnc, edge); + GHUtility.setSpeed(20, true, false, carAccessEnc, carSpeedEnc, edge); - GHUtility.setSpeed(10, true, true, carEncoder, graph.edge(1, 4).setDistance(7000)); - GHUtility.setSpeed(10, true, true, carEncoder, graph.edge(1, 5).setDistance(7000)); + GHUtility.setSpeed(10, true, true, carAccessEnc, carSpeedEnc, graph.edge(1, 4).setDistance(7000)); + GHUtility.setSpeed(10, true, true, carAccessEnc, carSpeedEnc, graph.edge(1, 5).setDistance(7000)); edge = graph.edge(1, 2).setDistance(20000); - GHUtility.setSpeed(5, true, true, footEncoder, edge); - GHUtility.setSpeed(10, true, true, carEncoder, edge); + GHUtility.setSpeed(5, true, true, footAccessEnc, footSpeedEnc, edge); + GHUtility.setSpeed(10, true, true, carAccessEnc, carSpeedEnc, edge); - GHUtility.setSpeed(10, true, false, carEncoder, graph.edge(5, 2).setDistance(5000)); + GHUtility.setSpeed(10, true, false, carAccessEnc, carSpeedEnc, graph.edge(5, 2).setDistance(5000)); edge = graph.edge(2, 3).setDistance(5000); - GHUtility.setSpeed(5, true, true, footEncoder, edge); - GHUtility.setSpeed(10, true, false, carEncoder, edge); + GHUtility.setSpeed(5, true, true, footAccessEnc, footSpeedEnc, edge); + GHUtility.setSpeed(10, true, false, carAccessEnc, carSpeedEnc, edge); - GHUtility.setSpeed(20, true, false, carEncoder, graph.edge(5, 3).setDistance(11000)); + GHUtility.setSpeed(20, true, false, carAccessEnc, carSpeedEnc, graph.edge(5, 3).setDistance(11000)); edge = graph.edge(3, 7).setDistance(7000); - GHUtility.setSpeed(5, true, true, footEncoder, edge); - GHUtility.setSpeed(10, true, false, carEncoder, edge); + GHUtility.setSpeed(5, true, true, footAccessEnc, footSpeedEnc, edge); + GHUtility.setSpeed(10, true, false, carAccessEnc, carSpeedEnc, edge); - GHUtility.setSpeed(20, true, false, carEncoder, graph.edge(4, 6).setDistance(5000)); + GHUtility.setSpeed(20, true, false, carAccessEnc, carSpeedEnc, graph.edge(4, 6).setDistance(5000)); edge = graph.edge(5, 4).setDistance(7000); - GHUtility.setSpeed(5, true, true, footEncoder, edge); - GHUtility.setSpeed(10, true, false, carEncoder, edge); + GHUtility.setSpeed(5, true, true, footAccessEnc, footSpeedEnc, edge); + GHUtility.setSpeed(10, true, false, carAccessEnc, carSpeedEnc, edge); - GHUtility.setSpeed(10, true, false, carEncoder, graph.edge(5, 6).setDistance(7000)); + GHUtility.setSpeed(10, true, false, carAccessEnc, carSpeedEnc, graph.edge(5, 6).setDistance(7000)); edge = graph.edge(7, 5).setDistance(5000); - GHUtility.setSpeed(5, true, true, footEncoder, edge); - GHUtility.setSpeed(20, true, false, carEncoder, edge); + GHUtility.setSpeed(5, true, true, footAccessEnc, footSpeedEnc, edge); + GHUtility.setSpeed(20, true, false, carAccessEnc, carSpeedEnc, edge); - GHUtility.setSpeed(20, true, true, carEncoder, graph.edge(6, 7).setDistance(5000)); + GHUtility.setSpeed(20, true, true, carAccessEnc, carSpeedEnc, graph.edge(6, 7).setDistance(5000)); } // see test-graph.svg ! - static void initTestStorage(Graph graph, FlagEncoder encoder) { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(7)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 4).setDistance(6)); + static void initTestStorage(Graph graph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(7)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 4).setDistance(6)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 5).setDistance(8)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 5).setDistance(8)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 5).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 5).setDistance(5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 5).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 7).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 5).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 7).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 6).setDistance(4)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(7)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 6).setDistance(4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(7)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 7).setDistance(1)); - EdgeIteratorState edge6_7 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(5)); + EdgeIteratorState edge6_7 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(5)); updateDistancesFor(graph, 0, 0.0010, 0.00001); updateDistancesFor(graph, 1, 0.0008, 0.0000); @@ -434,18 +444,18 @@ public void testNoPathFound(Fixture f) { // 7-5-6 // \| // 8 - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(0, 1).setDistance(7)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(5, 6).setDistance(2)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(5, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(5, 8).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(7)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(5, 6).setDistance(2)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(5, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(5, 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(7, 8).setDistance(1)); assertFalse(f.calcPath(graph, 0, 5).isFound()); // disconnected as directed graph // 2-0->1 graph = f.createGHStorage(); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(0, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 2).setDistance(1)); f.resetCH(); assertFalse(f.calcPath(graph, 1, 2).isFound()); assertTrue(f.calcPath(graph, 2, 1).isFound()); @@ -455,7 +465,7 @@ public void testNoPathFound(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testWikipediaShortestPath(Fixture f) { BaseGraph graph = f.createGHStorage(); - initWikipediaTestGraph(graph, f.carEncoder); + initWikipediaTestGraph(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, 0, 4); assertEquals(nodes(0, 2, 5, 4), p.calcNodes(), p.toString()); assertEquals(20, p.getDistance(), 1e-4, p.toString()); @@ -465,30 +475,30 @@ public void testWikipediaShortestPath(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testCalcIf1EdgeAway(Fixture f) { BaseGraph graph = f.createGHStorage(); - initTestStorage(graph, f.carEncoder); + initTestStorage(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, 1, 2); assertEquals(nodes(1, 2), p.calcNodes()); assertEquals(35.1, p.getDistance(), .1, p.toString()); } // see wikipedia-graph.svg ! - private void initWikipediaTestGraph(Graph graph, FlagEncoder encoder) { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(7)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2).setDistance(9)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 5).setDistance(14)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(15)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 5).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(11)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(6)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(9)); + private void initWikipediaTestGraph(Graph graph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(7)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2).setDistance(9)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 5).setDistance(14)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(15)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 5).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(11)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(6)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(9)); } @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testBidirectional(Fixture f) { BaseGraph graph = f.createGHStorage(); - initBiGraph(graph, f.carEncoder); + initBiGraph(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, 0, 4); assertEquals(nodes(0, 7, 6, 8, 3, 4), p.calcNodes(), p.toString()); @@ -505,18 +515,18 @@ public void testBidirectional(Fixture f) { // | 8 | // \ / | // 7-6----5 - public static void initBiGraph(Graph graph, FlagEncoder encoder) { + public static void initBiGraph(Graph graph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { // distance will be overwritten in second step as we need to calculate it from lat,lon - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 0).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 8).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 6).setDistance(1)); // we need lat,lon for edge precise queries because the distances of snapped point // to adjacent nodes is calculated from lat,lon of the necessary points @@ -542,16 +552,16 @@ public void testCreateAlgoTwice(Fixture f) { // \ / / // 7-6-5-/ BaseGraph graph = f.createGHStorage(); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(6, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(7, 0).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(3, 8).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(8, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(7, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(3, 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(8, 6).setDistance(1)); // run the same query twice, this can be interesting because in the second call algorithms that pre-process // the graph might depend on the state of the graph after the first call @@ -565,7 +575,7 @@ public void testCreateAlgoTwice(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testMaxVisitedNodes(Fixture f) { BaseGraph graph = f.createGHStorage(); - initBiGraph(graph, f.carEncoder); + initBiGraph(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, 0, 4); assertTrue(p.isFound()); @@ -578,29 +588,29 @@ public void testMaxVisitedNodes(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testBidirectional2(Fixture f) { BaseGraph graph = f.createGHStorage(); - initBidirGraphManualDistances(graph, f.carEncoder); + initBidirGraphManualDistances(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, 0, 4); assertEquals(40, p.getDistance(), 1e-4, p.toString()); assertEquals(5, p.calcNodes().size(), p.toString()); assertEquals(nodes(0, 7, 6, 5, 4), p.calcNodes()); } - private void initBidirGraphManualDistances(BaseGraph graph, FlagEncoder encoder) { + private void initBidirGraphManualDistances(BaseGraph graph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { // 0-1-2-3-4 // | / | // | 8 | // \ / / // 7-6-5-/ - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(20)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 0).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 8).setDistance(20)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 6).setDistance(20)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(20)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 0).setDistance(5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 8).setDistance(20)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 6).setDistance(20)); } @ParameterizedTest @@ -608,7 +618,7 @@ private void initBidirGraphManualDistances(BaseGraph graph, FlagEncoder encoder) public void testRekeyBugOfIntBinHeap(Fixture f) { // using Dijkstra + IntBinHeap then rekey loops endlessly BaseGraph matrixGraph = f.createGHStorage(); - initMatrixALikeGraph(matrixGraph, f.carEncoder); + initMatrixALikeGraph(matrixGraph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(matrixGraph, 36, 91); assertEquals(12, p.calcNodes().size()); @@ -623,7 +633,7 @@ public void testRekeyBugOfIntBinHeap(Fixture f) { testCorrectWeight(f, matrixGraph); } - private static void initMatrixALikeGraph(BaseGraph tmpGraph, FlagEncoder encoder) { + private static void initMatrixALikeGraph(BaseGraph tmpGraph, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { int WIDTH = 10; int HEIGHT = 15; int[][] matrix = new int[WIDTH][HEIGHT]; @@ -645,7 +655,7 @@ private static void initMatrixALikeGraph(BaseGraph tmpGraph, FlagEncoder encoder if (print) System.out.print(" " + (int) dist + "\t "); - GHUtility.setSpeed(60, true, true, encoder, tmpGraph.edge(matrix[w][h], matrix[w][h - 1]).setDistance(dist)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, tmpGraph.edge(matrix[w][h], matrix[w][h - 1]).setDistance(dist)); } } if (print) { @@ -663,7 +673,7 @@ private static void initMatrixALikeGraph(BaseGraph tmpGraph, FlagEncoder encoder float dist = 5 + Math.abs(rand.nextInt(5)); if (print) System.out.print("-- " + (int) dist + "\t-- "); - GHUtility.setSpeed(60, true, true, encoder, tmpGraph.edge(matrix[w][h], matrix[w - 1][h]).setDistance(dist)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, tmpGraph.edge(matrix[w][h], matrix[w - 1][h]).setDistance(dist)); } if (print) System.out.print("(" + matrix[w][h] + ")\t"); @@ -691,8 +701,8 @@ private void testCorrectWeight(Fixture f, BaseGraph g) { public void testCannotCalculateSP(Fixture f) { // 0->1->2 BaseGraph graph = f.createGHStorage(); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(1, 2).setDistance(1)); Path p = f.calcPath(graph, 0, 2); assertEquals(3, p.calcNodes().size(), p.toString()); } @@ -701,12 +711,12 @@ public void testCannotCalculateSP(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testDirectedGraphBug1(Fixture f) { BaseGraph graph = f.createGHStorage(); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(0, 1).setDistance(3)); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(1, 2).setDistance(2.99)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(1, 2).setDistance(2.99)); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(0, 3).setDistance(2)); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(3, 4).setDistance(3)); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(4, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 3).setDistance(2)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(3, 4).setDistance(3)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(4, 2).setDistance(1)); Path p = f.calcPath(graph, 0, 2); assertEquals(nodes(0, 1, 2), p.calcNodes(), p.toString()); @@ -720,10 +730,10 @@ public void testDirectedGraphBug2(Fixture f) { // | / // 3< BaseGraph graph = f.createGHStorage(); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(3, 1).setDistance(4)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(3, 1).setDistance(4)); Path p = f.calcPath(graph, 0, 3); assertEquals(nodes(0, 1, 2, 3), p.calcNodes()); @@ -736,23 +746,23 @@ public void testDirectedGraphBug2(Fixture f) { @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testWithCoordinates(Fixture f) { - Weighting weighting = new ShortestWeighting(f.carEncoder); + Weighting weighting = new ShortestWeighting(f.carAccessEnc, f.carSpeedEnc); BaseGraph graph = f.createGHStorage(false); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(0, 1).setDistance(2)). + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(2)). setWayGeometry(Helper.createPointList(1.5, 1)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(2, 3).setDistance(2)). + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(2, 3).setDistance(2)). setWayGeometry(Helper.createPointList(0, 1.5)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(3, 4).setDistance(2)). + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(3, 4).setDistance(2)). setWayGeometry(Helper.createPointList(0, 2)); // duplicate but the second edge is longer - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(0, 2).setDistance(1.2)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(0, 2).setDistance(1.5)). + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 2).setDistance(1.2)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 2).setDistance(1.5)). setWayGeometry(Helper.createPointList(0.5, 0)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(1, 3).setDistance(1.3)). + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(1, 3).setDistance(1.3)). setWayGeometry(Helper.createPointList(0.5, 1.5)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(1, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(1, 4).setDistance(1)); updateDistancesFor(graph, 0, 1, 0.6); updateDistancesFor(graph, 1, 1, 1.5); @@ -775,7 +785,7 @@ public void testWithCoordinates(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testCalcIfEmptyWay(Fixture f) { BaseGraph graph = f.createGHStorage(); - initTestStorage(graph, f.carEncoder); + initTestStorage(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, 0, 0); assertPathFromEqualsTo(p, 0); } @@ -784,7 +794,7 @@ public void testCalcIfEmptyWay(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testViaEdges_FromEqualsTo(Fixture f) { BaseGraph graph = f.createGHStorage(); - initTestStorage(graph, f.carEncoder); + initTestStorage(graph, f.carAccessEnc, f.carSpeedEnc); // identical tower nodes Path p = f.calcPath(graph, new GHPoint(0.001, 0.000), new GHPoint(0.001, 0.000)); assertPathFromEqualsTo(p, 0); @@ -803,7 +813,7 @@ public void testViaEdges_FromEqualsTo(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testViaEdges_BiGraph(Fixture f) { BaseGraph graph = f.createGHStorage(); - initBiGraph(graph, f.carEncoder); + initBiGraph(graph, f.carAccessEnc, f.carSpeedEnc); // 0-7 to 4-3 Path p = f.calcPath(graph, new GHPoint(0.0009, 0), new GHPoint(0.001, 0.001105)); @@ -820,7 +830,7 @@ public void testViaEdges_BiGraph(Fixture f) { @ArgumentsSource(FixtureProvider.class) public void testViaEdges_WithCoordinates(Fixture f) { BaseGraph graph = f.createGHStorage(); - initTestStorage(graph, f.carEncoder); + initTestStorage(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, f.defaultWeighting, 0, 1, 2, 3); assertEquals(nodes(8, 1, 2, 9), p.calcNodes()); assertEquals(56.7, p.getDistance(), .1, p.toString()); @@ -833,11 +843,11 @@ public void testViaEdges_SpecialCases(Fixture f) { // 0->1\ // | 2 // 4<-3/ - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(0, 1).setDistance(7)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(1, 2).setDistance(7)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(2, 3).setDistance(7)); - GHUtility.setSpeed(60, true, false, f.carEncoder, graph.edge(3, 4).setDistance(7)); - GHUtility.setSpeed(60, true, true, f.carEncoder, graph.edge(4, 0).setDistance(7)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(0, 1).setDistance(7)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(1, 2).setDistance(7)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(2, 3).setDistance(7)); + GHUtility.setSpeed(60, true, false, f.carAccessEnc, f.carSpeedEnc, graph.edge(3, 4).setDistance(7)); + GHUtility.setSpeed(60, true, true, f.carAccessEnc, f.carSpeedEnc, graph.edge(4, 0).setDistance(7)); updateDistancesFor(graph, 4, 0, 0); updateDistancesFor(graph, 0, 0.00010, 0); @@ -864,9 +874,9 @@ public void testViaEdges_SpecialCases(Fixture f) { @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testQueryGraphAndFastest(Fixture f) { - Weighting weighting = new FastestWeighting(f.carEncoder); + Weighting weighting = new FastestWeighting(f.carAccessEnc, f.carSpeedEnc); BaseGraph graph = f.createGHStorage(false); - initDirectedAndDiffSpeed(graph, f.carEncoder); + initDirectedAndDiffSpeed(graph, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, weighting, new GHPoint(0.002, 0.0005), new GHPoint(0.0017, 0.0031)); assertEquals(nodes(8, 1, 5, 3, 9), p.calcNodes()); assertEquals(602.98, p.getDistance(), 1e-1); @@ -875,11 +885,11 @@ public void testQueryGraphAndFastest(Fixture f) { @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testTwoWeightsPerEdge(Fixture f) { - FastestWeighting fastestWeighting = new FastestWeighting(f.bike2Encoder); + FastestWeighting fastestWeighting = new FastestWeighting(f.bike2AccessEnc, f.bike2SpeedEnc); BaseGraph graph = f.createGHStorage(true); - initEleGraph(graph, f.bike2Encoder, 18); + initEleGraph(graph, 18, f.bike2AccessEnc, f.bike2SpeedEnc); // force the other path - GHUtility.setSpeed(10, false, true, f.bike2Encoder, GHUtility.getEdge(graph, 0, 3)); + GHUtility.setSpeed(10, false, true, f.bike2AccessEnc, f.bike2SpeedEnc, GHUtility.getEdge(graph, 0, 3)); // for two weights per edge it happened that Path (and also the Weighting) read the wrong side // of the speed and read 0 => infinity weight => overflow of millis => negative millis! @@ -895,12 +905,10 @@ public void test0SpeedButUnblocked_Issue242(Fixture f) { BaseGraph graph = f.createGHStorage(); EdgeIteratorState edge01 = graph.edge(0, 1).setDistance(10); EdgeIteratorState edge12 = graph.edge(1, 2).setDistance(10); - BooleanEncodedValue carAccessEnc = f.carEncoder.getAccessEnc(); - DecimalEncodedValue carAvSpeedEnc = f.carEncoder.getAverageSpeedEnc(); - edge01.set(carAvSpeedEnc, 0.0).set(carAccessEnc, true, true); + edge01.set(f.carSpeedEnc, 0.0).set(f.carAccessEnc, true, true); edge01.setFlags(edge01.getFlags()); - edge12.set(carAvSpeedEnc, 0.0).set(carAccessEnc, true, true); + edge12.set(f.carSpeedEnc, 0.0).set(f.carAccessEnc, true, true); edge12.setFlags(edge12.getFlags()); try { @@ -916,7 +924,7 @@ public void test0SpeedButUnblocked_Issue242(Fixture f) { public void testTwoWeightsPerEdge2(Fixture f) { // other direction should be different! Weighting fakeWeighting = new Weighting() { - private final Weighting tmpW = new FastestWeighting(f.carEncoder); + private final Weighting tmpW = new FastestWeighting(f.carAccessEnc, f.carSpeedEnc); @Override public double getMinWeight(double distance) { @@ -980,15 +988,15 @@ public String toString() { }; BaseGraph graph = f.createGHStorage(true); - initEleGraph(graph, f.carEncoder, 60); + initEleGraph(graph, 60, f.carAccessEnc, f.carSpeedEnc); Path p = f.calcPath(graph, 0, 10); assertEquals(nodes(0, 4, 6, 10), p.calcNodes()); graph = f.createGHStorage(true); - initEleGraph(graph, f.carEncoder, 60); + initEleGraph(graph, 60, f.carAccessEnc, f.carSpeedEnc); p = f.calcPath(graph, fakeWeighting, 3, 0, 10, 9); assertEquals(nodes(12, 0, 1, 2, 11, 7, 10, 13), p.calcNodes()); - assertEquals(37009621, p.getTime()); + assertEquals(37009625, p.getTime()); assertEquals(616827, p.getDistance(), 1); assertEquals(493462, p.getWeight(), 1); } @@ -997,14 +1005,14 @@ public String toString() { @ArgumentsSource(FixtureProvider.class) public void testRandomGraph(Fixture f) { // todo: use speed both directions - FastestWeighting fastestWeighting = new FastestWeighting(f.carEncoder); + FastestWeighting fastestWeighting = new FastestWeighting(f.carAccessEnc, f.carSpeedEnc); BaseGraph graph = f.createGHStorage(false); final long seed = System.nanoTime(); LOGGER.info("testRandomGraph - using seed: " + seed); Random rnd = new Random(seed); // we're not including loops otherwise duplicate nodes in path might fail the test GHUtility.buildRandomGraph(graph, rnd, 10, 2.0, false, true, - f.carEncoder.getAccessEnc(), f.carEncoder.getAverageSpeedEnc(), null, 0.7, 0.7, 0.7); + f.carAccessEnc, f.carSpeedEnc, null, 0.7, 0.7, 0.7); final PathCalculator refCalculator = new DijkstraCalculator(); int numRuns = 100; for (int i = 0; i < numRuns; i++) { @@ -1020,11 +1028,11 @@ public void testRandomGraph(Fixture f) { @ParameterizedTest @ArgumentsSource(FixtureProvider.class) public void testMultipleVehicles_issue548(Fixture f) { - FastestWeighting footWeighting = new FastestWeighting(f.footEncoder); - FastestWeighting carWeighting = new FastestWeighting(f.carEncoder); + FastestWeighting footWeighting = new FastestWeighting(f.footAccessEnc, f.footSpeedEnc); + FastestWeighting carWeighting = new FastestWeighting(f.carAccessEnc, f.carSpeedEnc); BaseGraph graph = f.createGHStorage(false); - initFootVsCar(f.carEncoder, f.footEncoder, graph); + initFootVsCar(f.carAccessEnc, f.carSpeedEnc, f.footAccessEnc, f.footSpeedEnc, graph); // normal path would be 0-4-6-7 for car: Path carPath1 = f.calcPath(graph, carWeighting, 0, 7); @@ -1038,8 +1046,8 @@ public void testMultipleVehicles_issue548(Fixture f) { // ... but now we block 4-6 for car. note that we have to recreate the storage to create a new directory so // we can create new CHs :( graph = f.createGHStorage(false); - initFootVsCar(f.carEncoder, f.footEncoder, graph); - GHUtility.setSpeed(20, false, false, f.carEncoder, GHUtility.getEdge(graph, 4, 6)); + initFootVsCar(f.carAccessEnc, f.carSpeedEnc, f.footAccessEnc, f.footSpeedEnc, graph); + GHUtility.setSpeed(20, false, false, f.carAccessEnc, f.carSpeedEnc, GHUtility.getEdge(graph, 4, 6)); f.resetCH(); // ... car needs to take another way @@ -1059,26 +1067,26 @@ public void testMultipleVehicles_issue548(Fixture f) { // 5-6-7 // | |\| // 8-9-10 - private void initEleGraph(Graph graph, FlagEncoder encoder, double s) { - GHUtility.setSpeed(s, true, true, encoder, graph.edge(0, 1).setDistance(10)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(0, 4).setDistance(12)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(0, 3).setDistance(5)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(1, 2).setDistance(10)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(1, 4).setDistance(5)); - GHUtility.setSpeed(s, true, false, encoder, graph.edge(3, 5).setDistance(5)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(5, 6).setDistance(10)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(5, 8).setDistance(10)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(6, 4).setDistance(5)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(6, 7).setDistance(10)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(6, 10).setDistance(12)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(6, 9).setDistance(12)); - GHUtility.setSpeed(s, true, false, encoder, graph.edge(2, 11).setDistance(5)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(4, 11).setDistance(10)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(7, 11).setDistance(5)); - GHUtility.setSpeed(s, true, true, encoder, graph.edge(7, 10).setDistance(5)); - GHUtility.setSpeed(s, true, false, encoder, graph.edge(8, 9).setDistance(10)); - GHUtility.setSpeed(s, true, false, encoder, graph.edge(9, 8).setDistance(9)); - GHUtility.setSpeed(s, true, false, encoder, graph.edge(10, 9).setDistance(10)); + private void initEleGraph(Graph graph, double s, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(0, 4).setDistance(12)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(0, 3).setDistance(5)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(5)); + GHUtility.setSpeed(s, true, false, accessEnc, speedEnc, graph.edge(3, 5).setDistance(5)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(10)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(5, 8).setDistance(10)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(6, 4).setDistance(5)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(10)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(6, 10).setDistance(12)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(6, 9).setDistance(12)); + GHUtility.setSpeed(s, true, false, accessEnc, speedEnc, graph.edge(2, 11).setDistance(5)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(4, 11).setDistance(10)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(7, 11).setDistance(5)); + GHUtility.setSpeed(s, true, true, accessEnc, speedEnc, graph.edge(7, 10).setDistance(5)); + GHUtility.setSpeed(s, true, false, accessEnc, speedEnc, graph.edge(8, 9).setDistance(10)); + GHUtility.setSpeed(s, true, false, accessEnc, speedEnc, graph.edge(9, 8).setDistance(9)); + GHUtility.setSpeed(s, true, false, accessEnc, speedEnc, graph.edge(10, 9).setDistance(10)); updateDistancesFor(graph, 0, 3, 0); updateDistancesFor(graph, 3, 2.5, 0); updateDistancesFor(graph, 5, 1, 0); diff --git a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java index ee6452af162..9291f778741 100644 --- a/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java +++ b/core/src/test/java/com/graphhopper/routing/RoutingAlgorithmWithOSMTest.java @@ -24,12 +24,11 @@ import com.graphhopper.config.CHProfile; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; +import com.graphhopper.jackson.Jackson; import com.graphhopper.reader.dem.SRTMProvider; +import com.graphhopper.routing.weighting.custom.CustomProfile; import com.graphhopper.storage.Graph; -import com.graphhopper.util.DistanceCalc; -import com.graphhopper.util.DistanceCalcEarth; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.Helper; +import com.graphhopper.util.*; import com.graphhopper.util.shapes.GHPoint; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -42,6 +41,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import static com.graphhopper.json.Statement.If; +import static com.graphhopper.json.Statement.Op.MULTIPLY; import static com.graphhopper.util.Parameters.Algorithms.*; import static org.junit.jupiter.api.Assertions.*; @@ -55,6 +56,7 @@ public class RoutingAlgorithmWithOSMTest { private static final String ANDORRA = DIR + "/andorra.osm.gz"; private static final String ANDORRA_PBF = DIR + "/andorra.osm.pbf"; private static final String BAYREUTH = DIR + "/north-bayreuth.osm.gz"; + private static final String HOHEWARTE = DIR + "/hohe-warte.osm.gz"; private static final String KREMS = DIR + "/krems.osm.gz"; private static final String MONACO = DIR + "/monaco.osm.gz"; private static final String MOSCOW = DIR + "/moscow.osm.gz"; @@ -74,15 +76,15 @@ public void testMonaco() { GraphHopper hopper = createHopper(MONACO, new Profile("car").setVehicle("car").setWeighting("shortest")); hopper.importOrLoad(); checkQueries(hopper, createMonacoCarQueries()); - Graph g = hopper.getGraphHopperStorage(); + Graph g = hopper.getBaseGraph(); // When OSM file stays unchanged make static edge and node IDs a requirement - assertEquals(GHUtility.asSet(9, 111, 182), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(10))); - assertEquals(GHUtility.asSet(19, 21), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(20))); - assertEquals(GHUtility.asSet(478, 84, 83), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(480))); + assertEquals(GHUtility.asSet(924, 576, 2), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(10))); + assertEquals(GHUtility.asSet(291, 369, 19), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(20))); + assertEquals(GHUtility.asSet(45, 497, 488), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(480))); - assertEquals(43.736989, g.getNodeAccess().getLat(10), 1e-6); - assertEquals(7.429758, g.getNodeAccess().getLon(201), 1e-6); + assertEquals(43.738776, g.getNodeAccess().getLat(10), 1e-6); + assertEquals(7.4170402, g.getNodeAccess().getLon(201), 1e-6); } private List createMonacoCarQueries() { @@ -123,14 +125,17 @@ public void testMonacoMotorcycle() { @Test public void testMonacoMotorcycleCurvature() { List queries = new ArrayList<>(); - queries.add(new Query(43.730729, 7.42135, 43.727697, 7.419199, 2681, 119)); + queries.add(new Query(43.730729, 7.42135, 43.727697, 7.419199, 2675, 117)); queries.add(new Query(43.727687, 7.418737, 43.74958, 7.436566, 3727, 170)); - queries.add(new Query(43.728677, 7.41016, 43.739213, 7.4277, 3168, 169)); + queries.add(new Query(43.728677, 7.41016, 43.739213, 7.4277, 3157, 165)); queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 2423, 141)); queries.add(new Query(43.730949, 7.412338, 43.739643, 7.424542, 2253, 120)); queries.add(new Query(43.727592, 7.419333, 43.727712, 7.419333, 0, 1)); - GraphHopper hopper = createHopper(MONACO, - new Profile("motorcycle").setVehicle("motorcycle").setWeighting("curvature")); + CustomModel model = new CustomModel().setDistanceInfluence(70d).addToPriority(If("true", MULTIPLY, "curvature")); + + GraphHopper hopper = createHopper(MONACO, new CustomProfile("motorcycle").setCustomModel(model). + setVehicle("motorcycle")); + hopper.setEncodedValuesString("curvature"); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -143,7 +148,7 @@ public void testBike2_issue432() { // reverse route avoids the location // list.add(new OneRun(52.349713, 8.013293, 52.349969, 8.013813, 293, 21)); GraphHopper hopper = createHopper(DIR + "/map-bug432.osm.gz", - new Profile("bike2").setVehicle("bike2").setWeighting("fastest")); + new CustomProfile("bike2").setCustomModel(new CustomModel()).setVehicle("bike")); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -216,6 +221,15 @@ public void testSimplePTurn() { checkQueries(hopper, list); } + static CustomModel getCustomModel(String file) { + try { + String string = Helper.readJSONFileWithoutComments(new File("../custom_models/", file).getAbsolutePath()); + return Jackson.newObjectMapper().readValue(string, CustomModel.class); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + @Test public void testSidewalkNo() { List queries = new ArrayList<>(); @@ -224,7 +238,7 @@ public void testSidewalkNo() { // longer path should go through tertiary, see discussion in #476 queries.add(new Query(57.154888, -2.101822, 57.147299, -2.096286, 1118, 68)); - Profile profile = new Profile("hike").setVehicle("hike").setWeighting("fastest"); + Profile profile = new Profile("hike").setWeighting("fastest").setVehicle("foot"); GraphHopper hopper = createHopper(DIR + "/map-sidewalk-no.osm.gz", profile); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -268,15 +282,15 @@ public void testMonacoFoot() { new Profile("foot").setVehicle("foot").setWeighting("shortest")); hopper.importOrLoad(); checkQueries(hopper, createMonacoFoot()); - Graph g = hopper.getGraphHopperStorage(); + Graph g = hopper.getBaseGraph(); // see testMonaco for a similar ID test - assertEquals(GHUtility.asSet(2, 909, 571), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(10))); - assertEquals(GHUtility.asSet(444, 956, 740), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(441))); - assertEquals(GHUtility.asSet(911, 404, 122, 914), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(912))); + assertEquals(GHUtility.asSet(924, 576, 2), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(10))); + assertEquals(GHUtility.asSet(440, 442), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(441))); + assertEquals(GHUtility.asSet(913, 914, 911), GHUtility.getNeighbors(g.createEdgeExplorer().setBaseNode(912))); - assertEquals(43.743705, g.getNodeAccess().getLat(100), 1e-6); - assertEquals(7.426362, g.getNodeAccess().getLon(702), 1e-6); + assertEquals(43.7467818, g.getNodeAccess().getLat(100), 1e-6); + assertEquals(7.4312824, g.getNodeAccess().getLon(702), 1e-6); } @Test @@ -310,35 +324,52 @@ private List createMonacoFoot() { public void testNorthBayreuthHikeFastestAnd3D() { List queries = new ArrayList<>(); // prefer hiking route 'Teufelsloch Unterwaiz' and 'Rotmain-Wanderweg' - queries.add(new Query(49.974972, 11.515657, 49.991022, 11.512299, 2384, 93)); + queries.add(new Query(49.974972, 11.515657, 49.991022, 11.512299, 2365, 67)); // prefer hiking route 'Markgrafenweg Bayreuth Kulmbach' but avoid tertiary highway from Pechgraben - queries.add(new Query(49.990967, 11.545258, 50.023182, 11.555386, 4746, 119)); - GraphHopper hopper = createHopper(BAYREUTH, new Profile("hike").setVehicle("hike").setWeighting("fastest")); + queries.add(new Query(49.990967, 11.545258, 50.023182, 11.555386, 5636, 97)); + GraphHopper hopper = createHopper(BAYREUTH, new CustomProfile("hike").setCustomModel(getCustomModel("hike.json")).setVehicle("roads")); + hopper.setVehiclesString("roads,foot"); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, queries); } @Test - public void testMonacoBike3D_twoSpeedsPerEdge() { + public void testHikeCanUseExtremeSacScales() { + GraphHopper hopper = createHopper(HOHEWARTE, new CustomProfile("hike").setCustomModel(getCustomModel("hike.json")).setVehicle("roads")); + hopper.setVehiclesString("foot,roads"); + // do not pull elevation data: hopper.setElevationProvider(new SRTMProvider(DIR)); + hopper.importOrLoad(); + GHResponse res = hopper.route(new GHRequest(47.290322, 11.333889, 47.301593, 11.333489).setProfile("hike")); + assertEquals(3604, res.getBest().getTime() / 1000.0, 60); // 6100sec with srtm data + assertEquals(2000, res.getBest().getDistance(), 10); // 2536m with srtm data + } + + @Test + public void testMonacoBike3D() { List queries = new ArrayList<>(); // 1. alternative: go over steps 'Rampe Major' => 1.7km vs. around 2.7km - queries.add(new Query(43.730864, 7.420771, 43.727687, 7.418737, 2689, 118)); + queries.add(new Query(43.730864, 7.420771, 43.727687, 7.418737, 1999, 101)); // 2. - queries.add(new Query(43.728499, 7.417907, 43.74958, 7.436566, 3735, 194)); + queries.add(new Query(43.728499, 7.417907, 43.74958, 7.436566, 3939, 187)); // 3. - queries.add(new Query(43.728677, 7.41016, 43.739213, 7.427806, 2776, 167)); + queries.add(new Query(43.728677, 7.41016, 43.739213, 7.427806, 2776, 163)); // 4. queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 1544, 84)); // try reverse direction // 1. queries.add(new Query(43.727687, 7.418737, 43.730864, 7.420771, 2599, 115)); - queries.add(new Query(43.74958, 7.436566, 43.728499, 7.417907, 4180, 165)); - queries.add(new Query(43.739213, 7.427806, 43.728677, 7.41016, 3244, 179)); + queries.add(new Query(43.74958, 7.436566, 43.728499, 7.417907, 4230, 201)); + queries.add(new Query(43.739213, 7.427806, 43.728677, 7.41016, 2870, 154)); // 4. avoid tunnel(s)! - queries.add(new Query(43.739662, 7.424355, 43.733802, 7.413433, 2436, 112)); - GraphHopper hopper = createHopper(MONACO, new Profile("bike2").setVehicle("bike2").setWeighting("fastest")); + queries.add(new Query(43.739662, 7.424355, 43.733802, 7.413433, 1795, 96)); + // atm custom model is intended to be used with 'roads' vehicle when allowing reverse direction for oneways + // but tests here still assert that reverse oneways are excluded + GraphHopper hopper = createHopper(MONACO, + new CustomProfile("bike").setCustomModel(getCustomModel("bike.json"). + addToPriority(If("!bike_access", MULTIPLY, "0"))).setVehicle("roads")); + hopper.setVehiclesString("roads,bike"); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -379,7 +410,8 @@ public void testMonacoBike() { queries.add(new Query(43.727687, 7.418737, 43.74958, 7.436566, 3580, 168)); queries.add(new Query(43.728677, 7.41016, 43.739213, 7.427806, 2323, 121)); queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 1434, 89)); - GraphHopper hopper = createHopper(MONACO, new Profile("bike").setVehicle("bike").setWeighting("shortest")); + GraphHopper hopper = createHopper(MONACO, new CustomProfile("bike"). + setCustomModel(new CustomModel().setDistanceInfluence(7000d)/*shortest*/).setVehicle("bike")); hopper.importOrLoad(); checkQueries(hopper, queries); } @@ -387,20 +419,21 @@ public void testMonacoBike() { @Test public void testMonacoMountainBike() { List queries = new ArrayList<>(); - queries.add(new Query(43.730864, 7.420771, 43.727687, 7.418737, 2593, 110)); + // for mtb it is also ok to go over steps (43.7318,7.423) -> 1900m vs 2600m (in latest OSM data all bikes are forbidden and steps aren't taken) + queries.add(new Query(43.730864, 7.420771, 43.727687, 7.418737, 2594, 111)); queries.add(new Query(43.727687, 7.418737, 43.74958, 7.436566, 3655, 176)); queries.add(new Query(43.728677, 7.41016, 43.739213, 7.427806, 2331, 121)); // hard to select between secondary and primary (both are AVOID for mtb) queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 1459, 88)); - GraphHopper hopper = createHopper(MONACO, new Profile("mtb").setVehicle("mtb").setWeighting("fastest")); + GraphHopper hopper = createHopper(MONACO, new CustomProfile("mtb").setCustomModel(new CustomModel()).setVehicle("mtb")); hopper.importOrLoad(); checkQueries(hopper, queries); Helper.removeDir(new File(GH_LOCATION)); hopper = createHopper(MONACO, - new Profile("mtb").setVehicle("mtb").setWeighting("fastest"), + new CustomProfile("mtb").setCustomModel(new CustomModel()).setVehicle("mtb"), new Profile("racingbike").setVehicle("racingbike").setWeighting("fastest")); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -409,20 +442,19 @@ public void testMonacoMountainBike() { @Test public void testMonacoRacingBike() { List queries = new ArrayList<>(); - queries.add(new Query(43.730864, 7.420771, 43.727687, 7.418737, 2594, 111)); - queries.add(new Query(43.727687, 7.418737, 43.74958, 7.436566, 3614, 184)); - queries.add(new Query(43.728677, 7.41016, 43.739213, 7.427806, 2572, 135)); + queries.add(new Query(43.730864, 7.420771, 43.727687, 7.418737, 2597, 118)); + queries.add(new Query(43.727687, 7.418737, 43.74958, 7.436566, 3589, 170)); + queries.add(new Query(43.728677, 7.41016, 43.739213, 7.427806, 2568, 135)); queries.add(new Query(43.733802, 7.413433, 43.739662, 7.424355, 1490, 84)); - GraphHopper hopper = createHopper(MONACO, - new Profile("racingbike").setVehicle("racingbike").setWeighting("fastest")); + GraphHopper hopper = createHopper(MONACO, new CustomProfile("racingbike"). + setCustomModel(new CustomModel()).setVehicle("racingbike")); hopper.importOrLoad(); checkQueries(hopper, queries); Helper.removeDir(new File(GH_LOCATION)); - hopper = createHopper(MONACO, - new Profile("racingbike").setVehicle("racingbike").setWeighting("fastest"), + hopper = createHopper(MONACO, new CustomProfile("racingbike").setCustomModel(new CustomModel()).setVehicle("racingbike"), new Profile("bike").setVehicle("bike").setWeighting("fastest") ); hopper.importOrLoad(); @@ -437,15 +469,17 @@ public void testKremsBikeRelation() { queries.add(new Query(48.412294, 15.62007, 48.398306, 15.609667, 3965, 94)); GraphHopper hopper = createHopper(KREMS, - new Profile("bike").setVehicle("bike").setWeighting("fastest")); + new CustomProfile("bike"). + setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("bike")); hopper.importOrLoad(); checkQueries(hopper, queries); - hopper.getGraphHopperStorage(); + hopper.getBaseGraph(); Helper.removeDir(new File(GH_LOCATION)); hopper = createHopper(KREMS, - new Profile("bike").setVehicle("bike").setWeighting("fastest"), + new CustomProfile("bike"). + setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("bike"), new Profile("car").setVehicle("car").setWeighting("fastest")); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -458,15 +492,16 @@ public void testKremsMountainBikeRelation() { queries.add(new Query(48.410061, 15.63951, 48.411386, 15.604899, 3101, 94)); queries.add(new Query(48.412294, 15.62007, 48.398306, 15.609667, 3965, 95)); - GraphHopper hopper = createHopper(KREMS, - new Profile("mtb").setVehicle("mtb").setWeighting("fastest")); + GraphHopper hopper = createHopper(KREMS, new CustomProfile("mtb"). + setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("mtb")); hopper.importOrLoad(); checkQueries(hopper, queries); Helper.removeDir(new File(GH_LOCATION)); hopper = createHopper(KREMS, - new Profile("mtb").setVehicle("mtb").setWeighting("fastest"), + new CustomProfile("mtb"). + setCustomModel(new CustomModel().setDistanceInfluence(70d)).setVehicle("mtb"), new Profile("bike").setVehicle("bike").setWeighting("fastest")); hopper.importOrLoad(); checkQueries(hopper, queries); @@ -568,7 +603,8 @@ public void testNeudrossenfeld() { Helper.removeDir(new File(GH_LOCATION)); - hopper = createHopper(BAYREUTH, new Profile("bike2").setVehicle("bike2").setWeighting("fastest")); + CustomModel model = new CustomModel(); + hopper = createHopper(BAYREUTH, new CustomProfile("bike2").setCustomModel(model).setVehicle("bike")); hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); checkQueries(hopper, list); @@ -584,7 +620,6 @@ public void testDisconnectedAreaAndMultiplePoints() { GraphHopper hopper = createHopper(DIR + "/krautsand.osm.gz", new Profile("car").setVehicle("car").setWeighting("fastest")); - hopper.setElevationProvider(new SRTMProvider(DIR)); hopper.importOrLoad(); for (Function requestFactory : createRequestFactories()) { @@ -681,9 +716,10 @@ public String toString() { */ private GraphHopper createHopper(String osmFile, Profile... profiles) { GraphHopper hopper = new GraphHopper(). - setStoreOnFlush(true). + setStoreOnFlush(false). setOSMFile(osmFile). setProfiles(profiles). + setEncodedValuesString("average_slope,max_slope,hike_rating"). setGraphHopperLocation(GH_LOCATION); hopper.getRouterConfig().setSimplifyResponse(false); hopper.setMinNetworkSize(0); diff --git a/core/src/test/java/com/graphhopper/routing/RoutingCHGraphImplTest.java b/core/src/test/java/com/graphhopper/routing/RoutingCHGraphImplTest.java index 68147065930..0d07bb0a463 100644 --- a/core/src/test/java/com/graphhopper/routing/RoutingCHGraphImplTest.java +++ b/core/src/test/java/com/graphhopper/routing/RoutingCHGraphImplTest.java @@ -19,10 +19,12 @@ package com.graphhopper.routing; import com.graphhopper.routing.ch.PrepareEncoder; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.AccessFilter; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.storage.*; import com.graphhopper.util.EdgeExplorer; @@ -36,14 +38,15 @@ public class RoutingCHGraphImplTest { @Test public void testBaseAndCHEdges() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(carEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); graph.edge(1, 0); graph.edge(8, 9); graph.freeze(); - CHConfig chConfig = CHConfig.nodeBased("p", new FastestWeighting(carEncoder)); + CHConfig chConfig = CHConfig.nodeBased("p", new FastestWeighting(accessEnc, speedEnc)); CHStorage store = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(store); chBuilder.setIdentityLevels(); @@ -74,14 +77,15 @@ void testShortcutConnection() { // 4 ------ 1 > 0 // ^ \ // 3 2 - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - EdgeExplorer baseCarOutExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(encoder.getAccessEnc())); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 1).setDistance(30)); + EdgeExplorer baseCarOutExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(accessEnc)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 1).setDistance(30)); graph.freeze(); - CHConfig chConfig = CHConfig.nodeBased("ch", new FastestWeighting(encoder)); + CHConfig chConfig = CHConfig.nodeBased("ch", new FastestWeighting(accessEnc, speedEnc)); CHStorage store = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(store); chBuilder.setIdentityLevels(); @@ -111,14 +115,15 @@ void testShortcutConnection() { @Test public void testGetWeight() { - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); EdgeIteratorState edge1 = graph.edge(0, 1); EdgeIteratorState edge2 = graph.edge(1, 2); graph.freeze(); - CHConfig chConfig = CHConfig.nodeBased("ch", new FastestWeighting(encoder)); + CHConfig chConfig = CHConfig.nodeBased("ch", new FastestWeighting(accessEnc, speedEnc)); CHStorage store = CHStorage.fromGraph(graph, chConfig); RoutingCHGraph g = RoutingCHGraphImpl.fromGraph(graph, store, chConfig); assertFalse(g.getEdgeIteratorState(edge1.getEdge(), Integer.MIN_VALUE).isShortcut()); @@ -138,13 +143,14 @@ public void testGetWeight() { @Test public void testGetWeightIfAdvancedEncoder() { - FlagEncoder customEncoder = FlagEncoders.createBike2(); - EncodingManager em = EncodingManager.create(customEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, true); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph ghStorage = new BaseGraph.Builder(em).create(); ghStorage.edge(0, 3); ghStorage.freeze(); - FastestWeighting weighting = new FastestWeighting(customEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(ghStorage, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -165,14 +171,15 @@ public void testGetWeightIfAdvancedEncoder() { @Test public void testWeightExact() { - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); graph.freeze(); - CHConfig chConfig = CHConfig.nodeBased("ch", new FastestWeighting(encoder)); + CHConfig chConfig = CHConfig.nodeBased("ch", new FastestWeighting(accessEnc, speedEnc)); CHStorage store = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(store); chBuilder.setIdentityLevels(); @@ -189,15 +196,16 @@ public void testWeightExact() { @Test public void testSimpleShortcutCreationAndTraversal() { - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); graph.freeze(); - FastestWeighting weighting = new FastestWeighting(encoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -213,14 +221,15 @@ public void testSimpleShortcutCreationAndTraversal() { @Test public void testAddShortcutSkippedEdgesWriteRead() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(carEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 3).setDistance(10)); - final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 4).setDistance(10)); + final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(10)); + final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); graph.freeze(); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -234,14 +243,15 @@ public void testAddShortcutSkippedEdgesWriteRead() { @Test public void testSkippedEdges() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(carEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 3).setDistance(10)); - final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 4).setDistance(10)); + final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(10)); + final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); graph.freeze(); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); @@ -253,49 +263,52 @@ public void testSkippedEdges() { @Test public void testAddShortcut_edgeBased_throwsIfNotConfiguredForEdgeBased() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(carEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); graph.freeze(); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); - assertThrows(IllegalArgumentException.class, () -> chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 10, 0, 1, 0, 1)); + assertThrows(IllegalArgumentException.class, () -> chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 10, 0, 1, 0, 2)); } @Test public void testAddShortcut_edgeBased() { // 0 -> 1 -> 2 - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(carEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).set3D(true).create(); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(1, 2).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(3)); graph.freeze(); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.edgeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); chBuilder.setIdentityLevels(); - chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 10, 0, 1, 0, 1); - assertEquals(0, chStore.getOrigEdgeFirst(chStore.toShortcutPointer(0))); - assertEquals(1, chStore.getOrigEdgeLast(chStore.toShortcutPointer(0))); + chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 10, 0, 1, 0, 2); + assertEquals(0, chStore.getOrigEdgeKeyFirst(chStore.toShortcutPointer(0))); + assertEquals(2, chStore.getOrigEdgeKeyLast(chStore.toShortcutPointer(0))); } @Test public void outOfBounds() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(carEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).set3D(true).create(); graph.freeze(); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); RoutingCHGraph lg = RoutingCHGraphImpl.fromGraph(graph, chStore, chConfig); @@ -304,19 +317,20 @@ public void outOfBounds() { @Test public void testGetEdgeIterator() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(carEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).set3D(true).create(); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); graph.freeze(); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.edgeBased("p1", weighting); CHStorage store = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(store); chBuilder.setIdentityLevels(); - chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 10, 0, 1, 0, 1); + chBuilder.addShortcutEdgeBased(0, 2, PrepareEncoder.getScFwdDir(), 10, 0, 1, 0, 2); RoutingCHGraph lg = RoutingCHGraphImpl.fromGraph(graph, store, chConfig); @@ -327,8 +341,8 @@ public void testGetEdgeIterator() { assertEquals(2, sc02.getEdge()); assertEquals(0, sc02.getSkippedEdge1()); assertEquals(1, sc02.getSkippedEdge2()); - assertEquals(0, sc02.getOrigEdgeFirst()); - assertEquals(1, sc02.getOrigEdgeLast()); + assertEquals(0, sc02.getOrigEdgeKeyFirst()); + assertEquals(2, sc02.getOrigEdgeKeyLast()); RoutingCHEdgeIteratorState sc20 = lg.getEdgeIteratorState(2, 0); assertNotNull(sc20); @@ -339,7 +353,7 @@ public void testGetEdgeIterator() { // is still edge 0 and the second skipped/last orig edge is edge 1 assertEquals(0, sc20.getSkippedEdge1()); assertEquals(1, sc20.getSkippedEdge2()); - assertEquals(0, sc20.getOrigEdgeFirst()); - assertEquals(1, sc20.getOrigEdgeLast()); + assertEquals(0, sc20.getOrigEdgeKeyFirst()); + assertEquals(2, sc20.getOrigEdgeKeyLast()); } } diff --git a/core/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java b/core/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java index e7af18a99ee..f327f52475d 100644 --- a/core/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java +++ b/core/src/test/java/com/graphhopper/routing/TrafficChangeWithNodeOrderingReusingTest.java @@ -3,12 +3,17 @@ import com.graphhopper.reader.osm.OSMReader; import com.graphhopper.routing.ch.CHRoutingAlgorithmFactory; import com.graphhopper.routing.ch.PrepareContractionHierarchies; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; -import com.graphhopper.routing.util.TagParserManager; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.OSMParsers; import com.graphhopper.routing.util.TraversalMode; +import com.graphhopper.routing.util.parsers.CarAverageSpeedParser; import com.graphhopper.routing.weighting.AbstractWeighting; import com.graphhopper.routing.weighting.FastestWeighting; +import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.*; import com.graphhopper.util.EdgeIteratorState; @@ -45,17 +50,22 @@ public class TrafficChangeWithNodeOrderingReusingTest { private static class Fixture { private final int maxDeviationPercentage; private final BaseGraph graph; - private final TagParserManager em; + private final EncodingManager em; + private final OSMParsers osmParsers; private final CHConfig baseCHConfig; private final CHConfig trafficCHConfig; public Fixture(int maxDeviationPercentage) { this.maxDeviationPercentage = maxDeviationPercentage; - FlagEncoder encoder = FlagEncoders.createCar(); - em = TagParserManager.create(encoder); - baseCHConfig = CHConfig.nodeBased("base", new FastestWeighting(encoder)); - trafficCHConfig = CHConfig.nodeBased("traffic", new RandomDeviationWeighting(baseCHConfig.getWeighting(), encoder, maxDeviationPercentage)); - graph = new BaseGraph.Builder(em.getEncodingManager()).create(); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("car", 5, 5, false); + em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + CarAverageSpeedParser carParser = new CarAverageSpeedParser(em, new PMap()); + osmParsers = new OSMParsers() + .addWayTagParser(carParser); + baseCHConfig = CHConfig.nodeBased("base", new FastestWeighting(accessEnc, speedEnc)); + trafficCHConfig = CHConfig.nodeBased("traffic", new RandomDeviationWeighting(baseCHConfig.getWeighting(), accessEnc, speedEnc, maxDeviationPercentage)); + graph = new BaseGraph.Builder(em).create(); } @Override @@ -85,7 +95,7 @@ public void testPerformanceForRandomTrafficChange(Fixture f) throws IOException LOGGER.info("Running performance test, max deviation percentage: " + f.maxDeviationPercentage); // read osm - OSMReader reader = new OSMReader(f.graph, f.em, new OSMReaderConfig()); + OSMReader reader = new OSMReader(f.graph, f.osmParsers, new OSMReaderConfig()); reader.setFile(new File(OSM_FILE)); reader.readGraph(); f.graph.freeze(); @@ -189,8 +199,8 @@ private static class RandomDeviationWeighting extends AbstractWeighting { private final Weighting baseWeighting; private final double maxDeviationPercentage; - public RandomDeviationWeighting(Weighting baseWeighting, FlagEncoder encoder, double maxDeviationPercentage) { - super(encoder); + public RandomDeviationWeighting(Weighting baseWeighting, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, double maxDeviationPercentage) { + super(accessEnc, speedEnc, TurnCostProvider.NO_TURN_COST_PROVIDER); this.baseWeighting = baseWeighting; this.maxDeviationPercentage = maxDeviationPercentage; } diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHPreparationGraphTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHPreparationGraphTest.java index 9a4fe0b086c..a23638ed5ee 100644 --- a/core/src/test/java/com/graphhopper/routing/ch/CHPreparationGraphTest.java +++ b/core/src/test/java/com/graphhopper/routing/ch/CHPreparationGraphTest.java @@ -18,9 +18,10 @@ package com.graphhopper.routing.ch; +import com.graphhopper.util.GHUtility; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; /** * So far there aren't many tests but the graph should be very easy to test as it has basically no dependencies and @@ -47,10 +48,30 @@ void basic() { pg.addShortcut(3, 4, 1, 3, 1, 3, 16, 2); pg.disconnect(0); PrepareGraphEdgeIterator iter = pg.createOutEdgeExplorer().setBaseNode(3); - String res = ""; + StringBuilder res = new StringBuilder(); while (iter.next()) { - res += iter.toString() + ","; + res.append(iter).append(","); } - assertEquals("3-4,", res); + assertEquals("3-4 16.0,", res.toString()); + } + + @Test + void useLargeEdgeId() { + CHPreparationGraph.OrigGraph.Builder builder = new CHPreparationGraph.OrigGraph.Builder(); + int largeEdgeID = Integer.MAX_VALUE >> 2; + assertEquals(536_870_911, largeEdgeID); + // 0->1 + builder.addEdge(0, 1, largeEdgeID, true, false); + CHPreparationGraph.OrigGraph g = builder.build(); + PrepareGraphOrigEdgeIterator iter = g.createOutOrigEdgeExplorer().setBaseNode(0); + assertTrue(iter.next()); + assertEquals(largeEdgeID, GHUtility.getEdgeFromEdgeKey(iter.getOrigEdgeKeyFirst())); + iter = g.createInOrigEdgeExplorer().setBaseNode(0); + assertFalse(iter.next()); + + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> + new CHPreparationGraph.OrigGraph.Builder().addEdge(0, 1, largeEdgeID + 1, true, false) + ); + assertTrue(e.getMessage().contains("Maximum edge key exceeded: 1073741824, max: 1073741823"), e.getMessage()); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java deleted file mode 100644 index e32d2d269ef..00000000000 --- a/core/src/test/java/com/graphhopper/routing/ch/CHProfileSelectorTest.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.graphhopper.routing.ch; - -import com.graphhopper.config.CHProfile; -import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; -import com.graphhopper.routing.ProfileResolver; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; -import com.graphhopper.util.PMap; -import com.graphhopper.util.Parameters; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -public class CHProfileSelectorTest { - - private static final String MULTIPLE_MATCHES_ERROR = "There are multiple CH profiles matching your request. Use the `weighting`,`vehicle`,`turn_costs` and/or `u_turn_costs` parameters to be more specific"; - private static final String NO_MATCH_ERROR = "Cannot find matching profile that supports CH for your request"; - - private Profile fastCar; - private Profile fastCarEdge; - private Profile fastCarEdge10; - private Profile fastCarEdge30; - private Profile fastCarEdge50; - private Profile fastBike; - private Profile fastBikeEdge40; - private Profile shortCar; - private Profile shortBike; - private EncodingManager encodingManager; - - @BeforeEach - public void setup() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - FlagEncoder bikeEncoder = FlagEncoders.createBike(); - encodingManager = EncodingManager.create(carEncoder, bikeEncoder); - fastCar = new Profile("fast_car").setWeighting("fastest").setVehicle("car").setTurnCosts(false); - fastCarEdge = new Profile("fast_car_edge").setWeighting("fastest").setVehicle("car").setTurnCosts(true); - fastCarEdge10 = new Profile("fast_car_edge10").setWeighting("fastest").setVehicle("car").setTurnCosts(true).putHint(Parameters.Routing.U_TURN_COSTS, 10); - fastCarEdge30 = new Profile("fast_car_edge30").setWeighting("fastest").setVehicle("car").setTurnCosts(true).putHint(Parameters.Routing.U_TURN_COSTS, 30); - fastCarEdge50 = new Profile("fast_car_edge50").setWeighting("fastest").setVehicle("car").setTurnCosts(true).putHint(Parameters.Routing.U_TURN_COSTS, 50); - fastBike = new Profile("fast_bike").setWeighting("fastest").setVehicle("bike").setTurnCosts(false); - fastBikeEdge40 = new Profile("fast_bike_edge40").setWeighting("fastest").setVehicle("bike").setTurnCosts(true).putHint(Parameters.Routing.U_TURN_COSTS, 40); - shortCar = new Profile("short_car").setWeighting("shortest").setVehicle("car").setTurnCosts(false); - shortBike = new Profile("short_bike").setWeighting("shortest").setVehicle("bike").setTurnCosts(false); - } - - @Test - public void onlyNodeBasedPresent() { - List profiles = Collections.singletonList( - fastCar - ); - List chProfiles = Collections.singletonList( - new CHProfile("fast_car") - ); - assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, true, null); - assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, true, 20); - assertProfileFound(profiles.get(0), profiles, chProfiles, false, null); - assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, false, 20); - assertProfileFound(profiles.get(0), profiles, chProfiles, null, null); - assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, null, 30); - String error = assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, "foot", "fastest", false, null); - assertTrue(error.contains("requested: fastest|foot|turn_costs=false|u_turn_costs=*"), error); - assertTrue(error.contains("available: [fastest|car|turn_costs=false]"), error); - } - - @Test - public void onlyEdgeBasedPresent() { - List profiles = Collections.singletonList(fastCarEdge); - List chProfiles = Collections.singletonList(new CHProfile("fast_car_edge")); - assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, false, null); - assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, false, 20); - assertProfileFound(profiles.get(0), profiles, chProfiles, true, null); - assertProfileFound(profiles.get(0), profiles, chProfiles, null, null); - } - - @Test - public void edgeAndNodePresent() { - List profiles = Arrays.asList( - fastCar, - fastCarEdge - ); - List chProfiles = Arrays.asList( - new CHProfile("fast_car"), - new CHProfile("fast_car_edge") - ); - // in case edge-based is not specified we prefer the edge-based profile over the node-based one - assertProfileFound(profiles.get(1), profiles, chProfiles, null, null); - assertProfileFound(profiles.get(0), profiles, chProfiles, false, null); - assertProfileFound(profiles.get(1), profiles, chProfiles, true, null); - } - - @Test - public void multipleEdgeBased() { - List profiles = Arrays.asList( - fastCar, - fastCarEdge30, - fastCarEdge50 - ); - List chProfiles = Arrays.asList( - new CHProfile("fast_car"), - new CHProfile("fast_car_edge30"), - new CHProfile("fast_car_edge50") - ); - // when no u-turns are specified we throw - assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, chProfiles, true, null); - // when we request one that does not exist we throw - assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, true, 40); - // when we request one that exists it works - assertProfileFound(profiles.get(1), profiles, chProfiles, true, 30); - - // without specifying edge-based we also get an error - assertProfileFound(profiles.get(1), profiles, chProfiles, null, 30); - assertCHProfileSelectionError(NO_MATCH_ERROR, profiles, chProfiles, null, 40); - String error = assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, chProfiles, null, null); - assertTrue(error.contains("requested: fastest|car|turn_costs=*|u_turn_costs=*"), error); - assertTrue(error.contains("matched: [fastest|car|turn_costs=false, fastest|car|turn_costs=true|u_turn_costs=30, fastest|car|turn_costs=true|u_turn_costs=50]"), error); - assertTrue(error.contains("available: [fastest|car|turn_costs=false, fastest|car|turn_costs=true|u_turn_costs=30, fastest|car|turn_costs=true|u_turn_costs=50]"), error); - } - - @Test - public void missingVehicleOrWeighting() { - // when we do not set the weighting and/or the car but it can be derived from the profile the profile is returned - List profiles = Collections.singletonList(fastCar); - List chProfiles = Collections.singletonList(new CHProfile("fast_car")); - assertProfileFound(profiles.get(0), profiles, chProfiles, "car", "fastest", null, null); - assertProfileFound(profiles.get(0), profiles, chProfiles, "car", null, null, null); - assertProfileFound(profiles.get(0), profiles, chProfiles, null, "fastest", null, null); - assertProfileFound(profiles.get(0), profiles, chProfiles, null, null, null, null); - } - - @Test - public void missingVehicleOrWeighting_otherVehicleAndCar() { - List profiles = Collections.singletonList(shortBike); - List chProfiles = Collections.singletonList(new CHProfile("short_bike")); - assertProfileFound(profiles.get(0), profiles, chProfiles, "bike", "shortest", null, null); - assertProfileFound(profiles.get(0), profiles, chProfiles, "bike", null, null, null); - assertProfileFound(profiles.get(0), profiles, chProfiles, null, "shortest", null, null); - assertProfileFound(profiles.get(0), profiles, chProfiles, null, null, null, null); - } - - - @Test - public void missingVehicleMultipleProfiles() { - List profiles = Arrays.asList( - fastBike, - shortBike, - fastBikeEdge40 - ); - List chProfiles = Arrays.asList( - new CHProfile("fast_bike"), - new CHProfile("short_bike"), - new CHProfile("fast_bike_edge40") - ); - // the vehicle is not given but only bike is used so its fine. note that we prefer edge-based because no edge_based parameter is specified - assertProfileFound(profiles.get(2), profiles, chProfiles, null, "fastest", null, null); - // if we do not specify the weighting its not clear what to return -> there is an error - assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, chProfiles, null, null, null, null); - // if we do not specify the weighting but edge_based=true its clear what to return because for edge-based there is only one weighting - assertProfileFound(profiles.get(2), profiles, chProfiles, null, null, true, null); - // ... for edge_based=false this is an error because there are two node-based profiles - assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, chProfiles, null, null, false, null); - } - - @Test - public void missingWeightingMultipleProfiles() { - List profiles = Arrays.asList( - fastBike, - fastCarEdge10, - fastBikeEdge40 - ); - List chProfiles = Arrays.asList( - new CHProfile("fast_bike"), - new CHProfile("fast_car_edge10"), - new CHProfile("fast_bike_edge40") - ); - // the weighting is not given but only fastest is used so its fine. note that we prefer edge-based because no edge_based parameter is specified - assertProfileFound(profiles.get(2), profiles, chProfiles, "bike", null, null, null); - // if we do not specify the vehicle its not clear what to return -> there is an error - assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, chProfiles, null, null, null, null); - // if we do not specify the vehicle but edge_based=false its clear what to return because for node-based there is only one weighting - assertProfileFound(profiles.get(0), profiles, chProfiles, null, null, false, null); - // ... for edge_based=true this is an error, because there are two edge_based profiles - assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, chProfiles, null, null, true, null); - // ... we can however get a clear match if we specify the u-turn costs - assertProfileFound(profiles.get(1), profiles, chProfiles, null, null, true, 10); - } - - @Test - public void multipleVehiclesMissingWeighting() { - // this is a common use-case, there are multiple vehicles for one weighting - List profiles = new ArrayList<>(); - List chProfiles = new ArrayList<>(); - for (String vehicle : Arrays.asList("car", "bike", "motorcycle", "bike2", "foot")) { - profiles.add(new Profile(vehicle).setVehicle(vehicle).setWeighting("short_fastest").setTurnCosts(false)); - chProfiles.add(new CHProfile(vehicle)); - } - // we do not specify the weighting but this is ok, because there is only one in use - String weighting = null; - assertProfileFound(profiles.get(0), profiles, chProfiles, "car", weighting, null, null); - assertProfileFound(profiles.get(1), profiles, chProfiles, "bike", weighting, null, null); - assertProfileFound(profiles.get(2), profiles, chProfiles, "motorcycle", weighting, null, null); - assertProfileFound(profiles.get(3), profiles, chProfiles, "bike2", weighting, null, null); - assertProfileFound(profiles.get(4), profiles, chProfiles, "foot", weighting, null, null); - } - - - @Test - public void missingVehicle_multiplePossibilities_throws() { - List profiles = Arrays.asList( - fastBike, - fastCar - ); - List chProfiles = Arrays.asList( - new CHProfile("fast_bike"), - new CHProfile("fast_car") - ); - assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, chProfiles, null, "fastest", null, null); - } - - @Test - public void missingWeighting_multiplePossibilities_throws() { - List profiles = Arrays.asList( - fastBike, - shortBike - ); - List chProfiles = Arrays.asList( - new CHProfile("fast_bike"), - new CHProfile("short_bike") - ); - assertCHProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, chProfiles, "bike", null, null, null); - } - - private void assertProfileFound(Profile expectedProfile, List profiles, List chProfiles, Boolean edgeBased, Integer uTurnCosts) { - assertProfileFound(expectedProfile, profiles, chProfiles, "car", "fastest", edgeBased, uTurnCosts); - } - - private void assertProfileFound(Profile expectedProfile, List profiles, List chProfiles, String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) { - PMap hintsMap = createHintsMap(vehicle, weighting, edgeBased, uTurnCosts); - try { - Profile selectedProfile = new ProfileResolver(encodingManager, profiles, chProfiles, Collections.emptyList()).selectProfileCH(hintsMap); - assertEquals(expectedProfile, selectedProfile); - } catch (IllegalArgumentException e) { - fail("no profile found\nexpected: " + expectedProfile + "\nerror: " + e.getMessage()); - } - } - - private String assertCHProfileSelectionError(String expectedError, List profiles, List chProfiles, Boolean edgeBased, Integer uTurnCosts) { - return assertCHProfileSelectionError(expectedError, profiles, chProfiles, "car", "fastest", edgeBased, uTurnCosts); - } - - private String assertCHProfileSelectionError(String expectedError, List profiles, List chProfiles, String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) { - PMap hintsMap = createHintsMap(vehicle, weighting, edgeBased, uTurnCosts); - try { - new ProfileResolver(encodingManager, profiles, chProfiles, Collections.emptyList()).selectProfileCH(hintsMap); - fail("There should have been an error"); - return ""; - } catch (IllegalArgumentException e) { - assertTrue(e.getMessage().contains(expectedError), - "There should have been an error message containing:\n'" + expectedError + "'\nbut was:\n'" + e.getMessage() + "'"); - return e.getMessage(); - } - } - - private PMap createHintsMap(String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) { - PMap hintsMap = new PMap(); - if (weighting != null) - hintsMap.putObject("weighting", weighting); - if (vehicle != null) - hintsMap.putObject("vehicle", vehicle); - if (edgeBased != null) - hintsMap.putObject(Parameters.Routing.EDGE_BASED, edgeBased); - if (uTurnCosts != null) - hintsMap.putObject(Parameters.Routing.U_TURN_COSTS, uTurnCosts); - return hintsMap; - } -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java index 6eb462ae6a4..d6d28e18429 100644 --- a/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java +++ b/core/src/test/java/com/graphhopper/routing/ch/CHTurnCostTest.java @@ -22,13 +22,13 @@ import com.graphhopper.routing.DijkstraBidirectionEdgeCHNoSOD; import com.graphhopper.routing.Path; import com.graphhopper.routing.RoutingAlgorithm; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.querygraph.QueryRoutingCHGraph; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.routing.weighting.Weighting; @@ -68,7 +68,9 @@ public class CHTurnCostTest { private static final Logger LOGGER = LoggerFactory.getLogger(CHTurnCostTest.class); private int maxCost; - private FlagEncoder encoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; + private DecimalEncodedValue turnCostEnc; private EncodingManager encodingManager; private BaseGraph graph; private TurnCostStorage turnCostStorage; @@ -80,9 +82,11 @@ public class CHTurnCostTest { @BeforeEach public void init() { maxCost = 10; - encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", maxCost)); - encodingManager = EncodingManager.create(encoder); - graph = new BaseGraph.Builder(encodingManager).build(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + turnCostEnc = TurnCost.create("car", maxCost); + encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).build(); turnCostStorage = graph.getTurnCostStorage(); chConfigs = createCHConfigs(); // the default CH profile with infinite u-turn costs, can be reset in tests that should run with finite u-turn @@ -98,17 +102,17 @@ public void init() { private List createCHConfigs() { Set configs = new LinkedHashSet<>(5); // the first one is always the one with infinite u-turn costs - configs.add(CHConfig.edgeBased("p0", new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, INFINITE_U_TURN_COSTS)))); + configs.add(CHConfig.edgeBased("p0", new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, turnCostStorage, INFINITE_U_TURN_COSTS)))); // this one we also always add - configs.add(CHConfig.edgeBased("p1", new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, 0)))); + configs.add(CHConfig.edgeBased("p1", new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, turnCostStorage, 0)))); // ... and this one - configs.add(CHConfig.edgeBased("p2", new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, 50)))); + configs.add(CHConfig.edgeBased("p2", new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, turnCostStorage, 50)))); // add more (distinct) profiles long seed = System.nanoTime(); Random rnd = new Random(seed); while (configs.size() < 6) { int uTurnCosts = 10 + rnd.nextInt(90); - configs.add(CHConfig.edgeBased("p" + configs.size(), new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, turnCostStorage, uTurnCosts)))); + configs.add(CHConfig.edgeBased("p" + configs.size(), new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, turnCostStorage, uTurnCosts)))); } return new ArrayList<>(configs); } @@ -116,10 +120,10 @@ private List createCHConfigs() { @RepeatedTest(10) public void testFindPath_randomContractionOrder_linear() { // 2-1-0-3-4 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 1).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 0).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 1).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 0).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(3)); graph.freeze(); setTurnCost(2, 1, 0, 2); setTurnCost(0, 3, 4, 4); @@ -131,11 +135,11 @@ public void testFindPath_randomContractionOrder_duplicate_edges() { // /\ /<-3 // 0 1--2 // \/ \->4 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(6)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(6)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(3)); setRestriction(3, 2, 4); graph.freeze(); compareCHWithDijkstra(10, new int[]{0, 1, 2, 3, 4}); @@ -146,11 +150,11 @@ public void testFindPath_randomContractionOrder_double_duplicate_edges() { // /\ /\ // 0 1 2--3 // \/ \/ - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(25.789000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(26.016000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(21.902000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(21.862000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(52.987000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(25.789000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(26.016000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(21.902000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(21.862000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(52.987000)); graph.freeze(); compareCHWithDijkstra(1000, new int[]{0, 1, 2, 3}); } @@ -168,17 +172,17 @@ public void testFindPath_multipleInOutEdges_turnReplacementDifference() { // To cover all or at least as many as possible different cases we randomly apply some restrictions and compare // the resulting query with a standard Dijkstra search. // If this test fails use the logger output to generate code for further debugging. - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 7).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 8).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 9).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 10).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 9).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 10).setDistance(1)); long seed = System.nanoTime(); Random rnd = new Random(seed); @@ -212,15 +216,15 @@ public void testFindPath_multipleInOutEdges_turnReplacementDifference_bug1() { // 1 - 5 - 6 - 7 - 9 // / \ // 2 10 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 7).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 9).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 10).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 9).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 10).setDistance(1)); setTurnCost(2, 5, 6, 4); setRestriction(1, 5, 6); @@ -234,11 +238,11 @@ public void testFindPath_multipleInOutEdges_turnReplacementDifference_bug1() { public void testFindPath_duplicateEdge() { // 0 -> 1 -> 2 -> 3 -> 4 // \->/ - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); compareCHWithDijkstra(100, new int[]{2, 3, 0, 4, 1}); } @@ -247,14 +251,14 @@ public void testFindPath_chain() { // 0 2 4 6 8 // \ / \ / \ / \ / // 1 3 5 7 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 8).setDistance(1)); graph.freeze(); setTurnCost(1, 2, 3, 4); setTurnCost(3, 4, 5, 2); @@ -271,12 +275,12 @@ public void testFindPath_bidir_chain() { // 5 3 2 1 4 turn costs -> // 0-1-2-3-4-5-6 // 0 1 4 2 3 turn costs <- - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(1)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(1)); - EdgeIteratorState edge5 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(1)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + EdgeIteratorState edge5 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); graph.freeze(); // turn costs -> @@ -311,11 +315,11 @@ public void testFindPath_randomContractionOrder_simpleLoop() { // 0-4-3 // | // 1 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 3).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 3).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); graph.freeze(); // enforce loop (going counter-clockwise) @@ -333,14 +337,14 @@ public void testFindPath_randomContractionOrder_singleDirectedLoop() { // 7-5-0 // | // 6-4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 5).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 0).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 4).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 5).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 0).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 4).setDistance(2)); graph.freeze(); setRestriction(7, 5, 6); @@ -360,13 +364,13 @@ public void testFindPath_randomContractionOrder_singleLoop() { // 1-2-3 // | // 5-6 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(2)); graph.freeze(); // enforce loop (going counter-clockwise) @@ -391,29 +395,29 @@ public void testFindPath_randomContractionOrder_singleLoopWithNoise() { // } | } } // 11~12-13-14 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 8).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 2).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 12).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(12, 13).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(13, 14).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 8).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 12).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(12, 13).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(13, 14).setDistance(2)); // some more edges to make it more complicated -> potentially find more bugs - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(8)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 11).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(11, 12).setDistance(50)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 13).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 15).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(15, 16).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(16, 17).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(17, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 9).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(9, 14).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(8)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 11).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(11, 12).setDistance(50)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 13).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 15).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(15, 16).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(16, 17).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(17, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 9).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(9, 14).setDistance(2)); graph.freeze(); // enforce loop (going counter-clockwise) @@ -454,49 +458,49 @@ public void testFindPath_randomContractionOrder_complicatedGraphAndPath() { // 21-22-23-24 25-26 // first we add all edges that contribute to the shortest path, verticals: cost=1, horizontals: cost=2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 7).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 8).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 2).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 12).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(12, 11).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(11, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 7).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(7, 13).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(13, 14).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(14, 9).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(9, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 10).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(10, 9).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(14, 19).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(19, 18).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(18, 17).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(17, 16).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(16, 21).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(21, 22).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(22, 23).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(23, 24).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(24, 19).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(19, 20).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(20, 25).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(25, 26).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 7).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 8).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 12).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(12, 11).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(11, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(7, 13).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(13, 14).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(14, 9).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(9, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 10).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(10, 9).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(14, 19).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(19, 18).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(18, 17).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(17, 16).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(16, 21).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(21, 22).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(22, 23).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(23, 24).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(24, 19).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(19, 20).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(20, 25).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(25, 26).setDistance(2)); //some more edges to make it more complicated -> potentially find more bugs - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 9).setDistance(75)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(17, 22).setDistance(9)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(18, 23).setDistance(15)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(12, 17).setDistance(50)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(13, 18).setDistance(80)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(14, 15).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(15, 27).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(27, 28).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(28, 26).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(20, 28).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(8, 9).setDistance(75)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(17, 22).setDistance(9)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(18, 23).setDistance(15)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(12, 17).setDistance(50)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(13, 18).setDistance(80)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(14, 15).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(15, 27).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(27, 28).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(28, 26).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(20, 28).setDistance(1)); graph.freeze(); // enforce figure of eight curve at node 7 @@ -537,13 +541,13 @@ public void testFindPath_pTurn_uTurnAtContractedNode() { // 4- 0 // | // 5 -> 6 -> 1 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(1)); graph.freeze(); setRestriction(5, 6, 1); @@ -561,14 +565,14 @@ public void testFindPath_pTurn_uTurnAtContractedNode_twoShortcutsInAndOut() { // 1 // | // 5 -> 6 -> 7 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(1)); graph.freeze(); setRestriction(5, 6, 7); @@ -602,7 +606,7 @@ public void testFindPath_highlyConnectedGraph_compareWithDijkstra() { final int from = i * size + j; final int to = from + 1; final double dist = nextDist(maxDist, rnd); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(from, to).setDistance(dist)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(from, to).setDistance(dist)); LOGGER.trace("final EdgeIteratorState edge{} = graph.edge({},{},{},true);", edgeCounter++, from, to, dist); } } @@ -612,7 +616,7 @@ public void testFindPath_highlyConnectedGraph_compareWithDijkstra() { final int from = i * size + j; final int to = from + size; double dist = nextDist(maxDist, rnd); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(from, to).setDistance(dist)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(from, to).setDistance(dist)); LOGGER.trace("final EdgeIteratorState edge{} = graph.edge({},{},{},true);", edgeCounter++, from, to, dist); } } @@ -623,20 +627,20 @@ public void testFindPath_highlyConnectedGraph_compareWithDijkstra() { if (j < size - 1) { final double dist = nextDist(maxDist, rnd); final int to = from + size + 1; - GHUtility.setSpeed(60, true, true, encoder, graph.edge(from, to).setDistance(dist)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(from, to).setDistance(dist)); LOGGER.trace("final EdgeIteratorState edge{} = graph.edge({},{},{},true);", edgeCounter++, from, to, dist); } if (j > 0) { final double dist = nextDist(maxDist, rnd); final int to = from + size - 1; - GHUtility.setSpeed(60, true, true, encoder, graph.edge(from, to).setDistance(dist)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(from, to).setDistance(dist)); LOGGER.trace("final EdgeIteratorState edge{} = graph.edge({},{},{},true);", edgeCounter++, from, to, dist); } } } graph.freeze(); - EdgeExplorer inExplorer = graph.createEdgeExplorer(AccessFilter.inEdges(encoder.getAccessEnc())); - EdgeExplorer outExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(encoder.getAccessEnc())); + EdgeExplorer inExplorer = graph.createEdgeExplorer(AccessFilter.inEdges(accessEnc)); + EdgeExplorer outExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(accessEnc)); // add turn costs or restrictions for (int node = 0; node < size * size; ++node) { @@ -661,11 +665,11 @@ public void testFindPath_highlyConnectedGraph_compareWithDijkstra() { @Test public void testFindPath_bug() { - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(18.364000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(29.814000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2).setDistance(14.554000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(29.819000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(29.271000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(18.364000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(29.814000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2).setDistance(14.554000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(29.819000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(29.271000)); setRestriction(3, 1, 2); graph.freeze(); @@ -674,11 +678,12 @@ public void testFindPath_bug() { @Test public void testFindPath_bug2() { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 3).setDistance(24.001000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(6.087000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(6.067000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(46.631000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(46.184000)); + // 1 = 0 - 3 - 2 - 4 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 3).setDistance(24.001000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(6.087000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(6.067000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(46.631000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4).setDistance(46.184000)); graph.freeze(); compareCHWithDijkstra(1000, new int[]{1, 0, 3, 2, 4}); @@ -691,15 +696,15 @@ public void testFindPath_loop() { // 1 2 // \ / // 0 - 7 - 8 - 4 - 6 - 5 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 8).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(8, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(8, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 5).setDistance(1)); setRestriction(8, 4, 6); graph.freeze(); @@ -716,11 +721,11 @@ public void testFindPath_finiteUTurnCost() { // 0-3-4 // |/ // 2 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(100)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 2).setDistance(500)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(200)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 1).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 2).setDistance(500)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(200)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 1).setDistance(100)); setRestriction(0, 3, 1); graph.freeze(); chConfig = chConfigs.get(2); @@ -738,11 +743,11 @@ public void testFindPath_calcTurnCostTime() { // 2-1--3 // | | // 0->4 - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 4).setDistance(1)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 3).setDistance(1)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(1)); - EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 0).setDistance(1)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 4).setDistance(1)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 3).setDistance(1)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); + EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 0).setDistance(1)); setTurnCost(edge0, edge4, 1, 8); setRestriction(edge0, edge3, 1); graph.freeze(); @@ -754,10 +759,10 @@ public void testFindPath_loopsMustAlwaysBeAccepted() { // --- // \ / // 0 -- 1 -- 2 -- 3 - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 1).setDistance(1)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 1).setDistance(1)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); setTurnCost(edge0, edge1, 1, 1); setRestriction(edge0, edge2, 1); graph.freeze(); @@ -767,14 +772,14 @@ public void testFindPath_loopsMustAlwaysBeAccepted() { @Test public void testFindPath_compareWithDijkstra_zeroWeightLoops_random() { - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 3).setDistance(21.329000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(29.126000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(38.865000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 4).setDistance(80.005000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 1).setDistance(91.023000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 3).setDistance(21.329000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(29.126000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(38.865000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 4).setDistance(80.005000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 1).setDistance(91.023000)); // add loops with zero weight ... - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 1).setDistance(0.000000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 1).setDistance(0.000000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 1).setDistance(0.000000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 1).setDistance(0.000000)); graph.freeze(); automaticCompareCHWithDijkstra(100); } @@ -785,12 +790,12 @@ public void testFindPath_compareWithDijkstra_zeroWeightLoops() { // 0 -> 1 -> 2 -> 3 -- // | \| // 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); graph.freeze(); IntArrayList expectedPath = IntArrayList.from(0, 1, 2, 3, 4); checkPath(expectedPath, 4, 0, 0, 4, new int[]{2, 0, 4, 1, 3}); @@ -804,14 +809,14 @@ void anotherDoubleZeroWeightLoop() { // 0 - 5 - 2 // oo // note there are two (directed) zero weight loops at node 5! - GHUtility.setSpeed(60.000000, 60.000000, encoder, graph.edge(5, 1).setDistance(263.944000)); // edgeId=0 - GHUtility.setSpeed(120.000000, 120.000000, encoder, graph.edge(5, 2).setDistance(315.026000)); // edgeId=1 - GHUtility.setSpeed(40.000000, 40.000000, encoder, graph.edge(1, 4).setDistance(157.012000)); // edgeId=2 - GHUtility.setSpeed(45.000000, 45.000000, encoder, graph.edge(5, 0).setDistance(513.913000)); // edgeId=3 - GHUtility.setSpeed(15.000000, 15.000000, encoder, graph.edge(6, 4).setDistance(678.992000)); // edgeId=4 - GHUtility.setSpeed(60.000000, 0.000000, encoder, graph.edge(5, 5).setDistance(0.000000)); // edgeId=5 - GHUtility.setSpeed(40.000000, 40.000000, encoder, graph.edge(6, 7).setDistance(890.261000)); // edgeId=6 - GHUtility.setSpeed(90.000000, 0.000000, encoder, graph.edge(5, 5).setDistance(0.000000)); // edgeId=7 + GHUtility.setSpeed(60.000000, 60.000000, accessEnc, speedEnc, graph.edge(5, 1).setDistance(263.944000)); // edgeId=0 + GHUtility.setSpeed(120.000000, 120.000000, accessEnc, speedEnc, graph.edge(5, 2).setDistance(315.026000)); // edgeId=1 + GHUtility.setSpeed(40.000000, 40.000000, accessEnc, speedEnc, graph.edge(1, 4).setDistance(157.012000)); // edgeId=2 + GHUtility.setSpeed(45.000000, 45.000000, accessEnc, speedEnc, graph.edge(5, 0).setDistance(513.913000)); // edgeId=3 + GHUtility.setSpeed(15.000000, 15.000000, accessEnc, speedEnc, graph.edge(6, 4).setDistance(678.992000)); // edgeId=4 + GHUtility.setSpeed(60.000000, 0.000000, accessEnc, speedEnc, graph.edge(5, 5).setDistance(0.000000)); // edgeId=5 + GHUtility.setSpeed(40.000000, 40.000000, accessEnc, speedEnc, graph.edge(6, 7).setDistance(890.261000)); // edgeId=6 + GHUtility.setSpeed(90.000000, 0.000000, accessEnc, speedEnc, graph.edge(5, 5).setDistance(0.000000)); // edgeId=7 graph.freeze(); automaticCompareCHWithDijkstra(100); } @@ -822,12 +827,12 @@ public void testFindPath_compareWithDijkstra_zeroWeightLoops_withTurnRestriction // 0 -> 1 -> 2 -> 3 -- // | \| // 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - EdgeIteratorState edge5 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + EdgeIteratorState edge5 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); setTurnCost(edge2, edge3, 3, 5); setTurnCost(edge2, edge4, 3, 4); setTurnCost(edge3, edge4, 3, 2); @@ -841,11 +846,11 @@ public void testFindPath_compareWithDijkstra_zeroWeightLoops_withTurnRestriction public void testFindPath_oneWayLoop() { // o // 0-1-2-3-4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); setRestriction(1, 2, 3); graph.freeze(); automaticPrepareCH(); @@ -862,11 +867,11 @@ public void testFindPath_loopEdge() { // 1-0 // | | // 4-2o - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(802.964000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(615.195000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 2).setDistance(181.788000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2).setDistance(191.996000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(527.821000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(802.964000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(615.195000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 2).setDistance(181.788000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2).setDistance(191.996000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(527.821000)); setRestriction(0, 2, 4); setTurnCost(0, 2, 2, 3); setTurnCost(2, 2, 4, 4); @@ -890,11 +895,11 @@ public void test_issue1593_full(String algo) { na.setNode(2, 49.404004, 9.709110); na.setNode(3, 49.400160, 9.708787); na.setNode(4, 49.400883, 9.706347); - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 3).setDistance(194.063000)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(525.106000)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(525.106000)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(703.778000)); - EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(400.509000)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 3).setDistance(194.063000)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(525.106000)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(525.106000)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(703.778000)); + EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4).setDistance(400.509000)); // cannot go 4-2-1 and 1-2-4 (at least when using edge1, there is still edge2!) setRestriction(edge4, edge1, 2); setRestriction(edge1, edge4, 2); @@ -946,11 +951,11 @@ public void test_issue_1593_simple(String algo) { na.setNode(0, 0.1, 0.1); na.setNode(5, 0.1, 0.2); na.setNode(4, 0.1, 0.3); - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 1).setDistance(10)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 0).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 5).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 4).setDistance(10)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 1).setDistance(10)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 0).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 5).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 4).setDistance(10)); // cannot go, 2-3-1 setRestriction(edge1, edge0, 3); graph.freeze(); @@ -979,8 +984,8 @@ public void test_issue_1593_simple(String algo) { public void testRouteViaVirtualNode(String algo) { // 3 // 0-x-1-2 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(0)); updateDistancesFor(graph, 0, 0.00, 0.00); updateDistancesFor(graph, 1, 0.02, 0.02); updateDistancesFor(graph, 2, 0.03, 0.03); @@ -1006,9 +1011,9 @@ public void testRouteViaVirtualNode_withAlternative(String algo) { // 0-x-1 // \ | // \-2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 0).setDistance(1)); updateDistancesFor(graph, 0, 0.01, 0.00); updateDistancesFor(graph, 1, 0.01, 0.02); updateDistancesFor(graph, 2, 0.00, 0.02); @@ -1034,12 +1039,12 @@ public void testFiniteUTurnCost_virtualViaNode(String algo) { // 4->3->2->1-x-0 // | // 5->6 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(0)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 0).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 5).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(0)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 0).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 5).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(0)); updateDistancesFor(graph, 4, 0.1, 0.0); updateDistancesFor(graph, 3, 0.1, 0.1); updateDistancesFor(graph, 2, 0.1, 0.2); @@ -1073,7 +1078,7 @@ public void testFiniteUTurnCost_virtualViaNode(String algo) { assertEquals(IntArrayList.from(4, 3, 2, 1, 7, 0, 7, 1, 5, 6), dijkstraPath.calcNodes()); assertEquals(dijkstraPath.getWeight(), path.getWeight(), 1.e-2); assertEquals(dijkstraPath.getDistance(), path.getDistance(), 1.e-2); - assertEquals(dijkstraPath.getTime(), path.getTime()); + assertEquals(dijkstraPath.getTime(), path.getTime(), 5); } @ParameterizedTest @@ -1086,8 +1091,6 @@ public void test_astar_issue2061(String algo) { // cancelled the entire fwd search instead of simply stalling node 6. // |-------1-| // 7-6---0---2-3-4-5 - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - DecimalEncodedValue speedEnc = encoder.getAverageSpeedEnc(); graph.edge(0, 1).set(accessEnc, true).set(speedEnc, 60); graph.edge(1, 5).set(accessEnc, true).set(speedEnc, 60); graph.edge(0, 2).set(accessEnc, true).set(speedEnc, 60); @@ -1115,13 +1118,13 @@ public void test_astar_issue2061(String algo) { void testZeroUTurnCosts_atBarrier_issue2564() { // lvl: 0 3 2 4 5 1 // nd: 0-1-2-3-4-5 - GHUtility.setSpeed(60, 60, encoder, graph.edge(0, 1).setDistance(100)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); // the original bug was sometimes hidden depending on the exact distance, so we use these odd numbers here - GHUtility.setSpeed(60, 60, encoder, graph.edge(1, 2).setDistance(7.336)); - GHUtility.setSpeed(60, 60, encoder, graph.edge(2, 3).setDistance(10.161)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(1, 2).setDistance(7.336)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10.161)); // a zero distance edge (like a passable barrier)! - GHUtility.setSpeed(60, 60, encoder, graph.edge(3, 4).setDistance(0)); - GHUtility.setSpeed(60, 60, encoder, graph.edge(4, 5).setDistance(100)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(3, 4).setDistance(0)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(4, 5).setDistance(100)); graph.freeze(); // u-turn costs are zero! chConfig = chConfigs.get(1); @@ -1131,6 +1134,46 @@ void testZeroUTurnCosts_atBarrier_issue2564() { compareCHQueryWithDijkstra(0, 5); } + @Test + void testBestFwdBwdEntryUpdate() { + // 2-3 + // | | + // 0-4-1 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(2, 0).setDistance(800.22)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(3, 4).setDistance(478.84)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 4).setDistance(547.08)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(4, 1).setDistance(288.95)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(2, 3).setDistance(90)); + graph.freeze(); + prepareCH(1, 3, 0, 2, 4); + compareCHQueryWithDijkstra(1, 2); + } + + @Test + void testEdgeKeyBug() { + // 1 - 2 - 0 - 4 + // \ / + // 3 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 3).setDistance(100)); // edgeId=0 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(4, 3).setDistance(100)); // edgeId=1 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 4).setDistance(100)); // edgeId=2 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); // edgeId=3 + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 2).setDistance(100)); // edgeId=4 + graph.freeze(); + prepareCH(2, 0, 1, 3, 4); + assertEquals(2, chGraph.getShortcuts()); + RoutingCHEdgeIteratorState chEdge = chGraph.getEdgeIteratorState(6, 4); + assertEquals(3, chEdge.getBaseNode()); + assertEquals(4, chEdge.getAdjNode()); + assertEquals(2, chEdge.getSkippedEdge1()); + assertEquals(0, chEdge.getSkippedEdge2()); + // the first edge is 4-0 (edge 2 against the storage direction) -> key is 2*2+1=5 + assertEquals(5, chEdge.getOrigEdgeKeyFirst()); + // the second is 0-3 (edge 0 in storage direction) -> key is 2*0=0 + assertEquals(0, chEdge.getOrigEdgeKeyLast()); + compareCHQueryWithDijkstra(1, 3); + } + /** * This test runs on a random graph with random turn costs and a predefined (but random) contraction order. * It often produces exotic conditions that are hard to anticipate beforehand. @@ -1163,8 +1206,8 @@ private void compareWithDijkstraOnRandomGraph(long seed) { final Random rnd = new Random(seed); // for larger graphs preparation takes much longer the higher the degree is! GHUtility.buildRandomGraph(graph, rnd, 20, 3.0, true, true, - encoder.getAccessEnc(), encoder.getAverageSpeedEnc(), null, 0.7, 0.9, 0.8); - GHUtility.addRandomTurnCosts(graph, seed, encodingManager, encoder, maxCost, turnCostStorage); + accessEnc, speedEnc, null, 0.7, 0.9, 0.8); + GHUtility.addRandomTurnCosts(graph, seed, accessEnc, turnCostEnc, maxCost, turnCostStorage); graph.freeze(); checkStrict = false; IntArrayList contractionOrder = getRandomIntegerSequence(graph.getNodes(), rnd); @@ -1191,8 +1234,8 @@ public void testFindPath_heuristic_compareWithDijkstra_finiteUTurnCost() { private void compareWithDijkstraOnRandomGraph_heuristic(long seed) { GHUtility.buildRandomGraph(graph, new Random(seed), 20, 3.0, true, true, - encoder.getAccessEnc(), encoder.getAverageSpeedEnc(), null, 0.7, 0.9, 0.8); - GHUtility.addRandomTurnCosts(graph, seed, encodingManager, encoder, maxCost, turnCostStorage); + accessEnc, speedEnc, null, 0.7, 0.9, 0.8); + GHUtility.addRandomTurnCosts(graph, seed, accessEnc, turnCostEnc, maxCost, turnCostStorage); graph.freeze(); checkStrict = false; automaticCompareCHWithDijkstra(100); @@ -1306,7 +1349,7 @@ private void compareCHQueryWithDijkstra(int from, int to) { } if (algosDisagree) { System.out.println("Graph that produced error:"); - GHUtility.printGraphForUnitTest(graph, encoder); + GHUtility.printGraphForUnitTest(graph, accessEnc, speedEnc); fail("Dijkstra and CH did not find equal shortest paths for route from " + from + " to " + to + "\n" + " dijkstra: weight: " + dijkstraPath.getWeight() + ", distance: " + dijkstraPath.getDistance() + ", time: " + dijkstraPath.getTime() + ", nodes: " + dijkstraPath.calcNodes() + "\n" + diff --git a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java index 627dd089bb4..4e723b6bf95 100644 --- a/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java +++ b/core/src/test/java/com/graphhopper/routing/ch/EdgeBasedNodeContractorTest.java @@ -18,11 +18,8 @@ package com.graphhopper.routing.ch; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.routing.weighting.Weighting; @@ -48,7 +45,9 @@ */ public class EdgeBasedNodeContractorTest { private final int maxCost = 10; - private FlagEncoder encoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; + private DecimalEncodedValue turnCostEnc; private BaseGraph graph; private Weighting weighting; private CHStorage chStore; @@ -62,13 +61,15 @@ public void setup() { } private void initialize() { - encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", maxCost)); - EncodingManager encodingManager = EncodingManager.create(encoder); - graph = new BaseGraph.Builder(encodingManager).create(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + turnCostEnc = TurnCost.create("car", maxCost); + EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); chConfigs = Arrays.asList( - CHConfig.edgeBased("p1", new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage()))), - CHConfig.edgeBased("p2", new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage(), 60))), - CHConfig.edgeBased("p3", new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage(), 0))) + CHConfig.edgeBased("p1", new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()))), + CHConfig.edgeBased("p2", new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 60))), + CHConfig.edgeBased("p3", new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 0))) ); } @@ -86,12 +87,12 @@ public void testContractNodes_simpleLoop() { // 6- 7-8 // | // 9 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 7).setDistance(2)); - final EdgeIteratorState edge7to8 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 8).setDistance(2)); - final EdgeIteratorState edge8to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(8, 3).setDistance(1)); - final EdgeIteratorState edge3to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(2)); - final EdgeIteratorState edge2to7 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 9).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(2)); + final EdgeIteratorState edge7to8 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 8).setDistance(2)); + final EdgeIteratorState edge8to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(8, 3).setDistance(1)); + final EdgeIteratorState edge3to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(2)); + final EdgeIteratorState edge2to7 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 9).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); @@ -101,8 +102,8 @@ public void testContractNodes_simpleLoop() { contractNodes(5, 6, 3, 2, 9, 1, 8, 4, 7, 0); checkShortcuts( createShortcut(2, 8, edge8to3, edge3to2, 5, false, true), - createShortcut(8, 7, edge8to3.getEdge(), edge2to7.getEdge(), 6, edge2to7.getEdge(), 6, true, false), - createShortcut(7, 7, edge7to8.getEdge(), edge2to7.getEdge(), edge7to8.getEdge(), 7, 8, true, false) + createShortcut(8, 7, edge8to3.getEdgeKey(), edge2to7.getEdgeKey(), 6, edge2to7.getEdge(), 6, true, false), + createShortcut(7, 7, edge7to8.getEdgeKey(), edge2to7.getEdgeKey(), edge7to8.getEdge(), 7, 8, true, false) ); } @@ -114,13 +115,13 @@ public void testContractNodes_necessaryAlternative() { // 2 -> 6 -> 3 -> 5 -> 4 // | ^ // -> 0-| - final EdgeIteratorState e6to0 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 0).setDistance(4)); - final EdgeIteratorState e0to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 6).setDistance(1)); - final EdgeIteratorState e6to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 3).setDistance(1)); - final EdgeIteratorState e3to5 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 5).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 4).setDistance(2)); + final EdgeIteratorState e6to0 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 0).setDistance(4)); + final EdgeIteratorState e0to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 6).setDistance(1)); + final EdgeIteratorState e6to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 3).setDistance(1)); + final EdgeIteratorState e3to5 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 5).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 4).setDistance(2)); freeze(); setMaxLevelOnAllNodes(); setRestriction(1, 6, 3); @@ -131,7 +132,7 @@ public void testContractNodes_necessaryAlternative() { // from contracting node 3: two shortcuts: // 1) in case we come from 1->6 (cant turn left) // 2) in case we come from 2->6 (going via node 0 would be more expensive) - createShortcut(5, 6, e6to0.getEdge(), e3to5.getEdge(), 7, e3to5.getEdge(), 11, false, true), + createShortcut(5, 6, e6to0.getEdgeKey(), e3to5.getEdgeKey(), 7, e3to5.getEdge(), 11, false, true), createShortcut(5, 6, e6to3, e3to5, 3, false, true) ); } @@ -141,11 +142,11 @@ public void testContractNodes_alternativeNecessary_noUTurn() { // /->0--> // v \ // 4 <-----> 2 -> 3 -> 1 - EdgeIteratorState e0to4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 0).setDistance(3)); - EdgeIteratorState e0to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 2).setDistance(5)); - EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(2)); - EdgeIteratorState e1to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 1).setDistance(2)); - EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 2).setDistance(2)); + EdgeIteratorState e0to4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 0).setDistance(3)); + EdgeIteratorState e0to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 2).setDistance(5)); + EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(2)); + EdgeIteratorState e1to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 1).setDistance(2)); + EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 2).setDistance(2)); freeze(); setMaxLevelOnAllNodes(); @@ -156,7 +157,7 @@ public void testContractNodes_alternativeNecessary_noUTurn() { // from contraction of node 2 // It might look like it is always better to go directly from 4 to 2, but when we come from edge (2->4) // we may not do a u-turn at 4. - createShortcut(3, 4, e0to4.getEdge(), e2to3.getEdge(), 5, e2to3.getEdge(), 10, false, true), + createShortcut(3, 4, e0to4.getEdgeKey(), e2to3.getEdgeKey(), 5, e2to3.getEdge(), 10, false, true), createShortcut(3, 4, e2to4, e2to3, 4, false, true) ); } @@ -168,13 +169,13 @@ public void testContractNodes_bidirectionalLoop() { // 0-4-6 // | // 5-2 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 4).setDistance(2)); - final EdgeIteratorState e4to6 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 6).setDistance(2)); - final EdgeIteratorState e3to6 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 3).setDistance(1)); - final EdgeIteratorState e3to4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - final EdgeIteratorState e4to5 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 2).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 4).setDistance(2)); + final EdgeIteratorState e4to6 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 6).setDistance(2)); + final EdgeIteratorState e6to3 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 3).setDistance(1)); + final EdgeIteratorState e3to4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + final EdgeIteratorState e4to5 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 2).setDistance(2)); freeze(); // enforce loop (going counter-clockwise) @@ -186,21 +187,21 @@ public void testContractNodes_bidirectionalLoop() { contractAllNodesInOrder(); checkShortcuts( // from contraction of node 3 - createShortcut(4, 6, e3to4, e3to6, 6, true, false), - createShortcut(4, 6, e3to6, e3to4, 4, false, true), + createShortcut(4, 6, e3to4.detach(true), e6to3.detach(true), 6, true, false), + createShortcut(4, 6, e6to3, e3to4, 4, false, true), // from contraction of node 4 // two 'parallel' shortcuts to preserve shortest paths to 5 when coming from 4->6 and 3->6 !! - createShortcut(5, 6, e3to6.getEdge(), e4to5.getEdge(), 8, e4to5.getEdge(), 5, false, true), - createShortcut(5, 6, e4to6, e4to5, 3, false, true) + createShortcut(5, 6, e6to3.getEdgeKey(), e4to5.getEdgeKey(), 8, e4to5.getEdge(), 5, false, true), + createShortcut(5, 6, e4to6.detach(true), e4to5, 3, false, true) ); } @Test public void testContractNode_twoNormalEdges_noSourceEdgeToConnect() { // 1 --> 0 --> 2 --> 3 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 2).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 2).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(0, 3, 1, 2); @@ -213,9 +214,9 @@ public void testContractNode_twoNormalEdges_noSourceEdgeToConnect() { @Test public void testContractNode_twoNormalEdges_noTargetEdgeToConnect() { // 3 --> 1 --> 0 --> 2 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 2).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 2).setDistance(5)); freeze(); setMaxLevelOnAllNodes(); contractNodes(0, 3, 1, 2); @@ -228,10 +229,10 @@ public void testContractNode_twoNormalEdges_noTargetEdgeToConnect() { @Test public void testContractNode_twoNormalEdges_noEdgesToConnectBecauseOfTurnRestrictions() { // 0 --> 3 --> 2 --> 4 --> 1 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); setRestriction(0, 3, 2); setRestriction(2, 4, 1); freeze(); @@ -244,10 +245,10 @@ public void testContractNode_twoNormalEdges_noEdgesToConnectBecauseOfTurnRestric @Test public void testContractNode_twoNormalEdges_noTurncosts() { // 0 --> 3 --> 2 --> 4 --> 1 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(1)); - final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(3)); - final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); EdgeBasedNodeContractor nodeContractor = createNodeContractor(); @@ -267,10 +268,10 @@ public void testContractNode_twoNormalEdges_noTurncosts() { @Test public void testContractNode_twoNormalEdges_noShortcuts() { // 0 --> 1 --> 2 --> 3 --> 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractAllNodesInOrder(); @@ -281,10 +282,10 @@ public void testContractNode_twoNormalEdges_noShortcuts() { @Test public void testContractNode_twoNormalEdges_noOutgoingEdges() { // 0 --> 1 --> 2 <-- 3 <-- 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 3).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 4, 1, 3); @@ -294,10 +295,10 @@ public void testContractNode_twoNormalEdges_noOutgoingEdges() { @Test public void testContractNode_twoNormalEdges_noIncomingEdges() { // 0 <-- 1 <-- 2 --> 3 --> 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 4, 1, 3); @@ -311,17 +312,17 @@ public void testContractNode_duplicateOutgoingEdges_differentWeight() { // 0 -> 1 -> 2 -> 3 -> 4 // \->/ - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 4, 1, 3); // there should be only one shortcut checkShortcuts( - createShortcut(1, 3, 1, 3, 1, 3, 2) + createShortcut(1, 3, 2, 6, 1, 3, 2) ); } @@ -329,16 +330,16 @@ public void testContractNode_duplicateOutgoingEdges_differentWeight() { public void testContractNode_duplicateIncomingEdges_differentWeight() { // 0 -> 1 -> 2 -> 3 -> 4 // \->/ - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 4, 1, 3); checkShortcuts( - createShortcut(1, 3, 2, 3, 2, 3, 2) + createShortcut(1, 3, 4, 6, 2, 3, 2) ); } @@ -351,11 +352,11 @@ public void testContractNode_duplicateOutgoingEdges_sameWeight() { // 0 -> 1 -> 2 -> 3 -> 4 // \->/ - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 4, 1, 3); @@ -366,11 +367,11 @@ public void testContractNode_duplicateOutgoingEdges_sameWeight() { public void testContractNode_duplicateIncomingEdges_sameWeight() { // 0 -> 1 -> 2 -> 3 -> 4 // \->/ - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 4, 1, 3); @@ -380,10 +381,10 @@ public void testContractNode_duplicateIncomingEdges_sameWeight() { @Test public void testContractNode_twoNormalEdges_withTurnCost() { // 0 --> 3 --> 2 --> 4 --> 1 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(1)); - final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(3)); - final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); setTurnCost(3, 2, 4, 4); freeze(); setMaxLevelOnAllNodes(); @@ -394,10 +395,10 @@ public void testContractNode_twoNormalEdges_withTurnCost() { @Test public void testContractNode_twoNormalEdges_withTurnRestriction() { // 0 --> 3 --> 2 --> 4 --> 1 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); setRestriction(3, 2, 4); freeze(); setMaxLevelOnAllNodes(); @@ -408,10 +409,10 @@ public void testContractNode_twoNormalEdges_withTurnRestriction() { @Test public void testContractNode_twoNormalEdges_bidirectional() { // 0 -- 3 -- 2 -- 4 -- 1 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 3).setDistance(1)); - final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 2).setDistance(3)); - final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4).setDistance(5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); setTurnCost(e3to2, e2to4, 2, 4); setTurnCost(e2to4, e3to2, 2, 4); freeze(); @@ -421,35 +422,35 @@ public void testContractNode_twoNormalEdges_bidirectional() { // note that for now we add a shortcut for each direction. using fwd/bwd flags would be more efficient, // but requires a more sophisticated way to determine the 'first' and 'last' original edges at various // places - createShortcut(3, 4, e3to2, e2to4, 12, true, false), - createShortcut(3, 4, e2to4, e3to2, 12, false, true) + createShortcut(3, 4, 2, 4, 1, 2, 12, true, false), + createShortcut(3, 4, 5, 3, 2, 1, 12, false, true) ); } @Test public void testContractNode_twoNormalEdges_bidirectional_differentCosts() { // 0 -- 3 -- 2 -- 4 -- 1 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 3).setDistance(1)); - final EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 2).setDistance(3)); - final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 1).setDistance(1)); - setTurnCost(e2to3, e2to4, 2, 4); - setTurnCost(e2to4, e2to3, 2, 7); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4).setDistance(5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); + setTurnCost(e3to2, e2to4, 2, 4); + setTurnCost(e2to4, e3to2, 2, 7); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 3, 4); checkShortcuts( - createShortcut(3, 4, e2to3, e2to4, 12, true, false), - createShortcut(3, 4, e2to4, e2to3, 15, false, true) + createShortcut(3, 4, e3to2, e2to4, 12, true, false), + createShortcut(3, 4, e2to4.detach(true), e3to2.detach(true), 15, false, true) ); } @Test public void testContractNode_multiple_bidirectional_linear() { // 3 -- 2 -- 1 -- 4 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 2).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 1).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(6)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 1).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(6)); freeze(); setMaxLevelOnAllNodes(); @@ -472,11 +473,11 @@ private void runTestWithTurnCostAndLoop(boolean loopHelps) { // />\ // \ / // 0 --> 3 --> 2 --> 4 --> 1 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(1)); - final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(3)); - final EdgeIteratorState e2to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 2).setDistance(2)); - final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(1)); + final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + final EdgeIteratorState e2to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 2).setDistance(2)); + final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); setTurnCost(e3to2, e2to2, 2, 2); setTurnCost(e2to2, e2to4, 2, 1); @@ -490,7 +491,7 @@ private void runTestWithTurnCostAndLoop(boolean loopHelps) { // the first (this is important for path unpacking) checkShortcuts( createShortcut(2, 3, e3to2, e2to2, 7, false, true), - createShortcut(3, 4, e3to2.getEdge(), e2to4.getEdge(), 5, e2to4.getEdge(), 13, true, false)); + createShortcut(3, 4, e3to2.getEdgeKey(), e2to4.getEdgeKey(), 5, e2to4.getEdge(), 13, true, false)); } else { // taking the loop would be worse, so the path is just 3-2-4 and we only need a single shortcut checkShortcuts( @@ -503,12 +504,12 @@ public void testContractNode_shortcutDoesNotSpanUTurn() { // 2 -> 7 -> 3 -> 5 -> 6 // | // 1 <-> 4 - final EdgeIteratorState e7to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 3).setDistance(1)); - final EdgeIteratorState e3to5 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 5).setDistance(1)); - final EdgeIteratorState e3to4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(1)); + final EdgeIteratorState e7to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 3).setDistance(1)); + final EdgeIteratorState e3to5 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 5).setDistance(1)); + final EdgeIteratorState e3to4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); setRestriction(7, 3, 5); @@ -516,7 +517,7 @@ public void testContractNode_shortcutDoesNotSpanUTurn() { checkShortcuts( // from contracting node 3 createShortcut(4, 7, e7to3, e3to4, 3, false, true), - createShortcut(4, 5, e3to4, e3to5, 3, true, false) + createShortcut(4, 5, e3to4.detach(true), e3to5, 3, true, false) // important! no shortcut from 7 to 5 when contracting node 4, because it includes a u-turn ); } @@ -534,8 +535,8 @@ public void testContractNode_multiple_loops_leftLoopIsBest() { // direct turn is restricted, so we take the left loop -> two extra shortcuts GraphWithTwoLoops g = new GraphWithTwoLoops(2, maxCost, 1, 2, 3, maxCost); g.contractAndCheckShortcuts( - createShortcut(6, 7, g.e7to6.getEdge(), g.e1to6.getEdge(), g.e7to6.getEdge(), g.getScEdge(3), 12, false, true), - createShortcut(7, 8, g.e7to6.getEdge(), g.e6to8.getEdge(), g.getScEdge(4), g.e6to8.getEdge(), 20, true, false) + createShortcut(6, 7, g.e7to6.getEdgeKey(), g.e1to6.getEdgeKey(), g.e7to6.getEdge(), g.getScEdge(3), 12, false, true), + createShortcut(7, 8, g.e7to6.getEdgeKey(), g.e6to8.getEdgeKey(), g.getScEdge(4), g.e6to8.getEdge(), 20, true, false) ); } @@ -544,8 +545,8 @@ public void testContractNode_multiple_loops_rightLoopIsBest() { // direct turn is restricted, going on left loop is expensive, so we take the right loop -> two extra shortcuts GraphWithTwoLoops g = new GraphWithTwoLoops(8, 1, 1, 2, 3, maxCost); g.contractAndCheckShortcuts( - createShortcut(6, 7, g.e7to6.getEdge(), g.e3to6.getEdge(), g.e7to6.getEdge(), g.getScEdge(2), 12, false, true), - createShortcut(7, 8, g.e7to6.getEdge(), g.e6to8.getEdge(), g.getScEdge(4), g.e6to8.getEdge(), 21, true, false) + createShortcut(6, 7, g.e7to6.getEdgeKey(), g.e3to6.getEdgeKey(), g.e7to6.getEdge(), g.getScEdge(2), 12, false, true), + createShortcut(7, 8, g.e7to6.getEdgeKey(), g.e6to8.getEdgeKey(), g.getScEdge(4), g.e6to8.getEdge(), 21, true, false) ); } @@ -554,9 +555,9 @@ public void testContractNode_multiple_loops_leftRightLoopIsBest() { // multiple turns are restricted, it is best to take the left and the right loop -> three extra shortcuts GraphWithTwoLoops g = new GraphWithTwoLoops(3, maxCost, 1, maxCost, 3, maxCost); g.contractAndCheckShortcuts( - createShortcut(6, 7, g.e7to6.getEdge(), g.e1to6.getEdge(), g.e7to6.getEdge(), g.getScEdge(3), 13, false, true), - createShortcut(6, 7, g.e7to6.getEdge(), g.e3to6.getEdge(), g.getScEdge(5), g.getScEdge(2), 24, false, true), - createShortcut(7, 8, g.e7to6.getEdge(), g.e6to8.getEdge(), g.getScEdge(4), g.e6to8.getEdge(), 33, true, false) + createShortcut(6, 7, g.e7to6.getEdgeKey(), g.e1to6.getEdgeKey(), g.e7to6.getEdge(), g.getScEdge(3), 13, false, true), + createShortcut(6, 7, g.e7to6.getEdgeKey(), g.e3to6.getEdgeKey(), g.getScEdge(5), g.getScEdge(2), 24, false, true), + createShortcut(7, 8, g.e7to6.getEdgeKey(), g.e6to8.getEdgeKey(), g.getScEdge(4), g.e6to8.getEdge(), 33, true, false) ); } @@ -565,9 +566,9 @@ public void testContractNode_multiple_loops_rightLeftLoopIsBest() { // multiple turns are restricted, it is best to take the right and the left loop -> three extra shortcuts GraphWithTwoLoops g = new GraphWithTwoLoops(maxCost, 5, 4, 2, maxCost, maxCost); g.contractAndCheckShortcuts( - createShortcut(6, 7, g.e7to6.getEdge(), g.e3to6.getEdge(), g.e7to6.getEdge(), g.getScEdge(2), 16, false, true), - createShortcut(6, 7, g.e7to6.getEdge(), g.e1to6.getEdge(), g.getScEdge(5), g.getScEdge(3), 25, false, true), - createShortcut(7, 8, g.e7to6.getEdge(), g.e6to8.getEdge(), g.getScEdge(4), g.e6to8.getEdge(), 33, true, false) + createShortcut(6, 7, g.e7to6.getEdgeKey(), g.e3to6.getEdgeKey(), g.e7to6.getEdge(), g.getScEdge(2), 16, false, true), + createShortcut(6, 7, g.e7to6.getEdgeKey(), g.e1to6.getEdgeKey(), g.getScEdge(5), g.getScEdge(3), 25, false, true), + createShortcut(7, 8, g.e7to6.getEdgeKey(), g.e6to8.getEdgeKey(), g.getScEdge(4), g.e6to8.getEdge(), 33, true, false) ); } @@ -578,19 +579,19 @@ public void testContractNode_multiple_loops_rightLeftLoopIsBest() { // 9--7 5 8--10 private class GraphWithTwoLoops { final int centerNode = 6; - final EdgeIteratorState e0to1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(3)); - final EdgeIteratorState e1to6 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 6).setDistance(2)); - final EdgeIteratorState e6to0 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 0).setDistance(4)); - final EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(2)); - final EdgeIteratorState e3to6 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 6).setDistance(7)); - final EdgeIteratorState e6to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 2).setDistance(1)); - final EdgeIteratorState e7to6 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 6).setDistance(1)); - final EdgeIteratorState e6to8 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 8).setDistance(6)); - final EdgeIteratorState e9to7 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(9, 7).setDistance(2)); - final EdgeIteratorState e8to10 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(8, 10).setDistance(3)); + final EdgeIteratorState e0to1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(3)); + final EdgeIteratorState e1to6 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 6).setDistance(2)); + final EdgeIteratorState e6to0 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 0).setDistance(4)); + final EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(2)); + final EdgeIteratorState e3to6 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 6).setDistance(7)); + final EdgeIteratorState e6to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 2).setDistance(1)); + final EdgeIteratorState e7to6 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 6).setDistance(1)); + final EdgeIteratorState e6to8 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 8).setDistance(6)); + final EdgeIteratorState e9to7 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(9, 7).setDistance(2)); + final EdgeIteratorState e8to10 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(8, 10).setDistance(3)); // these two edges help to avoid loop avoidance for the left and right loops - final EdgeIteratorState e4to6 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 6).setDistance(1)); - final EdgeIteratorState e5to6 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); + final EdgeIteratorState e4to6 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 6).setDistance(1)); + final EdgeIteratorState e5to6 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); final int numEdges = 12; GraphWithTwoLoops(int turnCost70, int turnCost72, int turnCost12, int turnCost18, int turnCost38, int turnCost78) { @@ -614,9 +615,9 @@ private void contractAndCheckShortcuts(Shortcut... shortcuts) { HashSet expectedShortcuts = new HashSet<>(); expectedShortcuts.addAll(Arrays.asList( createShortcut(1, 6, e6to0, e0to1, 7, false, true), - createShortcut(6, 6, e6to0.getEdge(), e1to6.getEdge(), getScEdge(0), e1to6.getEdge(), 9, true, false), + createShortcut(6, 6, e6to0.getEdgeKey(), e1to6.getEdgeKey(), getScEdge(0), e1to6.getEdge(), 9, true, false), createShortcut(3, 6, e6to2, e2to3, 3, false, true), - createShortcut(6, 6, e6to2.getEdge(), e3to6.getEdge(), getScEdge(1), e3to6.getEdge(), 10, true, false) + createShortcut(6, 6, e6to2.getEdgeKey(), e3to6.getEdgeKey(), getScEdge(1), e3to6.getEdge(), 10, true, false) )); expectedShortcuts.addAll(Arrays.asList(shortcuts)); checkShortcuts(expectedShortcuts); @@ -650,11 +651,11 @@ public void testContractNode_detour_detourIsWorse() { // / \ // 4--1---2--3 private class GraphWithDetour { - private final EdgeIteratorState e4to1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(2)); - private final EdgeIteratorState e1to0 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(4)); - private final EdgeIteratorState e1to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(3)); - private final EdgeIteratorState e0to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 2).setDistance(3)); - private final EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(2)); + private final EdgeIteratorState e4to1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(2)); + private final EdgeIteratorState e1to0 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(4)); + private final EdgeIteratorState e1to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(3)); + private final EdgeIteratorState e0to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 2).setDistance(3)); + private final EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(2)); GraphWithDetour(int turnCost42, int turnCost13, int turnCost40, int turnCost03) { setTurnCost(e4to1, e1to2, 1, turnCost42); @@ -692,14 +693,14 @@ public void testContractNode_detour_multipleInOut_restrictedIn() { // \ / \ / // 2-1-0-4-6 private class GraphWithDetourMultipleInOutEdges { - final EdgeIteratorState e5to1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 1).setDistance(3)); - final EdgeIteratorState e2to1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(2)); - final EdgeIteratorState e1to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 3).setDistance(1)); - final EdgeIteratorState e3to4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(2)); - final EdgeIteratorState e1to0 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(5)); - final EdgeIteratorState e0to4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 4).setDistance(2)); - final EdgeIteratorState e4to6 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 6).setDistance(1)); - final EdgeIteratorState e4to7 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 7).setDistance(3)); + final EdgeIteratorState e5to1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 1).setDistance(3)); + final EdgeIteratorState e2to1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(2)); + final EdgeIteratorState e1to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); + final EdgeIteratorState e3to4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(2)); + final EdgeIteratorState e1to0 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(5)); + final EdgeIteratorState e0to4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 4).setDistance(2)); + final EdgeIteratorState e4to6 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 6).setDistance(1)); + final EdgeIteratorState e4to7 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 7).setDistance(3)); GraphWithDetourMultipleInOutEdges(int turnCost20, int turnCost50, int turnCost23, int turnCost53, int turnCost36) { setTurnCost(e1to3, e3to4, 3, 2); @@ -721,7 +722,7 @@ public void testContractNode_loopAvoidance_loopNecessary() { final int numEdges = 6; checkShortcuts( createShortcut(1, 2, g.e2to0, g.e0to1, 3, false, true), - createShortcut(2, 2, g.e2to0.getEdge(), g.e1to2.getEdge(), numEdges, g.e1to2.getEdge(), 4, true, false) + createShortcut(2, 2, g.e2to0.getEdgeKey(), g.e1to2.getEdgeKey(), numEdges, g.e1to2.getEdge(), 4, true, false) ); } @@ -741,12 +742,12 @@ public void testContractNode_loopAvoidance_loopAvoidable() { // | // 5 private class GraphWithLoop { - final EdgeIteratorState e0to1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(2)); - final EdgeIteratorState e1to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - final EdgeIteratorState e2to0 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 0).setDistance(1)); - final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(3)); - final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(5)); - final EdgeIteratorState e5to2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 2).setDistance(2)); + final EdgeIteratorState e0to1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(2)); + final EdgeIteratorState e1to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + final EdgeIteratorState e2to0 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 0).setDistance(1)); + final EdgeIteratorState e3to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + final EdgeIteratorState e2to4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(5)); + final EdgeIteratorState e5to2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 2).setDistance(2)); GraphWithLoop(int turnCost34) { setTurnCost(e3to2, e2to4, 2, turnCost34); @@ -762,16 +763,16 @@ public void testContractNode_witnessPathsAreFound() { // 0 - 1 3 - 4 | // | | / // 5 - 9 ---- - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 9).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(9, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 7).setDistance(6)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(9, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 10).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 9).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(9, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 7).setDistance(6)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(9, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 10).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 10, 4, 1, 5, 7, 9, 3); @@ -785,13 +786,13 @@ public void testContractNode_noUnnecessaryShortcut_witnessPathOfEqualWeight() { // 0 -> 1 -> 5 <_ // v v \ // 2 -> 3 -> 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 5).setDistance(1)); - EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - EdgeIteratorState e3to4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - EdgeIteratorState e5to3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 5).setDistance(1)); + EdgeIteratorState e2to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + EdgeIteratorState e3to4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + EdgeIteratorState e5to3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 3).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(3, 2, 0, 1, 5, 4); @@ -813,14 +814,16 @@ public void testContractNode_noUnnecessaryShortcut_differentWitnessesForDifferen // 0 --> 1 ---> 3 ---> 5 --> 6 // \ / // \--> 4 ---/ - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 5).setDistance(1)); // bidirectional - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 5).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(1)); // bidirectional - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 4).setDistance(1)); + // bidirectional + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 5).setDistance(1)); + // bidirectional + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(3, 0, 6, 1, 2, 5, 4); @@ -844,14 +847,16 @@ public void testContractNode_noUnnecessaryShortcut_differentInitialEntriesForDif // 0 --> 1 ---> 3 ---> 5 --> 6 // \ / // \--- 4 ->-/ - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); // bidirectional - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(1)); // bidirectional - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + // bidirectional + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); + // bidirectional + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(3, 0, 6, 1, 2, 5, 4); @@ -872,17 +877,17 @@ public void testContractNode_bidirectional_edge_at_fromNode(boolean edge1to2bidi // 0 -> 1 <-> 5 // v v // 2 --> 3 -> 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, edge1to2bidirectional, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, edge1to2bidirectional, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 3).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 5, 4, 3); // we might come from (5->1) so we still need a way back to (3->4) -> we need a shortcut - Shortcut expectedShortcuts = createShortcut(1, 3, 1, 2, 1, 2, 2); + Shortcut expectedShortcuts = createShortcut(1, 3, 2, 4, 1, 2, 2); checkShortcuts(expectedShortcuts); } @@ -891,12 +896,12 @@ public void testContractNode_bidirectional_edge_at_fromNode_going_to_node() { // 0 -> 1 <-> 5 // v v // 2 --> 3 -> 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 3).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(5, 0, 4, 1, 2, 3); @@ -909,29 +914,29 @@ public void testNodeContraction_directWitness() { // 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 // / \ / \ //10 -> ------> 9 ------> -> 11 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 8).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 9).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(9, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(10, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(7, 11).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 9).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(9, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(10, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(7, 11).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 6, 3, 5, 4, 0, 8, 10, 11, 1, 7, 9); // note that the shortcut edge ids depend on the insertion order which might change when changing the implementation checkShortcuts( - createShortcut(3, 1, 1, 2, 1, 2, 2, false, true), - createShortcut(1, 9, 1, 8, 1, 8, 2, true, false), - createShortcut(5, 7, 5, 6, 5, 6, 2, true, false), - createShortcut(7, 9, 9, 6, 9, 6, 2, false, true), - createShortcut(4, 1, 1, 3, 12, 3, 3, false, true), - createShortcut(4, 7, 4, 6, 4, 13, 3, true, false) + createShortcut(3, 1, 2, 4, 1, 2, 2, false, true), + createShortcut(1, 9, 2, 16, 1, 8, 2, true, false), + createShortcut(5, 7, 10, 12, 5, 6, 2, true, false), + createShortcut(7, 9, 18, 12, 9, 6, 2, false, true), + createShortcut(4, 1, 2, 6, 12, 3, 3, false, true), + createShortcut(4, 7, 8, 12, 4, 13, 3, true, false) ); } @@ -942,12 +947,12 @@ public void testNodeContraction_witnessBetterBecauseOfTurnCostAtTargetNode() { // 0 -> 1 -> 2 -> 3 -> 4 // \ / // -- 5 -> - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 5).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 5).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 3).setDistance(1)); setTurnCost(2, 3, 4, 5); setTurnCost(5, 3, 4, 2); freeze(); @@ -966,18 +971,18 @@ public void testNodeContraction_letShortcutsWitnessEachOther_twoIn() { // 0 -> 1 -> 2 -> 3 -> 4 -> 5 // \ | // ------->| - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 3).setDistance(4)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 3).setDistance(4)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(3, 0, 5, 1, 4, 2); checkShortcuts( - createShortcut(4, 2, 2, 3, 2, 3, 2, false, true) + createShortcut(4, 2, 4, 6, 2, 3, 2, false, true) ); } @@ -990,28 +995,28 @@ public void testNodeContraction_letShortcutsWitnessEachOther_twoOut() { // 0 -> 1 -> 2 -> 3 -> 4 -> 5 // | / // -------> - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(4)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(4)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 5, 1, 4, 3); checkShortcuts( - createShortcut(1, 3, 1, 2, 1, 2, 2) + createShortcut(1, 3, 2, 4, 1, 2, 2) ); } @Test public void testNodeContraction_parallelEdges_onlyOneLoopShortcutNeeded() { + // /--\ // 0 -- 1 -- 2 - // \--/ - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(2)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 0).setDistance(4)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(5)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(2)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 0).setDistance(4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(5)); setTurnCost(edge0, edge1, 0, 1); setTurnCost(edge1, edge0, 0, 2); freeze(); @@ -1019,7 +1024,7 @@ public void testNodeContraction_parallelEdges_onlyOneLoopShortcutNeeded() { contractNodes(0, 2, 1); // it is sufficient to be able to travel the 1-0-1 loop in one (the cheaper) direction checkShortcuts( - createShortcut(1, 1, 0, 1, 0, 1, 7) + createShortcut(1, 1, 1, 3, 0, 1, 7) ); } @@ -1029,12 +1034,12 @@ public void testNodeContraction_duplicateEdge_severalLoops() { // |\ | // | \ / // -- 2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(47)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(19)); - EdgeIteratorState e2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 5).setDistance(38)); - EdgeIteratorState e3 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 5).setDistance(57)); // note there is a duplicate edge here (with different weight) - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10)); - EdgeIteratorState e5 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(56)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(47)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4).setDistance(19)); + EdgeIteratorState e2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 5).setDistance(38)); + EdgeIteratorState e3 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 5).setDistance(57)); // note there is a duplicate edge here (with different weight) + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10)); + EdgeIteratorState e5 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(56)); setTurnCost(e3, e2, 5, 4); setTurnCost(e2, e3, 5, 5); @@ -1049,33 +1054,33 @@ public void testNodeContraction_duplicateEdge_severalLoops() { checkNumShortcuts(11); checkShortcuts( // from node 4 contraction - createShortcut(5, 3, 5, 4, 5, 4, 66, true, false), - createShortcut(5, 3, 4, 5, 4, 5, 66, false, true), - createShortcut(3, 2, 1, 4, 1, 4, 29, false, true), - createShortcut(3, 2, 4, 1, 4, 1, 29, true, false), - createShortcut(5, 2, 1, 5, 1, 5, 75, false, true), - createShortcut(5, 2, 5, 1, 5, 1, 75, true, false), + createShortcut(5, 3, 11, 9, 5, 4, 66, true, false), + createShortcut(5, 3, 8, 10, 4, 5, 66, false, true), + createShortcut(3, 2, 2, 9, 1, 4, 29, false, true), + createShortcut(3, 2, 8, 3, 4, 1, 29, true, false), + createShortcut(5, 2, 2, 10, 1, 5, 75, false, true), + createShortcut(5, 2, 11, 3, 5, 1, 75, true, false), // from node 5 contraction - createShortcut(2, 2, 3, 2, 3, 2, 99, true, false), - createShortcut(2, 2, 3, 1, 3, 6, 134, true, false), - createShortcut(2, 2, 1, 2, 8, 2, 114, true, false), - createShortcut(3, 2, 2, 4, 2, 7, 106, false, true), - createShortcut(3, 2, 4, 2, 9, 2, 105, true, false) + createShortcut(2, 2, 6, 5, 3, 2, 99, true, false), + createShortcut(2, 2, 6, 3, 3, 6, 134, true, false), + createShortcut(2, 2, 2, 5, 8, 2, 114, true, false), + createShortcut(3, 2, 4, 9, 2, 7, 106, false, true), + createShortcut(3, 2, 8, 5, 9, 2, 105, true, false) ); } @Test public void testNodeContraction_tripleConnection() { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1.0)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(2.0)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(3.5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1.0)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(2.0)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(3.5)); freeze(); setMaxLevelOnAllNodes(); contractNodes(1, 0); checkShortcuts( - createShortcut(0, 0, 1, 2, 1, 2, 5.5), - createShortcut(0, 0, 0, 2, 0, 2, 4.5), - createShortcut(0, 0, 0, 1, 0, 1, 3.0) + createShortcut(0, 0, 2, 5, 1, 2, 5.5), + createShortcut(0, 0, 0, 5, 0, 2, 4.5), + createShortcut(0, 0, 0, 3, 0, 1, 3.0) ); } @@ -1086,10 +1091,10 @@ public void testNodeContraction_fromAndToNodesEqual() { // v ^ // \ / // 2 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 3); @@ -1103,11 +1108,11 @@ public void testNodeContraction_node_in_loop() { // 0-4-3 // | // 1 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 3).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 3).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 1).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); @@ -1117,8 +1122,8 @@ public void testNodeContraction_node_in_loop() { setTurnCost(3, 2, 4, 2); contractNodes(2, 0, 1, 4, 3); checkShortcuts( - createShortcut(4, 3, 3, 2, 3, 2, 6, true, false), - createShortcut(4, 3, 2, 3, 2, 3, 4, false, true) + createShortcut(4, 3, 7, 5, 3, 2, 6, true, false), + createShortcut(4, 3, 4, 6, 2, 3, 4, false, true) ); } @@ -1131,11 +1136,11 @@ public void testFindPath_finiteUTurnCost() { // 0-3-4 // |/ // 2 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(100)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(100)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 2).setDistance(500)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(200)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 1).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(100)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(100)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 2).setDistance(500)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(200)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 1).setDistance(100)); freeze(); chStore = CHStorage.fromGraph(graph, chConfigs.get(1)); chBuilder = new CHStorageBuilder(chStore); @@ -1144,8 +1149,8 @@ public void testFindPath_finiteUTurnCost() { setRestriction(0, 3, 1); contractNodes(4, 0, 1, 2, 3); checkShortcuts( - createShortcut(2, 3, 1, 2, 1, 2, 600, false, true), - createShortcut(3, 3, 1, 1, 1, 1, 260, true, false) + createShortcut(2, 3, 2, 4, 1, 2, 600, false, true), + createShortcut(3, 3, 2, 3, 1, 1, 260, true, false) ); } @@ -1154,11 +1159,11 @@ public void testNodeContraction_turnRestrictionAndLoop() { // /\ /<-3 // 0 1--2 // \/ \->4 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(6)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(6)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(3)); setRestriction(3, 2, 4); freeze(); setMaxLevelOnAllNodes(); @@ -1171,11 +1176,11 @@ public void testNodeContraction_forwardLoopNeedsToBeRecognizedAsIncoming() { // --- // \ / // 0 -- 1 -- 2 -- 3 -- 4 - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 1).setDistance(1)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 1).setDistance(1)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); setRestriction(edge0, edge2, 1); freeze(); setMaxLevelOnAllNodes(); @@ -1191,15 +1196,15 @@ public void testNodeContraction_forwardLoopNeedsToBeRecognizedAsIncoming() { @Test public void testNodeContraction_minorWeightDeviation() { // 0 -> 1 -> 2 -> 3 -> 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(51.401)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(70.041)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(75.806)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(05.003)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(51.401)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(70.041)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(75.806)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(05.003)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 3, 4); checkShortcuts( - createShortcut(1, 3, 1, 2, 1, 2, 145.847) + createShortcut(1, 3, 2, 4, 1, 2, 145.847) ); } @@ -1208,15 +1213,15 @@ public void testNodeContraction_zeroWeightLoop_loopOnly() { // zero weight loops are quite a headache..., also see #1355 // /| // 0 -> 1 -> 2 -> 3 -- - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 3); checkShortcuts( - createShortcut(1, 3, 1, 2, 1, 2, 2) + createShortcut(1, 3, 2, 4, 1, 2, 2) ); } @@ -1226,16 +1231,16 @@ public void testNodeContraction_zeroWeightLoop_loopAndEdge() { // 0 -> 1 -> 2 -> 3 -- // | // 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 4, 3); checkShortcuts( - createShortcut(1, 3, 1, 2, 1, 2, 2) + createShortcut(1, 3, 2, 4, 1, 2, 2) ); } @@ -1244,16 +1249,16 @@ public void testNodeContraction_zeroWeightLoop_twoLoops() { // /| // 0 -> 1 -> 2 -> 3 -- // \| - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 3); checkShortcuts( - createShortcut(1, 3, 1, 2, 1, 2, 2) + createShortcut(1, 3, 2, 4, 1, 2, 2) ); } @@ -1263,17 +1268,17 @@ public void testNodeContraction_zeroWeightLoop_twoLoopsAndEdge_edgeFirst() { // 0 -> 1 -> 2 -> 3 -- // | \| // 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 4, 3); checkShortcuts( - createShortcut(1, 3, 1, 2, 1, 2, 2) + createShortcut(1, 3, 2, 4, 1, 2, 2) ); } @@ -1283,17 +1288,17 @@ public void testNodeContraction_zeroWeightLoop_twoLoopsAndEdge_loopsFirst() { // 0 -> 1 -> 2 -> 3 -- // | \| // 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 4, 3); checkShortcuts( - createShortcut(1, 3, 1, 4, 1, 4, 2) + createShortcut(1, 3, 2, 8, 1, 4, 2) ); } @@ -1303,21 +1308,21 @@ public void testNodeContraction_zeroWeightLoop_manyLoops() { // 0 -> 1 -> 2 -> 3 -- // | // 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); freeze(); setMaxLevelOnAllNodes(); contractNodes(2, 0, 1, 4, 3); checkShortcuts( - createShortcut(1, 3, 3, 5, 3, 5, 2) + createShortcut(1, 3, 6, 10, 3, 5, 2) ); } @@ -1328,14 +1333,22 @@ public void testNodeContraction_zeroWeightLoop_another() { // 0 - 5 - 2 // oo // note there are two (directed) zero weight loops at node 5! - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 1).setDistance(100)); // edgeId=0 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 2).setDistance(100)); // edgeId=1 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(100)); // edgeId=2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 0).setDistance(100)); // edgeId=3 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 4).setDistance(100)); // edgeId=4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 5).setDistance(0)); // edgeId=5 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(100)); // edgeId=6 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 5).setDistance(0)); // edgeId=7 + // edgeId=0 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 1).setDistance(100)); + // edgeId=1 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 2).setDistance(100)); + // edgeId=2 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(100)); + // edgeId=3 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 0).setDistance(100)); + // edgeId=4 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 4).setDistance(100)); + // edgeId=5 + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 5).setDistance(0)); + // edgeId=6 + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(100)); + // edgeId=7 + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 5).setDistance(0)); freeze(); setMaxLevelOnAllNodes(); contractNodes(1, 3, 0, 2, 7, 6, 4); @@ -1344,8 +1357,8 @@ public void testNodeContraction_zeroWeightLoop_another() { // connects the same nodes and is not a bridge path. this needs special handling in our edge-based witness path // searcher. checkShortcuts( - createShortcut(4, 5, 0, 2, 0, 2, 200, false, true), - createShortcut(4, 5, 2, 0, 2, 0, 200, true, false) + createShortcut(4, 5, 0, 4, 0, 2, 200, false, true), + createShortcut(4, 5, 5, 1, 2, 0, 200, true, false) ); } @@ -1355,14 +1368,14 @@ public void testNodeContraction_zeroWeightLoop_twoLoopsAndEdge_withTurnRestricti // 0 -> 1 -> 2 -> 3 -- // | // 4 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(1)); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); // add a few more loops to make this test more difficult to pass - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 3).setDistance(0)); // we have to use the zero weight loop so it may not be excluded setTurnCost(edge2, edge3, 3, 5); setRestriction(edge2, edge4, 3); @@ -1378,14 +1391,14 @@ public void testNodeContraction_numPolledEdges() { // | // 0 -> 3 -> 2 <-> 4 -> 5 // \---<----| - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(71.203000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 3).setDistance(79.003000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 0).setDistance(21.328000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 4).setDistance(16.499000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 2).setDistance(16.487000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 1).setDistance(55.603000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(33.453000)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(29.665000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(71.203000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 3).setDistance(79.003000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 0).setDistance(21.328000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 4).setDistance(16.499000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 2).setDistance(16.487000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 1).setDistance(55.603000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(33.453000)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(29.665000)); freeze(); setMaxLevelOnAllNodes(); EdgeBasedNodeContractor nodeContractor = createNodeContractor(); @@ -1397,11 +1410,11 @@ public void testNodeContraction_numPolledEdges() { @Test void issue_2564() { // 0-1-2-3-4-5 - GHUtility.setSpeed(60, 60, encoder, graph.edge(0, 1).setDistance(100)); - GHUtility.setSpeed(60, 60, encoder, graph.edge(1, 2).setDistance(7.336)); - GHUtility.setSpeed(60, 60, encoder, graph.edge(2, 3).setDistance(10.161)); - GHUtility.setSpeed(60, 60, encoder, graph.edge(3, 4).setDistance(0)); - GHUtility.setSpeed(60, 60, encoder, graph.edge(4, 5).setDistance(100)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(1, 2).setDistance(7.336)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10.161)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(3, 4).setDistance(0)); + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(4, 5).setDistance(100)); freeze(); chStore = CHStorage.fromGraph(graph, chConfigs.get(2)); chBuilder = new CHStorageBuilder(chStore); @@ -1409,8 +1422,8 @@ void issue_2564() { setMaxLevelOnAllNodes(); contractNodes(0, 5, 2, 1, 3, 4); checkShortcuts( - createShortcut(1, 3, 1, 2, 1, 2, 17.497, true, false), - createShortcut(1, 3, 2, 1, 2, 1, 17.497, false, true) + createShortcut(1, 3, 2, 4, 1, 2, 17.497, true, false), + createShortcut(1, 3, 5, 3, 2, 1, 17.497, false, true) ); } @@ -1464,7 +1477,7 @@ private void setTurnCost(int from, int via, int to, double cost) { private void setTurnCost(EdgeIteratorState inEdge, EdgeIteratorState outEdge, int viaNode, double cost) { double cost1 = cost >= maxCost ? Double.POSITIVE_INFINITY : cost; - graph.getTurnCostStorage().set(((EncodedValueLookup) encoder).getDecimalEncodedValue(TurnCost.key("car")), inEdge.getEdge(), viaNode, outEdge.getEdge(), cost1); + graph.getTurnCostStorage().set(turnCostEnc, inEdge.getEdge(), viaNode, outEdge.getEdge(), cost1); } private EdgeIteratorState getEdge(int from, int to) { @@ -1476,15 +1489,15 @@ private Shortcut createShortcut(int from, int to, EdgeIteratorState edge1, EdgeI } private Shortcut createShortcut(int from, int to, EdgeIteratorState edge1, EdgeIteratorState edge2, double weight, boolean fwd, boolean bwd) { - return createShortcut(from, to, edge1.getEdge(), edge2.getEdge(), edge1.getEdge(), edge2.getEdge(), weight, fwd, bwd); + return createShortcut(from, to, edge1.getEdgeKey(), edge2.getEdgeKey(), edge1.getEdge(), edge2.getEdge(), weight, fwd, bwd); } - private Shortcut createShortcut(int from, int to, int firstOrigEdge, int lastOrigEdge, int skipEdge1, int skipEdge2, double weight) { - return createShortcut(from, to, firstOrigEdge, lastOrigEdge, skipEdge1, skipEdge2, weight, true, false); + private Shortcut createShortcut(int from, int to, int firstOrigEdgeKey, int lastOrigEdgeKey, int skipEdge1, int skipEdge2, double weight) { + return createShortcut(from, to, firstOrigEdgeKey, lastOrigEdgeKey, skipEdge1, skipEdge2, weight, true, false); } - private Shortcut createShortcut(int from, int to, int firstOrigEdge, int lastOrigEdge, int skipEdge1, int skipEdge2, double weight, boolean fwd, boolean bwd) { - return new Shortcut(from, to, firstOrigEdge, lastOrigEdge, skipEdge1, skipEdge2, weight, fwd, bwd); + private Shortcut createShortcut(int from, int to, int firstOrigEdgeKey, int lastOrigEdgeKey, int skipEdge1, int skipEdge2, double weight, boolean fwd, boolean bwd) { + return new Shortcut(from, to, firstOrigEdgeKey, lastOrigEdgeKey, skipEdge1, skipEdge2, weight, fwd, bwd); } /** @@ -1512,7 +1525,7 @@ private Set getCurrentShortcuts() { long ptr = chStore.toShortcutPointer(i); shortcuts.add(new Shortcut( chStore.getNodeA(ptr), chStore.getNodeB(ptr), - chStore.getOrigEdgeFirst(ptr), chStore.getOrigEdgeLast(ptr), + chStore.getOrigEdgeKeyFirst(ptr), chStore.getOrigEdgeKeyLast(ptr), chStore.getSkippedEdge1(ptr), chStore.getSkippedEdge2(ptr), chStore.getWeight(ptr), chStore.getFwdAccess(ptr), chStore.getBwdAccess(ptr) @@ -1532,20 +1545,20 @@ private void setMaxLevelOnAllNodes() { private static class Shortcut { int baseNode; int adjNode; - int firstOrigEdge; - int lastOrigEdge; + int firstOrigEdgeKey; + int lastOrigEdgeKey; double weight; boolean fwd; boolean bwd; int skipEdge1; int skipEdge2; - public Shortcut(int baseNode, int adjNode, int firstOrigEdge, int lastOrigEdge, int skipEdge1, int skipEdge2, double weight, + public Shortcut(int baseNode, int adjNode, int firstOrigEdgeKey, int lastOrigEdgeKey, int skipEdge1, int skipEdge2, double weight, boolean fwd, boolean bwd) { this.baseNode = baseNode; this.adjNode = adjNode; - this.firstOrigEdge = firstOrigEdge; - this.lastOrigEdge = lastOrigEdge; + this.firstOrigEdgeKey = firstOrigEdgeKey; + this.lastOrigEdgeKey = lastOrigEdgeKey; this.weight = weight; this.fwd = fwd; this.bwd = bwd; @@ -1560,8 +1573,8 @@ public boolean equals(Object o) { Shortcut shortcut = (Shortcut) o; return baseNode == shortcut.baseNode && adjNode == shortcut.adjNode && - firstOrigEdge == shortcut.firstOrigEdge && - lastOrigEdge == shortcut.lastOrigEdge && + firstOrigEdgeKey == shortcut.firstOrigEdgeKey && + lastOrigEdgeKey == shortcut.lastOrigEdgeKey && Double.compare(shortcut.weight, weight) == 0 && fwd == shortcut.fwd && bwd == shortcut.bwd && @@ -1571,7 +1584,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(baseNode, adjNode, firstOrigEdge, lastOrigEdge, weight, fwd, bwd, + return Objects.hash(baseNode, adjNode, firstOrigEdgeKey, lastOrigEdgeKey, weight, fwd, bwd, skipEdge1, skipEdge2); } @@ -1580,8 +1593,8 @@ public String toString() { return "Shortcut{" + "baseNode=" + baseNode + ", adjNode=" + adjNode + - ", firstOrigEdge=" + firstOrigEdge + - ", lastOrigEdge=" + lastOrigEdge + + ", firstOrigEdgeKey=" + firstOrigEdgeKey + + ", lastOrigEdgeKey=" + lastOrigEdgeKey + ", weight=" + weight + ", fwd=" + fwd + ", bwd=" + bwd + diff --git a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java index df9c9f42388..fa5bf1b4bf3 100644 --- a/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java +++ b/core/src/test/java/com/graphhopper/routing/ch/NodeBasedNodeContractorTest.java @@ -20,7 +20,13 @@ import com.graphhopper.routing.Dijkstra; import com.graphhopper.routing.DijkstraBidirectionCH; import com.graphhopper.routing.Path; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; +import com.graphhopper.routing.util.AllEdgesIterator; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.routing.weighting.Weighting; @@ -40,9 +46,10 @@ import static org.junit.jupiter.api.Assertions.*; public class NodeBasedNodeContractorTest { - private final FlagEncoder encoder = FlagEncoders.createCar(); - private final EncodingManager encodingManager = EncodingManager.create(encoder); - private final Weighting weighting = new ShortestWeighting(encoder); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + private final Weighting weighting = new ShortestWeighting(accessEnc, speedEnc); private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); private final CHConfig chConfig = CHConfig.nodeBased("profile", weighting); private CHStorage store; @@ -72,17 +79,17 @@ public void testDirectedGraph(boolean reverse) { //4-3_1<-\ 10 // \_|/ // 0___2_11 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(10, 2).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(11, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(10, 2).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(11, 2).setDistance(2)); // create a longer one directional edge => no longish one-dir shortcut should be created - final EdgeIteratorState edge2to1bidirected = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 1).setDistance(2)); - final EdgeIteratorState edge2to1directed = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(10)); - final EdgeIteratorState edge1to3 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 5).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 6).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 7).setDistance(2)); + final EdgeIteratorState edge2to1bidirected = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 1).setDistance(2)); + final EdgeIteratorState edge2to1directed = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(10)); + final EdgeIteratorState edge1to3 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 5).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 6).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 7).setDistance(2)); freeze(); setMaxLevelOnAllNodes(); @@ -104,13 +111,13 @@ public void testFindShortcuts_Roundabout() { // 1 -- 3 -- 4 ---> 5 ---> 6 -- 7 // \ / // <--- 8 <--- - final EdgeIteratorState iter1to3 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(1)); - final EdgeIteratorState iter3to4 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - final EdgeIteratorState iter4to5 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(4, 5).setDistance(1)); - final EdgeIteratorState iter5to6 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(1)); - final EdgeIteratorState iter6to8 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(6, 8).setDistance(2)); - final EdgeIteratorState iter8to4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(8, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(1)); + final EdgeIteratorState iter1to3 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1)); + final EdgeIteratorState iter3to4 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(1)); + final EdgeIteratorState iter4to5 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(4, 5).setDistance(1)); + final EdgeIteratorState iter5to6 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(1)); + final EdgeIteratorState iter6to8 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(6, 8).setDistance(2)); + final EdgeIteratorState iter8to4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(8, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(6, 7).setDistance(1)); freeze(); contractInOrder(3, 5, 7, 8, 4, 1, 6); @@ -142,9 +149,9 @@ public void testShortcutMergeBug(boolean reverse) { // where there are two roads from 1 to 2 and the directed road has a smaller weight. to get from 2 to 1 we // have to use the bidirectional edge despite the higher weight and therefore we need an extra shortcut for // this. - final EdgeIteratorState edge1to2bidirected = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(2)); - final EdgeIteratorState edge1to2directed = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(1)); - final EdgeIteratorState edge2to3 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(1)); + final EdgeIteratorState edge1to2bidirected = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); + final EdgeIteratorState edge1to2directed = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + final EdgeIteratorState edge2to3 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); if (reverse) { @@ -165,8 +172,8 @@ public void testShortcutMergeBug(boolean reverse) { @Test public void testContractNode_directed_shortcutRequired() { // 0 --> 1 --> 2 - final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(2)); + final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); freeze(); setMaxLevelOnAllNodes(); contractInOrder(1, 0, 2); @@ -176,8 +183,8 @@ public void testContractNode_directed_shortcutRequired() { @Test public void testContractNode_directed_shortcutRequired_reverse() { // 0 <-- 1 <-- 2 - final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 1).setDistance(1)); - final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 0).setDistance(2)); + final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 1).setDistance(1)); + final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 0).setDistance(2)); freeze(); setMaxLevelOnAllNodes(); contractInOrder(1, 2, 0); @@ -187,8 +194,8 @@ public void testContractNode_directed_shortcutRequired_reverse() { @Test public void testContractNode_bidirected_shortcutsRequired() { // 0 -- 1 -- 2 - final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(2)); + final EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + final EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); freeze(); contractInOrder(1, 2, 0); checkShortcuts(expectedShortcut(2, 0, edge2, edge1, true, true)); @@ -198,9 +205,9 @@ public void testContractNode_bidirected_shortcutsRequired() { public void testContractNode_directed_withWitness() { // 0 --> 1 --> 2 // \_________/ - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 2).setDistance(1)); freeze(); setMaxLevelOnAllNodes(); createNodeContractor().contractNode(1); @@ -214,11 +221,11 @@ public void testNodeContraction_shortcutDistanceRounding() { // \ / // 1 --> 2 --> 3 double[] distances = {4.019, 1.006, 1.004, 1.006, 1.004}; - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 4).setDistance(distances[0])); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(distances[1])); - EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(distances[2])); - EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(distances[3])); - EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(distances[4])); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 4).setDistance(distances[0])); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(distances[1])); + EdgeIteratorState edge2 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(distances[2])); + EdgeIteratorState edge3 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(distances[3])); + EdgeIteratorState edge4 = GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(distances[4])); freeze(); setMaxLevelOnAllNodes(); @@ -262,21 +269,22 @@ public void testNodeContraction_shortcutDistanceRounding() { */ @Test public void testNodeContraction_shortcutWeightRounding() { - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager encodingManager = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); // 0 ------------> 4 // \ / // 1 --> 2 --> 3 double fac = 60 / 3.6; double[] distances = {fac * 4.019, fac * 1.006, fac * 1.004, fac * 1.006, fac * 1.004}; - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 4).setDistance(distances[0])); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(distances[1])); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(distances[2])); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(distances[3])); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 4).setDistance(distances[4])); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 4).setDistance(distances[0])); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(distances[1])); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(distances[2])); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(distances[3])); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 4).setDistance(distances[4])); graph.freeze(); - Weighting weighting = new FastestWeighting(encoder); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); setMaxLevelOnAllNodes(chStore); @@ -301,19 +309,20 @@ public void testNodeContraction_shortcutWeightRounding() { public void testNodeContraction_preventUnnecessaryShortcutWithLoop() { // there should not be shortcuts where one of the skipped edges is a loop at the node to be contracted, // see also #1583 - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager encodingManager = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); // 0 - 1 - 2 - 3 // o o - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 0).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 3).setDistance(1)); graph.freeze(); - Weighting weighting = new FastestWeighting(encoder); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("p1", weighting); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); setMaxLevelOnAllNodes(chStore); diff --git a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java index 797e5331f01..46940b09828 100644 --- a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java +++ b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java @@ -22,6 +22,7 @@ import com.graphhopper.routing.Dijkstra; import com.graphhopper.routing.Path; import com.graphhopper.routing.RoutingAlgorithm; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.*; import com.graphhopper.routing.weighting.FastestWeighting; @@ -42,9 +43,10 @@ * @author Peter Karich */ public class PrepareContractionHierarchiesTest { - private final FlagEncoder carEncoder = FlagEncoders.createCar(new PMap().putObject("speed_two_directions", true)); - private final EncodingManager encodingManager = EncodingManager.create(carEncoder); - private final Weighting weighting = new ShortestWeighting(carEncoder); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + private final Weighting weighting = new ShortestWeighting(accessEnc, speedEnc); private final CHConfig chConfig = CHConfig.nodeBased("c", weighting); private BaseGraph g; @@ -52,68 +54,68 @@ public class PrepareContractionHierarchiesTest { // | ^ \ // | | | // 17-16-...-11<-/ - private static void initDirected2(Graph g, FlagEncoder encoder) { - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(6, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(7, 8).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(8, 9).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(9, 10).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(10, 11).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(11, 12).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(11, 9).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(12, 13).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(13, 14).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(14, 15).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(15, 16).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(16, 17).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(17, 0).setDistance(1)); + private static void initDirected2(Graph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 9).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(9, 10).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(10, 11).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(11, 12).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(11, 9).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(12, 13).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(13, 14).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(14, 15).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(15, 16).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(16, 17).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(17, 0).setDistance(1)); } // prepare-routing.svg - private static void initShortcutsGraph(Graph g, FlagEncoder encoder) { - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(1.5)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 9).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(9, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(10, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(6, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(7, 8).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(8, 9).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(4, 11).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(9, 14).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(10, 14).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(11, 12).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(12, 15).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(12, 13).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(13, 16).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(15, 16).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(14, 16).setDistance(1)); + private static void initShortcutsGraph(Graph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(1.5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 9).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(9, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(10, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 9).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 11).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(9, 14).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(10, 14).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(11, 12).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(12, 15).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(12, 13).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(13, 16).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(15, 16).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(14, 16).setDistance(1)); } - private static void initExampleGraph(Graph g, FlagEncoder encoder) { + private static void initExampleGraph(Graph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { //5-1-----2 // \ __/| // 0 | // / | // 4-----3 // - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 4).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(4, 3).setDistance(2)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(5, 1).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 4).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 3).setDistance(2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 1).setDistance(2)); } @BeforeEach @@ -134,7 +136,7 @@ public void testReturnsCorrectWeighting() { @Test public void testAddShortcuts() { - initExampleGraph(g, carEncoder); + initExampleGraph(g, accessEnc, speedEnc); PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g); useNodeOrdering(prepare, new int[]{5, 3, 4, 0, 1, 2}); PrepareContractionHierarchies.Result res = prepare.doWork(); @@ -143,7 +145,7 @@ public void testAddShortcuts() { @Test public void testMoreComplexGraph() { - initShortcutsGraph(g, carEncoder); + initShortcutsGraph(g, accessEnc, speedEnc); PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g); useNodeOrdering(prepare, new int[]{0, 5, 6, 7, 8, 10, 11, 13, 15, 1, 3, 9, 14, 16, 12, 4, 2}); PrepareContractionHierarchies.Result res = prepare.doWork(); @@ -152,12 +154,12 @@ public void testMoreComplexGraph() { @Test public void testDirectedGraph() { - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(5, 4).setDistance(3)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(4, 5).setDistance(10)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(2, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(5, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(3, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(4, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(5, 4).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(4, 5).setDistance(10)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(2, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(5, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(3, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(4, 3).setDistance(1)); g.freeze(); assertEquals(6, g.getEdges()); PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g); @@ -173,7 +175,7 @@ public void testDirectedGraph() { @Test public void testDirectedGraph2() { - initDirected2(g, carEncoder); + initDirected2(g, accessEnc, speedEnc); int oldCount = g.getEdges(); assertEquals(19, oldCount); PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g); @@ -193,7 +195,7 @@ public void testDirectedGraph2() { assertEquals(IntArrayList.from(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), p.calcNodes()); } - private static void initRoundaboutGraph(Graph g, FlagEncoder encoder) { + private static void initRoundaboutGraph(Graph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { // roundabout: //16-0-9-10--11 12<-13 // \ \ / \ @@ -201,52 +203,51 @@ private static void initRoundaboutGraph(Graph g, FlagEncoder encoder) { // -15-1--2--3--4 / / // / \-5->6/ / // -14 \________/ - - GHUtility.setSpeed(60, true, true, encoder, g.edge(16, 0).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 9).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 17).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(9, 10).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(10, 11).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(11, 28).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(28, 29).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(29, 30).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(30, 31).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(31, 4).setDistance(1)); - - GHUtility.setSpeed(60, true, true, encoder, g.edge(17, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(15, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(14, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(14, 18).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(18, 19).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(19, 20).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(20, 15).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(19, 21).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(21, 16).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(3, 4).setDistance(1)); - - GHUtility.setSpeed(60, true, false, encoder, g.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(6, 7).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(7, 13).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(13, 12).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(12, 4).setDistance(1)); - - GHUtility.setSpeed(60, true, true, encoder, g.edge(7, 8).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(8, 22).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(22, 23).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(23, 24).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(24, 25).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(25, 27).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(27, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(25, 26).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(26, 25).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(16, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 9).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 17).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(9, 10).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(10, 11).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(11, 28).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(28, 29).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(29, 30).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(30, 31).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(31, 4).setDistance(1)); + + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(17, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(15, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(14, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(14, 18).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(18, 19).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(19, 20).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(20, 15).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(19, 21).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(21, 16).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(1)); + + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(7, 13).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(13, 12).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(12, 4).setDistance(1)); + + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 22).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(22, 23).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(23, 24).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(24, 25).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(25, 27).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(27, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(25, 26).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(26, 25).setDistance(1)); } @Test public void testRoundaboutUnpacking() { - initRoundaboutGraph(g, carEncoder); + initRoundaboutGraph(g, accessEnc, speedEnc); int oldCount = g.getEdges(); PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g); useNodeOrdering(prepare, new int[]{26, 6, 12, 13, 2, 3, 8, 9, 10, 11, 14, 15, 16, 17, 18, 20, 21, 23, 24, 25, 19, 22, 27, 5, 29, 30, 31, 28, 7, 1, 0, 4}); @@ -271,14 +272,14 @@ public void testDisconnects() { // 2 // v // 7 - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(8, 3).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(3, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(6, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(1, 5).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(4, 0).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(0, 6).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(6, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(2, 7).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(8, 3).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(3, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(6, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(1, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(4, 0).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(0, 6).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(6, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(2, 7).setDistance(1)); g.freeze(); PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g) @@ -319,7 +320,7 @@ public void testStallOnDemandViaVirtuaNode_issue1574() { g = createGraph(); // use fastest weighting in this test to be able to fine-tune some weights via the speed (see below) - Weighting fastestWeighting = new FastestWeighting(carEncoder); + Weighting fastestWeighting = new FastestWeighting(accessEnc, speedEnc); CHConfig chConfig = CHConfig.nodeBased("c", fastestWeighting); // the following graph reproduces the issue. note that we will use the node ids as ch levels, so there will // be a shortcut 3->2 visible at node 2 and another one 3->4 visible at node 3. @@ -331,13 +332,13 @@ public void testStallOnDemandViaVirtuaNode_issue1574() { // start 0 - 3 - x - 1 - 2 // \ | // sc ---- 4 - 5 - 6 - 7 finish - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 3).setDistance(1)); - EdgeIteratorState edge31 = GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 2).setDistance(1)); - EdgeIteratorState edge24 = GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(4, 5).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(6, 7).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 3).setDistance(1)); + EdgeIteratorState edge31 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(1)); + EdgeIteratorState edge24 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(6, 7).setDistance(1)); updateDistancesFor(g, 0, 0.001, 0.0000); updateDistancesFor(g, 3, 0.001, 0.0001); updateDistancesFor(g, 1, 0.001, 0.0002); @@ -352,14 +353,14 @@ public void testStallOnDemandViaVirtuaNode_issue1574() { // at node 2 coming from 3. this happens because due to the virtual node x between 3 and 1, the weight of the // spt entry at 2 is different to the sum of the weights of the spt entry at node 3 and the shortcut edge. this // is due to different floating point rounding arithmetic of shortcuts and virtual edges on the query graph. - edge31.set(carEncoder.getAverageSpeedEnc(), 12, 12); + edge31.set(speedEnc, 12, 12); // just stalling node 2 alone would not lead to connection not found, because the shortcut 3-4 still finds node // 4. however, we can choose the weight of edge 2-4 such that node 4 also gets stalled via node 2. // it is important that node 2 gets stalled before otherwise node 4 would have already be discovered. // note that without the virtual node between 3 and 1 node 2 would not even be explored in the forward search, // but because of the virtual node the strict upward search is modified and goes like 0-3-x-1-2. - edge24.set(carEncoder.getAverageSpeedEnc(), 27.5, 27.5); + edge24.set(speedEnc, 27.5, 27.5); // prepare ch, use node ids as levels PrepareContractionHierarchies pch = createPrepareContractionHierarchies(g, chConfig); @@ -368,7 +369,7 @@ public void testStallOnDemandViaVirtuaNode_issue1574() { assertEquals(2, routingCHGraph.getEdges() - g.getEdges(), "there should be exactly two (bidirectional) shortcuts (2-3) and (3-4)"); // insert virtual node and edges - Snap snap = new Snap(0.0001, 0.0015); + Snap snap = new Snap(0.001, 0.00015); // between 3 and 1 snap.setClosestEdge(edge31); snap.setSnappedPosition(Snap.Position.EDGE); snap.setClosestNode(8); @@ -395,7 +396,7 @@ private double getWeight(Graph graph, Weighting w, int from, int to, boolean inc } private EdgeIteratorState getEdge(Graph graph, int from, int to, boolean incoming) { - EdgeFilter filter = incoming ? AccessFilter.inEdges(carEncoder.getAccessEnc()) : AccessFilter.outEdges(carEncoder.getAccessEnc()); + EdgeFilter filter = incoming ? AccessFilter.inEdges(accessEnc) : AccessFilter.outEdges(accessEnc); EdgeIterator iter = graph.createEdgeExplorer(filter).setBaseNode(from); while (iter.next()) { if (iter.getAdjNode() == to) { @@ -420,10 +421,10 @@ public void testCircleBug() { // /--1 // -0--/ // | - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 1).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 1).setDistance(4)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 2).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 3).setDistance(10)); PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g); PrepareContractionHierarchies.Result result = prepare.doWork(); assertEquals(0, result.getShortcuts()); @@ -436,15 +437,15 @@ public void testBug178() { // 0-1->-2--3--4 // \-<-/ // - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(2, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(2, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(5, 0).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(6, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(6, 3).setDistance(1)); PrepareContractionHierarchies prepare = createPrepareContractionHierarchies(g); useNodeOrdering(prepare, new int[]{4, 1, 2, 0, 5, 6, 3}); @@ -464,19 +465,24 @@ public void testBits() { @Test public void testMultiplePreparationsIdenticalView() { - FlagEncoder tmpCarEncoder = FlagEncoders.createCar(); - FlagEncoder tmpBikeEncoder = FlagEncoders.createBike(); - EncodingManager tmpEncodingManager = EncodingManager.create(tmpCarEncoder, tmpBikeEncoder); + BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); + DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); + EncodingManager tmpEncodingManager = EncodingManager.start() + .add(carAccessEnc).add(carSpeedEnc) + .add(bikeAccessEnc).add(bikeSpeedEnc) + .build(); // FastestWeighting would lead to different shortcuts due to different default speeds for bike and car - CHConfig carProfile = CHConfig.nodeBased("c1", new ShortestWeighting(tmpCarEncoder)); - CHConfig bikeProfile = CHConfig.nodeBased("c2", new ShortestWeighting(tmpBikeEncoder)); + CHConfig carProfile = CHConfig.nodeBased("c1", new ShortestWeighting(carAccessEnc, carSpeedEnc)); + CHConfig bikeProfile = CHConfig.nodeBased("c2", new ShortestWeighting(bikeAccessEnc, bikeSpeedEnc)); BaseGraph graph = new BaseGraph.Builder(tmpEncodingManager).create(); - initShortcutsGraph(graph, tmpCarEncoder); + initShortcutsGraph(graph, carAccessEnc, carSpeedEnc); AllEdgesIterator iter = graph.getAllEdges(); while (iter.next()) { - GHUtility.setSpeed(18, true, true, tmpBikeEncoder, iter); + GHUtility.setSpeed(18, true, true, bikeAccessEnc, bikeSpeedEnc, iter); } graph.freeze(); @@ -486,22 +492,27 @@ public void testMultiplePreparationsIdenticalView() { @Test public void testMultiplePreparationsDifferentView() { - FlagEncoder tmpCarEncoder = FlagEncoders.createCar(); - FlagEncoder tmpBikeEncoder = FlagEncoders.createBike(); - EncodingManager tmpEncodingManager = EncodingManager.create(tmpCarEncoder, tmpBikeEncoder); - - CHConfig carConfig = CHConfig.nodeBased("c1", new FastestWeighting(tmpCarEncoder)); - CHConfig bikeConfig = CHConfig.nodeBased("c2", new FastestWeighting(tmpBikeEncoder)); + BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); + DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); + EncodingManager tmpEncodingManager = EncodingManager.start() + .add(carAccessEnc).add(carSpeedEnc) + .add(bikeAccessEnc).add(bikeSpeedEnc) + .build(); + + CHConfig carConfig = CHConfig.nodeBased("c1", new FastestWeighting(carAccessEnc, carSpeedEnc)); + CHConfig bikeConfig = CHConfig.nodeBased("c2", new FastestWeighting(bikeAccessEnc, bikeSpeedEnc)); BaseGraph graph = new BaseGraph.Builder(tmpEncodingManager).create(); - initShortcutsGraph(graph, tmpCarEncoder); + initShortcutsGraph(graph, carAccessEnc, carSpeedEnc); AllEdgesIterator iter = graph.getAllEdges(); while (iter.next()) { - GHUtility.setSpeed(18, true, true, tmpBikeEncoder, iter); + GHUtility.setSpeed(18, true, true, bikeAccessEnc, bikeSpeedEnc, iter); } GHUtility.getEdge(graph, 9, 14). - set(tmpBikeEncoder.getAccessEnc(), false). - setReverse(tmpBikeEncoder.getAccessEnc(), false); + set(bikeAccessEnc, false). + setReverse(bikeAccessEnc, false); graph.freeze(); @@ -512,25 +523,31 @@ public void testMultiplePreparationsDifferentView() { @Test public void testReusingNodeOrdering() { - FlagEncoder car1FlagEncoder = FlagEncoders.createCar(new PMap("name=car1|turn_costs=true|speed_two_directions=true")); - FlagEncoder car2FlagEncoder = FlagEncoders.createCar(new PMap("name=car2|turn_costs=true|speed_two_directions=true")); - EncodingManager em = EncodingManager.create(car1FlagEncoder, car2FlagEncoder); - CHConfig car1Config = CHConfig.nodeBased("c1", new FastestWeighting(car1FlagEncoder)); - CHConfig car2Config = CHConfig.nodeBased("c2", new FastestWeighting(car2FlagEncoder)); + BooleanEncodedValue car1AccessEnc = new SimpleBooleanEncodedValue("car1_access", true); + BooleanEncodedValue car2AccessEnc = new SimpleBooleanEncodedValue("car2_access", true); + DecimalEncodedValue car1SpeedEnc = new DecimalEncodedValueImpl("car1_speed", 5, 5, true); + DecimalEncodedValue car2SpeedEnc = new DecimalEncodedValueImpl("car2_speed", 5, 5, true); + DecimalEncodedValue car1TurnCostEnc = TurnCost.create("car1", 1); + DecimalEncodedValue car2TurnCostEnc = TurnCost.create("car2", 1); + EncodingManager em = EncodingManager.start() + .add(car1AccessEnc).add(car1SpeedEnc).addTurnCostEncodedValue(car1TurnCostEnc) + .add(car2AccessEnc).add(car2SpeedEnc).addTurnCostEncodedValue(car2TurnCostEnc) + .build(); + CHConfig car1Config = CHConfig.nodeBased("c1", new FastestWeighting(car1AccessEnc, car1SpeedEnc)); + CHConfig car2Config = CHConfig.nodeBased("c2", new FastestWeighting(car2AccessEnc, car2SpeedEnc)); BaseGraph graph = new BaseGraph.Builder(em).create(); int numNodes = 5_000; int numQueries = 100; long seed = System.nanoTime(); Random rnd = new Random(seed); - GHUtility.buildRandomGraph(graph, rnd, numNodes, 1.3, true, true, - car1FlagEncoder.getAccessEnc(), null, null, 0.7, 0.9, 0.8); + GHUtility.buildRandomGraph(graph, rnd, numNodes, 1.3, true, true, car1AccessEnc, null, null, 0.7, 0.9, 0.8); AllEdgesIterator iter = graph.getAllEdges(); while (iter.next()) { - iter.set(car1FlagEncoder.getAccessEnc(), rnd.nextDouble() > 0.05, rnd.nextDouble() > 0.05); - iter.set(car2FlagEncoder.getAccessEnc(), rnd.nextDouble() > 0.05, rnd.nextDouble() > 0.05); - iter.set(car1FlagEncoder.getAverageSpeedEnc(), rnd.nextDouble() * 100, rnd.nextDouble() * 100); - iter.set(car2FlagEncoder.getAverageSpeedEnc(), rnd.nextDouble() * 100, rnd.nextDouble() * 100); + iter.set(car1AccessEnc, rnd.nextDouble() > 0.05, rnd.nextDouble() > 0.05); + iter.set(car2AccessEnc, rnd.nextDouble() > 0.05, rnd.nextDouble() > 0.05); + iter.set(car1SpeedEnc, rnd.nextDouble() * 100, rnd.nextDouble() * 100); + iter.set(car2SpeedEnc, rnd.nextDouble() * 100, rnd.nextDouble() * 100); } graph.freeze(); diff --git a/core/src/test/java/com/graphhopper/routing/ev/BooleanEncodedValueTest.java b/core/src/test/java/com/graphhopper/routing/ev/BooleanEncodedValueTest.java index 784ca09ea46..54247797256 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/BooleanEncodedValueTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/BooleanEncodedValueTest.java @@ -1,6 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -16,11 +15,12 @@ public void testBit() { BooleanEncodedValue bool = new SimpleBooleanEncodedValue("access", false); bool.init(config); - IntsRef ref = new IntsRef(1); - bool.setBool(false, ref, false); - assertFalse(bool.getBool(false, ref)); - bool.setBool(false, ref, true); - assertTrue(bool.getBool(false, ref)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + bool.setBool(false, edgeId, edgeIntAccess, false); + assertFalse(bool.getBool(false, edgeId, edgeIntAccess)); + bool.setBool(false, edgeId, edgeIntAccess, true); + assertTrue(bool.getBool(false, edgeId, edgeIntAccess)); } @Test @@ -28,11 +28,12 @@ public void testBitDirected() { EncodedValue.InitializerConfig config = new EncodedValue.InitializerConfig(); BooleanEncodedValue bool = new SimpleBooleanEncodedValue("access", true); bool.init(config); - IntsRef ref = new IntsRef(1); - bool.setBool(false, ref, false); - bool.setBool(true, ref, true); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + bool.setBool(false, edgeId, edgeIntAccess, false); + bool.setBool(true, edgeId, edgeIntAccess, true); - assertFalse(bool.getBool(false, ref)); - assertTrue(bool.getBool(true, ref)); + assertFalse(bool.getBool(false, edgeId, edgeIntAccess)); + assertTrue(bool.getBool(true, edgeId, edgeIntAccess)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/ev/DecimalEncodedValueImplTest.java b/core/src/test/java/com/graphhopper/routing/ev/DecimalEncodedValueImplTest.java index bc107187260..3fdb38cf5aa 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/DecimalEncodedValueImplTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/DecimalEncodedValueImplTest.java @@ -1,6 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; import java.util.Random; @@ -11,89 +10,112 @@ public class DecimalEncodedValueImplTest { @Test public void getDecimal() { - DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, 1, false, false); + DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, 1, false); testEnc.init(new EncodedValue.InitializerConfig()); - IntsRef intsRef = new IntsRef(1); - assertEquals(0, testEnc.getDecimal(false, intsRef), .1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + assertEquals(0, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); - testEnc.setDecimal(false, intsRef, 7); - assertEquals(7, testEnc.getDecimal(false, intsRef), .1); + testEnc.setDecimal(false, edgeId, edgeIntAccess, 7); + assertEquals(7, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); } @Test - public void testInfinityDefault() { - IntsRef intsRef = new IntsRef(1); - DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, 1, true, false); + public void setMaxToInfinity() { + DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, 0, 1, false, false, true); testEnc.init(new EncodedValue.InitializerConfig()); - assertTrue(Double.isInfinite(testEnc.getDecimal(false, intsRef))); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + assertEquals(0, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); - // set the default which maps to infinity (see discussion in #2473) - testEnc.setDecimal(false, intsRef, 0); - assertTrue(Double.isInfinite(testEnc.getDecimal(false, intsRef))); + assertTrue(Double.isInfinite(testEnc.getMaxOrMaxStorableDecimal())); + assertTrue(Double.isInfinite(testEnc.getMaxStorableDecimal())); + assertTrue(Double.isInfinite(testEnc.getNextStorableValue(7))); + assertEquals(6, testEnc.getNextStorableValue(6)); - assertTrue(Double.MAX_VALUE < testEnc.getDecimal(false, intsRef)); - testEnc.setDecimal(false, intsRef, Double.POSITIVE_INFINITY); - assertTrue(Double.isInfinite(testEnc.getDecimal(false, intsRef))); - } + testEnc.setDecimal(false, edgeId, edgeIntAccess, 5); + assertEquals(5, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); - @Test - public void setMaxToInfinity() { - DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, 0, 1, false, false, false, true); - testEnc.init(new EncodedValue.InitializerConfig()); - IntsRef intsRef = new IntsRef(1); - assertEquals(0, testEnc.getDecimal(false, intsRef), .1); + assertEquals(5, testEnc.getMaxOrMaxStorableDecimal()); + assertTrue(Double.isInfinite(testEnc.getMaxStorableDecimal())); - testEnc.setDecimal(false, intsRef, Double.POSITIVE_INFINITY); - assertEquals(Double.POSITIVE_INFINITY, testEnc.getDecimal(false, intsRef), .1); + testEnc.setDecimal(false, edgeId, edgeIntAccess, Double.POSITIVE_INFINITY); + assertEquals(Double.POSITIVE_INFINITY, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + assertTrue(Double.isInfinite(testEnc.getMaxOrMaxStorableDecimal())); + assertTrue(Double.isInfinite(testEnc.getMaxStorableDecimal())); } @Test public void testNegative() { - DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, -6, 0.1, false, false, false, true); + DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, -6, 0.1, false, false, true); testEnc.init(new EncodedValue.InitializerConfig()); - IntsRef intsRef = new IntsRef(1); - testEnc.setDecimal(false, intsRef, -5.5); - assertEquals(-5.5, testEnc.getDecimal(false, intsRef), .1); - assertEquals(-5.5, testEnc.getDecimal(true, intsRef), .1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + // a bit ugly: the default is the minimum not 0 + assertEquals(-6, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + + testEnc.setDecimal(false, edgeId, edgeIntAccess, -5.5); + assertEquals(-5.5, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + assertEquals(-5.5, testEnc.getDecimal(true, edgeId, edgeIntAccess), .1); Exception e = assertThrows(IllegalArgumentException.class, () -> { - new DecimalEncodedValueImpl("test", 3, -6, 0.11, false, false, false, true); + new DecimalEncodedValueImpl("test", 3, -6, 0.11, false, false, true); }); - assertTrue(e.getMessage().contains("minValue -6.0 is not a multiple of the specified factor"), e.getMessage()); + assertTrue(e.getMessage().contains("minStorableValue -6.0 is not a multiple of the specified factor"), e.getMessage()); } @Test public void testInfinityWithMinValue() { - DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, -6, 0.1, false, false, false, true); + DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 3, -6, 0.1, false, false, true); testEnc.init(new EncodedValue.InitializerConfig()); - IntsRef intsRef = new IntsRef(1); - testEnc.setDecimal(false, intsRef, Double.POSITIVE_INFINITY); - assertEquals(Double.POSITIVE_INFINITY, testEnc.getDecimal(false, intsRef), .1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + testEnc.setDecimal(false, edgeId, edgeIntAccess, Double.POSITIVE_INFINITY); + assertEquals(Double.POSITIVE_INFINITY, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); } @Test public void testNegateReverse() { - DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 4, 0, 0.5, false, true, false, false); + DecimalEncodedValueImpl testEnc = new DecimalEncodedValueImpl("test", 4, 0, 0.5, true, false, false); testEnc.init(new EncodedValue.InitializerConfig()); - IntsRef intsRef = new IntsRef(1); - testEnc.setDecimal(false, intsRef, 5.5); - assertEquals(5.5, testEnc.getDecimal(false, intsRef), .1); - assertEquals(-5.5, testEnc.getDecimal(true, intsRef), .1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + testEnc.setDecimal(false, edgeId, edgeIntAccess, 5.5); + assertEquals(5.5, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + assertEquals(-5.5, testEnc.getDecimal(true, edgeId, edgeIntAccess), .1); + + testEnc.setDecimal(false, edgeId, edgeIntAccess, -5.5); + assertEquals(-5.5, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + assertEquals(5.5, testEnc.getDecimal(true, edgeId, edgeIntAccess), .1); + + EncodedValue.InitializerConfig config = new EncodedValue.InitializerConfig(); + new DecimalEncodedValueImpl("tmp1", 5, 1, false).init(config); + testEnc = new DecimalEncodedValueImpl("tmp2", 5, 0, 1, true, false, false); + testEnc.init(config); + edgeIntAccess = new ArrayEdgeIntAccess(1); + testEnc.setDecimal(true, edgeId, edgeIntAccess, 2.6); + assertEquals(-3, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + assertEquals(3, testEnc.getDecimal(true, edgeId, edgeIntAccess), .1); + + testEnc.setDecimal(true, edgeId, edgeIntAccess, -2.6); + assertEquals(3, testEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + assertEquals(-3, testEnc.getDecimal(true, edgeId, edgeIntAccess), .1); } @Test public void testNextStorableValue() { DecimalEncodedValueImpl enc = new DecimalEncodedValueImpl("test", 4, 3, false); enc.init(new EncodedValue.InitializerConfig()); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; // some values can be stored... - enc.setDecimal(false, intsRef, 3); - assertEquals(3, enc.getDecimal(false, intsRef)); + enc.setDecimal(false, edgeId, edgeIntAccess, 3); + assertEquals(3, enc.getDecimal(false, edgeId, edgeIntAccess)); // ... and some cannot: - enc.setDecimal(false, intsRef, 5); - assertEquals(6, enc.getDecimal(false, intsRef)); + enc.setDecimal(false, edgeId, edgeIntAccess, 5); + assertEquals(6, enc.getDecimal(false, edgeId, edgeIntAccess)); // getNextStorableValue tells us the next highest value we can store without such modification between set/get assertEquals(0, enc.getNextStorableValue(0)); @@ -115,8 +137,8 @@ public void testNextStorableValue() { double value = rnd.nextDouble() * 45; double nextStorable = enc.getNextStorableValue(value); assertTrue(nextStorable >= value, "next storable value should be larger than the value"); - enc.setDecimal(false, intsRef, nextStorable); - assertEquals(nextStorable, enc.getDecimal(false, intsRef), "next storable value should be returned without modification"); + enc.setDecimal(false, edgeId, edgeIntAccess, nextStorable); + assertEquals(nextStorable, enc.getDecimal(false, edgeId, edgeIntAccess), "next storable value should be returned without modification"); } } @@ -130,34 +152,68 @@ public void smallestNonZeroValue() { assertSmallestNonZeroValue(new DecimalEncodedValueImpl("test", 5, 0.1, true), 0.1); assertTrue(assertThrows(IllegalStateException.class, - () -> new DecimalEncodedValueImpl("test", 5, 0, 5, false, true, false, false).getSmallestNonZeroValue()) + () -> new DecimalEncodedValueImpl("test", 5, 0, 5, true, false, false).getSmallestNonZeroValue()) .getMessage().contains("getting the smallest non-zero value is not possible")); } private void assertSmallestNonZeroValue(DecimalEncodedValueImpl enc, double expected) { enc.init(new EncodedValue.InitializerConfig()); assertEquals(expected, enc.getSmallestNonZeroValue()); - IntsRef intsRef = new IntsRef(1); - enc.setDecimal(false, intsRef, enc.getSmallestNonZeroValue()); - assertEquals(expected, enc.getDecimal(false, intsRef)); - enc.setDecimal(false, intsRef, enc.getSmallestNonZeroValue() / 2 - 0.01); - assertEquals(0, enc.getDecimal(false, intsRef)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + enc.setDecimal(false, edgeId, edgeIntAccess, enc.getSmallestNonZeroValue()); + assertEquals(expected, enc.getDecimal(false, edgeId, edgeIntAccess)); + enc.setDecimal(false, edgeId, edgeIntAccess, enc.getSmallestNonZeroValue() / 2 - 0.01); + assertEquals(0, enc.getDecimal(false, edgeId, edgeIntAccess)); } @Test public void testNextStorableValue_maxInfinity() { - DecimalEncodedValueImpl enc = new DecimalEncodedValueImpl("test", 4, 0, 3, false, false, false, true); + DecimalEncodedValueImpl enc = new DecimalEncodedValueImpl("test", 4, 0, 3, false, false, true); enc.init(new EncodedValue.InitializerConfig()); assertEquals(12, enc.getNextStorableValue(11.2)); - assertEquals(45, enc.getNextStorableValue(44.3)); - assertEquals(45, enc.getNextStorableValue(45)); + assertEquals(42, enc.getNextStorableValue(41.3)); + assertEquals(42, enc.getNextStorableValue(42)); + assertEquals(Double.POSITIVE_INFINITY, enc.getNextStorableValue(42.1)); + assertEquals(Double.POSITIVE_INFINITY, enc.getNextStorableValue(45)); assertEquals(Double.POSITIVE_INFINITY, enc.getNextStorableValue(45.1)); - assertEquals(Double.POSITIVE_INFINITY, enc.getNextStorableValue(48)); - assertEquals(Double.POSITIVE_INFINITY, enc.getNextStorableValue(48.1)); - IntsRef intsRef = new IntsRef(1); - assertThrows(IllegalArgumentException.class, () -> enc.setDecimal(false, intsRef, 48)); - enc.setDecimal(false, intsRef, Double.POSITIVE_INFINITY); - assertEquals(Double.POSITIVE_INFINITY, enc.getDecimal(false, intsRef)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + enc.setDecimal(false, edgeId, edgeIntAccess, 45); + assertEquals(42, enc.getDecimal(false, edgeId, edgeIntAccess)); + + enc.setDecimal(false, edgeId, edgeIntAccess, Double.POSITIVE_INFINITY); + assertEquals(Double.POSITIVE_INFINITY, enc.getDecimal(false, edgeId, edgeIntAccess)); } + @Test + public void lowestUpperBound_with_negateReverseDirection() { + DecimalEncodedValueImpl enc = new DecimalEncodedValueImpl("test", 4, 0, 3, true, false, false); + enc.init(new EncodedValue.InitializerConfig()); + assertEquals(15 * 3, enc.getMaxOrMaxStorableDecimal()); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + enc.setDecimal(false, edgeId, edgeIntAccess, 3); + assertEquals(3, enc.getDecimal(false, edgeId, edgeIntAccess)); + assertEquals(3, enc.getMaxOrMaxStorableDecimal()); + enc.setDecimal(true, edgeId, edgeIntAccess, -6); + assertEquals(6, enc.getDecimal(false, edgeId, edgeIntAccess)); + assertEquals(6, enc.getMaxOrMaxStorableDecimal()); + // note that the maximum is never lowered, even when we lower the value for the 'same' edge flags + enc.setDecimal(false, edgeId, edgeIntAccess, 0); + assertEquals(0, enc.getDecimal(false, edgeId, edgeIntAccess)); + assertEquals(6, enc.getMaxOrMaxStorableDecimal()); + } + + @Test + public void minStorableBug() { + DecimalEncodedValue enc = new DecimalEncodedValueImpl("test", 5, -3, 0.2, false, true, false); + enc.init(new EncodedValue.InitializerConfig()); + assertEquals(3.2, enc.getMaxStorableDecimal()); + + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + enc.setDecimal(true, edgeId, edgeIntAccess, 1.6); + assertEquals(1.6, enc.getDecimal(true, edgeId, edgeIntAccess)); + } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/ev/DecimalEncodedValueTest.java b/core/src/test/java/com/graphhopper/routing/ev/DecimalEncodedValueTest.java index 84a70bdf03f..e079585f465 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/DecimalEncodedValueTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/DecimalEncodedValueTest.java @@ -1,13 +1,10 @@ package com.graphhopper.routing.ev; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.util.CarTagParser; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; public class DecimalEncodedValueTest { @@ -15,38 +12,28 @@ public class DecimalEncodedValueTest { public void testInit() { DecimalEncodedValue prop = new DecimalEncodedValueImpl("test", 10, 2, false); prop.init(new EncodedValue.InitializerConfig()); - IntsRef ref = new IntsRef(1); - prop.setDecimal(false, ref, 10d); - assertEquals(10d, prop.getDecimal(false, ref), 0.1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + prop.setDecimal(false, edgeId, edgeIntAccess, 10d); + assertEquals(10d, prop.getDecimal(false, edgeId, edgeIntAccess), 0.1); } @Test public void testMaxValue() { - CarTagParser carEncoder = new CarTagParser(10, 0.5, 0); - EncodingManager em = EncodingManager.create(carEncoder); - DecimalEncodedValue carAverageSpeedEnc = em.getDecimalEncodedValue(EncodingManager.getKey(carEncoder, "average_speed")); - - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "motorway_link"); - way.setTag("maxspeed", "70 mph"); - IntsRef flags = carEncoder.handleWayTags(em.createEdgeFlags(), way); - assertEquals(101.5, carAverageSpeedEnc.getDecimal(true, flags), 1e-1); - - DecimalEncodedValue instance1 = new DecimalEncodedValueImpl("test1", 8, 0.5, false); - instance1.init(new EncodedValue.InitializerConfig()); - flags = em.createEdgeFlags(); - instance1.setDecimal(false, flags, 100d); - assertEquals(100, instance1.getDecimal(false, flags), 1e-1); + DecimalEncodedValue ev = new DecimalEncodedValueImpl("test1", 8, 0.5, false); + EncodingManager.start().add(ev).build(); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + ev.setDecimal(false, edgeId, edgeIntAccess, 100d); + assertEquals(100, ev.getDecimal(false, edgeId, edgeIntAccess), 1e-1); } @Test public void testNegativeBounds() { DecimalEncodedValue prop = new DecimalEncodedValueImpl("test", 10, 5, false); prop.init(new EncodedValue.InitializerConfig()); - try { - prop.setDecimal(false, new IntsRef(1), -1); - assertTrue(false); - } catch (Exception ex) { - } + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + assertThrows(Exception.class, () -> prop.setDecimal(false, edgeId, edgeIntAccess, -1)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/ev/EncodedValueSerializerTest.java b/core/src/test/java/com/graphhopper/routing/ev/EncodedValueSerializerTest.java index 97849f09ce1..95b3a467e46 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/EncodedValueSerializerTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/EncodedValueSerializerTest.java @@ -18,23 +18,27 @@ package com.graphhopper.routing.ev; +import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class EncodedValueSerializerTest { @Test public void serializationAndDeserialization() { List encodedValues = new ArrayList<>(); // add enum, int, decimal and boolean encoded values - encodedValues.add(new EnumEncodedValue<>(RoadClass.KEY, RoadClass.class)); - encodedValues.add(Lanes.create()); - encodedValues.add(MaxWidth.create()); - encodedValues.add(GetOffBike.create()); + DefaultEncodedValueFactory evFactory = new DefaultEncodedValueFactory(); + encodedValues.add(evFactory.create(RoadClass.KEY, new PMap())); + encodedValues.add(evFactory.create(Lanes.KEY, new PMap())); + encodedValues.add(evFactory.create(MaxWidth.KEY, new PMap())); + encodedValues.add(evFactory.create(GetOffBike.KEY, new PMap())); StringEncodedValue namesEnc = new StringEncodedValue("names", 3, Arrays.asList("jim", "joe", "kate"), false); encodedValues.add(namesEnc); @@ -64,14 +68,34 @@ public void serializationAndDeserialization() { } @Test - void wrongVersion() { - String serializedEV = "{\"className\":\"com.graphhopper.routing.ev.EnumEncodedValue\",\"name\":\"road_class\",\"bits\":5," + - "\"min_value\":0,\"max_value\":31,\"negate_reverse_direction\":false,\"store_two_directions\":false," + - "\"enum_type\":\"com.graphhopper.routing.ev.RoadClass\",\"version\":"; - // this fails, because the version is wrong - IllegalStateException e = assertThrows(IllegalStateException.class, () -> EncodedValueSerializer.deserializeEncodedValue(serializedEV + "404}")); - assertTrue(e.getMessage().contains("Version does not match"), e.getMessage()); - // this works - assertEquals("road_class", EncodedValueSerializer.deserializeEncodedValue(serializedEV + "979560347}").getName()); + void explicitString() { + EncodedValue.InitializerConfig initializerConfig = new EncodedValue.InitializerConfig(); + DefaultEncodedValueFactory evFactory = new DefaultEncodedValueFactory(); + List evs = Arrays.asList( + evFactory.create(Lanes.KEY, new PMap()), + evFactory.create(MaxWidth.KEY, new PMap()), + evFactory.create(GetOffBike.KEY, new PMap()) + ); + evs.forEach(ev -> ev.init(initializerConfig)); + + List serialized = evs.stream().map(EncodedValueSerializer::serializeEncodedValue).collect(Collectors.toList()); + assertEquals("{\"className\":\"com.graphhopper.routing.ev.IntEncodedValueImpl\",\"name\":\"lanes\",\"bits\":3," + + "\"min_storable_value\":0,\"max_storable_value\":7,\"max_value\":-2147483648,\"negate_reverse_direction\":false,\"store_two_directions\":false," + + "\"fwd_data_index\":0,\"bwd_data_index\":0,\"fwd_shift\":0,\"bwd_shift\":-1,\"fwd_mask\":7,\"bwd_mask\":0}", serialized.get(0)); + assertEquals("{\"className\":\"com.graphhopper.routing.ev.DecimalEncodedValueImpl\",\"name\":\"max_width\",\"bits\":7," + + "\"min_storable_value\":0,\"max_storable_value\":127,\"max_value\":-2147483648,\"negate_reverse_direction\":false,\"store_two_directions\":false," + + "\"fwd_data_index\":0,\"bwd_data_index\":0,\"fwd_shift\":3,\"bwd_shift\":-1,\"fwd_mask\":1016,\"bwd_mask\":0," + + "\"factor\":0.1,\"use_maximum_as_infinity\":true}", serialized.get(1)); + assertEquals("{\"className\":\"com.graphhopper.routing.ev.SimpleBooleanEncodedValue\",\"name\":\"get_off_bike\",\"bits\":1," + + "\"min_storable_value\":0,\"max_storable_value\":1,\"max_value\":-2147483648,\"negate_reverse_direction\":false,\"store_two_directions\":true,\"fwd_data_index\":0," + + "\"bwd_data_index\":0,\"fwd_shift\":10,\"bwd_shift\":11,\"fwd_mask\":1024,\"bwd_mask\":2048}", serialized.get(2)); + + EncodedValue ev0 = EncodedValueSerializer.deserializeEncodedValue(serialized.get(0)); + assertEquals("lanes", ev0.getName()); + EncodedValue ev1 = EncodedValueSerializer.deserializeEncodedValue(serialized.get(1)); + assertEquals("max_width", ev1.getName()); + EncodedValue ev2 = EncodedValueSerializer.deserializeEncodedValue(serialized.get(2)); + assertEquals("get_off_bike", ev2.getName()); } + } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/ev/EnumEncodedValueTest.java b/core/src/test/java/com/graphhopper/routing/ev/EnumEncodedValueTest.java index 32d03bb64d0..00ef4adf083 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/EnumEncodedValueTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/EnumEncodedValueTest.java @@ -1,6 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -9,19 +8,19 @@ public class EnumEncodedValueTest { @Test public void testInit() { - EnumEncodedValue prop = new EnumEncodedValue<>("road_class", RoadClass.class); + EnumEncodedValue prop = RoadClass.create(); EncodedValue.InitializerConfig init = new EncodedValue.InitializerConfig(); assertEquals(5, prop.init(init)); assertEquals(5, prop.bits); assertEquals(0, init.dataIndex); assertEquals(0, init.shift); - IntsRef ref = new IntsRef(1); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1); // default if empty - ref.ints[0] = 0; - assertEquals(RoadClass.OTHER, prop.getEnum(false, ref)); + intAccess.setInt(0, 0, 0); + assertEquals(RoadClass.OTHER, prop.getEnum(false, 0, intAccess)); - prop.setEnum(false, ref, RoadClass.SECONDARY); - assertEquals(RoadClass.SECONDARY, prop.getEnum(false, ref)); + prop.setEnum(false, 0, intAccess, RoadClass.SECONDARY); + assertEquals(RoadClass.SECONDARY, prop.getEnum(false, 0, intAccess)); } @Test diff --git a/core/src/test/java/com/graphhopper/routing/ev/IntEncodedValueImplTest.java b/core/src/test/java/com/graphhopper/routing/ev/IntEncodedValueImplTest.java index 964a71e7bb9..bec8f00dc37 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/IntEncodedValueImplTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/IntEncodedValueImplTest.java @@ -1,8 +1,10 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; +import java.util.Arrays; + +import static com.graphhopper.routing.ev.IntEncodedValueImpl.isValidEncodedValue; import static org.junit.jupiter.api.Assertions.*; public class IntEncodedValueImplTest { @@ -12,7 +14,7 @@ public void testInvalidReverseAccess() { IntEncodedValue prop = new IntEncodedValueImpl("test", 10, false); prop.init(new EncodedValue.InitializerConfig()); try { - prop.setInt(true, new IntsRef(1), -1); + prop.setInt(true, 0, createIntAccess(1), -1); fail(); } catch (Exception ex) { } @@ -22,33 +24,42 @@ public void testInvalidReverseAccess() { public void testDirectedValue() { IntEncodedValue prop = new IntEncodedValueImpl("test", 10, true); prop.init(new EncodedValue.InitializerConfig()); - IntsRef ref = new IntsRef(1); - prop.setInt(false, ref, 10); - prop.setInt(true, ref, 20); - assertEquals(10, prop.getInt(false, ref)); - assertEquals(20, prop.getInt(true, ref)); + EdgeIntAccess edgeIntAccess = createIntAccess(1); + prop.setInt(false, 0, edgeIntAccess, 10); + prop.setInt(true, 0, edgeIntAccess, 20); + assertEquals(10, prop.getInt(false, 0, edgeIntAccess)); + assertEquals(20, prop.getInt(true, 0, edgeIntAccess)); } @Test public void multiIntsUsage() { IntEncodedValue prop = new IntEncodedValueImpl("test", 31, true); prop.init(new EncodedValue.InitializerConfig()); - IntsRef ref = new IntsRef(2); - prop.setInt(false, ref, 10); - prop.setInt(true, ref, 20); - assertEquals(10, prop.getInt(false, ref)); - assertEquals(20, prop.getInt(true, ref)); + EdgeIntAccess edgeIntAccess = createIntAccess(2); + prop.setInt(false, 0, edgeIntAccess, 10); + prop.setInt(true, 0, edgeIntAccess, 20); + assertEquals(10, prop.getInt(false, 0, edgeIntAccess)); + assertEquals(20, prop.getInt(true, 0, edgeIntAccess)); } @Test public void padding() { IntEncodedValue prop = new IntEncodedValueImpl("test", 30, true); prop.init(new EncodedValue.InitializerConfig()); - IntsRef ref = new IntsRef(2); - prop.setInt(false, ref, 10); - prop.setInt(true, ref, 20); - assertEquals(10, prop.getInt(false, ref)); - assertEquals(20, prop.getInt(true, ref)); + EdgeIntAccess edgeIntAccess = createIntAccess(2); + prop.setInt(false, 0, edgeIntAccess, 10); + prop.setInt(true, 0, edgeIntAccess, 20); + assertEquals(10, prop.getInt(false, 0, edgeIntAccess)); + assertEquals(20, prop.getInt(true, 0, edgeIntAccess)); + } + + @Test + public void maxValue() { + IntEncodedValue prop = new IntEncodedValueImpl("test", 31, false); + prop.init(new EncodedValue.InitializerConfig()); + EdgeIntAccess edgeIntAccess = createIntAccess(2); + prop.setInt(false, 0, edgeIntAccess, (1 << 31) - 1); + assertEquals(2_147_483_647L, prop.getInt(false, 0, edgeIntAccess)); } @Test @@ -57,15 +68,15 @@ public void testSignedInt() { EncodedValue.InitializerConfig config = new EncodedValue.InitializerConfig(); prop.init(config); - IntsRef ref = new IntsRef(1); + EdgeIntAccess edgeIntAccess = createIntAccess(1); Exception exception = assertThrows(IllegalArgumentException.class, () -> { - prop.setInt(false, ref, Integer.MAX_VALUE); + prop.setInt(false, 0, edgeIntAccess, Integer.MAX_VALUE); }); assertTrue(exception.getMessage().contains("test value too large for encoding"), exception.getMessage()); - prop.setInt(false, ref, -5); - assertEquals(-5, prop.getInt(false, ref)); - assertEquals(-5, prop.getInt(false, ref)); + prop.setInt(false, 0, edgeIntAccess, -5); + assertEquals(-5, prop.getInt(false, 0, edgeIntAccess)); + assertEquals(-5, prop.getInt(false, 0, edgeIntAccess)); } @Test @@ -74,12 +85,12 @@ public void testSignedInt2() { EncodedValue.InitializerConfig config = new EncodedValue.InitializerConfig(); prop.init(config); - IntsRef ref = new IntsRef(1); - prop.setInt(false, ref, Integer.MAX_VALUE); - assertEquals(Integer.MAX_VALUE, prop.getInt(false, ref)); + EdgeIntAccess edgeIntAccess = createIntAccess(1); + prop.setInt(false, 0, edgeIntAccess, Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, prop.getInt(false, 0, edgeIntAccess)); Exception exception = assertThrows(IllegalArgumentException.class, () -> { - prop.setInt(false, ref, -5); + prop.setInt(false, 0, edgeIntAccess, -5); }); assertTrue(exception.getMessage().contains("test value too small for encoding"), exception.getMessage()); } @@ -90,17 +101,38 @@ public void testNegateReverseDirection() { EncodedValue.InitializerConfig config = new EncodedValue.InitializerConfig(); prop.init(config); - IntsRef ref = new IntsRef(1); - prop.setInt(false, ref, 5); - assertEquals(5, prop.getInt(false, ref)); - assertEquals(-5, prop.getInt(true, ref)); + EdgeIntAccess edgeIntAccess = createIntAccess(1); + prop.setInt(false, 0, edgeIntAccess, 5); + assertEquals(5, prop.getInt(false, 0, edgeIntAccess)); + assertEquals(-5, prop.getInt(true, 0, edgeIntAccess)); + + prop.setInt(true, 0, edgeIntAccess, 2); + assertEquals(-2, prop.getInt(false, 0, edgeIntAccess)); + assertEquals(2, prop.getInt(true, 0, edgeIntAccess)); + + prop.setInt(false, 0, edgeIntAccess, -3); + assertEquals(-3, prop.getInt(false, 0, edgeIntAccess)); + assertEquals(3, prop.getInt(true, 0, edgeIntAccess)); + } + + @Test + public void testEncodedValueName() { + for (String str : Arrays.asList("blup_test", "test", "test12", "car_test_test")) { + assertTrue(isValidEncodedValue(str), str); + } - prop.setInt(true, ref, 2); - assertEquals(-2, prop.getInt(false, ref)); - assertEquals(2, prop.getInt(true, ref)); + for (String str : Arrays.asList("Test", "12test", "test|3", "car__test", "small_car$average_speed", "tes$0", + "blup_te.st_", "car___test", "car$$access", "test{34", "truck__average_speed", "blup.test", "test,21", + "täst", "blup.two.three", "blup..test")) { + assertFalse(isValidEncodedValue(str), str); + } + + for (String str : Arrays.asList("break", "switch")) { + assertFalse(isValidEncodedValue(str), str); + } + } - prop.setInt(false, ref, -3); - assertEquals(-3, prop.getInt(false, ref)); - assertEquals(3, prop.getInt(true, ref)); + private static ArrayEdgeIntAccess createIntAccess(int ints) { + return new ArrayEdgeIntAccess(ints); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/ev/MaxWeightTest.java b/core/src/test/java/com/graphhopper/routing/ev/MaxWeightTest.java index 652d65030fe..d5245b0c0fd 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/MaxWeightTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/MaxWeightTest.java @@ -1,6 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -11,12 +10,13 @@ public class MaxWeightTest { public void testSetAndGet() { DecimalEncodedValue mappedDecimalEnc = MaxWeight.create(); mappedDecimalEnc.init(new EncodedValue.InitializerConfig()); - IntsRef intsRef = new IntsRef(1); - mappedDecimalEnc.setDecimal(false, intsRef, 20); - assertEquals(20, mappedDecimalEnc.getDecimal(false, intsRef), .1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + mappedDecimalEnc.setDecimal(false, edgeId, edgeIntAccess, 20); + assertEquals(20, mappedDecimalEnc.getDecimal(false, edgeId, edgeIntAccess), .1); - intsRef = new IntsRef(1); - mappedDecimalEnc.setDecimal(false, intsRef, Double.POSITIVE_INFINITY); - assertEquals(Double.POSITIVE_INFINITY, mappedDecimalEnc.getDecimal(false, intsRef), .1); + edgeIntAccess = new ArrayEdgeIntAccess(1); + mappedDecimalEnc.setDecimal(false, edgeId, edgeIntAccess, Double.POSITIVE_INFINITY); + assertEquals(Double.POSITIVE_INFINITY, mappedDecimalEnc.getDecimal(false, edgeId, edgeIntAccess), .1); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/ev/StringEncodedValueTest.java b/core/src/test/java/com/graphhopper/routing/ev/StringEncodedValueTest.java index 82b0f8082d0..6b411317211 100644 --- a/core/src/test/java/com/graphhopper/routing/ev/StringEncodedValueTest.java +++ b/core/src/test/java/com/graphhopper/routing/ev/StringEncodedValueTest.java @@ -1,6 +1,5 @@ package com.graphhopper.routing.ev; -import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -58,8 +57,8 @@ public void testNull() { StringEncodedValue prop = new StringEncodedValue("country", 3); prop.init(new EncodedValue.InitializerConfig()); - IntsRef ref = new IntsRef(1); - prop.setString(false, ref, null); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + prop.setString(false, 0, edgeIntAccess, null); assertEquals(0, prop.getValues().size()); } @@ -80,24 +79,24 @@ public void testLookup() { StringEncodedValue prop = new StringEncodedValue("country", 3); prop.init(new EncodedValue.InitializerConfig()); - IntsRef ref = new IntsRef(1); - assertEquals(null, prop.getString(false, ref)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + assertEquals(null, prop.getString(false, 0, edgeIntAccess)); assertEquals(0, prop.getValues().size()); - prop.setString(false, ref, "aut"); - assertEquals("aut", prop.getString(false, ref)); + prop.setString(false, 0, edgeIntAccess, "aut"); + assertEquals("aut", prop.getString(false, 0, edgeIntAccess)); assertEquals(1, prop.getValues().size()); - prop.setString(false, ref, "deu"); - assertEquals("deu", prop.getString(false, ref)); + prop.setString(false, 0, edgeIntAccess, "deu"); + assertEquals("deu", prop.getString(false, 0, edgeIntAccess)); assertEquals(2, prop.getValues().size()); - prop.setString(false, ref, "che"); - assertEquals("che", prop.getString(false, ref)); + prop.setString(false, 0, edgeIntAccess, "che"); + assertEquals("che", prop.getString(false, 0, edgeIntAccess)); assertEquals(3, prop.getValues().size()); - prop.setString(false, ref, "deu"); - assertEquals("deu", prop.getString(false, ref)); + prop.setString(false, 0, edgeIntAccess, "deu"); + assertEquals("deu", prop.getString(false, 0, edgeIntAccess)); assertEquals(3, prop.getValues().size()); } @@ -106,20 +105,20 @@ public void testStoreTooManyEntries() { StringEncodedValue prop = new StringEncodedValue("country", 3); prop.init(new EncodedValue.InitializerConfig()); - IntsRef ref = new IntsRef(1); - assertEquals(null, prop.getString(false, ref)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + assertEquals(null, prop.getString(false, 0, edgeIntAccess)); - prop.setString(false, ref, "aut"); - assertEquals("aut", prop.getString(false, ref)); + prop.setString(false, 0, edgeIntAccess, "aut"); + assertEquals("aut", prop.getString(false, 0, edgeIntAccess)); - prop.setString(false, ref, "deu"); - assertEquals("deu", prop.getString(false, ref)); + prop.setString(false, 0, edgeIntAccess, "deu"); + assertEquals("deu", prop.getString(false, 0, edgeIntAccess)); - prop.setString(false, ref, "che"); - assertEquals("che", prop.getString(false, ref)); + prop.setString(false, 0, edgeIntAccess, "che"); + assertEquals("che", prop.getString(false, 0, edgeIntAccess)); try { - prop.setString(false, ref, "xyz"); + prop.setString(false, 0, edgeIntAccess, "xyz"); fail("The encoded value should only allow a limited number of values"); } catch (IllegalStateException e) { assertTrue(e.getMessage().startsWith("Maximum number of values reached for")); diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMApproximatorTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMApproximatorTest.java index c5686aede95..1e1d716db15 100644 --- a/core/src/test/java/com/graphhopper/routing/lm/LMApproximatorTest.java +++ b/core/src/test/java/com/graphhopper/routing/lm/LMApproximatorTest.java @@ -20,15 +20,16 @@ import com.graphhopper.routing.Dijkstra; import com.graphhopper.routing.Path; -import com.graphhopper.routing.ev.Subnetwork; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.*; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Directory; import com.graphhopper.storage.RAMDirectory; import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.RepeatedTest; import java.util.Random; @@ -45,15 +46,16 @@ public void randomGraph() { private void run(long seed) { Directory dir = new RAMDirectory(); - FlagEncoder encoder = FlagEncoders.createCar(new PMap("turn_costs=true")); - EncodingManager encodingManager = new EncodingManager.Builder().add(encoder).add(Subnetwork.create("car")).build(); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + DecimalEncodedValue turnCostEnc = TurnCost.create("car", 1); + EncodingManager encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).add(Subnetwork.create("car")).build(); BaseGraph graph = new BaseGraph.Builder(encodingManager).setDir(dir).withTurnCosts(true).create(); Random rnd = new Random(seed); - GHUtility.buildRandomGraph(graph, rnd, 100, 2.2, true, true, - encoder.getAccessEnc(), encoder.getAverageSpeedEnc(), null, 0.7, 0.8, 0.8); + GHUtility.buildRandomGraph(graph, rnd, 100, 2.2, true, true, accessEnc, speedEnc, null, 0.7, 0.8, 0.8); - Weighting weighting = new FastestWeighting(encoder); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc); PrepareLandmarks lm = new PrepareLandmarks(dir, graph, encodingManager, new LMConfig("car", weighting), 16); lm.setMaximumWeight(10000); @@ -61,13 +63,13 @@ private void run(long seed) { LandmarkStorage landmarkStorage = lm.getLandmarkStorage(); for (int t = 0; t < graph.getNodes(); t++) { - LMApproximator lmApproximator = new LMApproximator(graph, weighting, graph.getNodes(), landmarkStorage, 8, landmarkStorage.getFactor(), false); + LMApproximator lmApproximator = new LMApproximator(graph, weighting, weighting, graph.getNodes(), landmarkStorage, 8, landmarkStorage.getFactor(), false); WeightApproximator reverseLmApproximator = lmApproximator.reverse(); BeelineWeightApproximator beelineApproximator = new BeelineWeightApproximator(graph.getNodeAccess(), weighting); WeightApproximator reverseBeelineApproximator = beelineApproximator.reverse(); PerfectApproximator perfectApproximator = new PerfectApproximator(graph, weighting, TraversalMode.NODE_BASED, false); PerfectApproximator reversePerfectApproximator = new PerfectApproximator(graph, weighting, TraversalMode.NODE_BASED, true); - BalancedWeightApproximator balancedWeightApproximator = new BalancedWeightApproximator(new LMApproximator(graph, weighting, graph.getNodes(), landmarkStorage, 8, landmarkStorage.getFactor(), false)); + BalancedWeightApproximator balancedWeightApproximator = new BalancedWeightApproximator(new LMApproximator(graph, weighting, weighting, graph.getNodes(), landmarkStorage, 8, landmarkStorage.getFactor(), false)); lmApproximator.setTo(t); beelineApproximator.setTo(t); @@ -106,7 +108,7 @@ private void run(long seed) { // That's a requirement for normal A*-implementations, because if it is violated, // the heap-weight of settled nodes can decrease, and that would mean our // stopping criterion is not sufficient. - EdgeIterator neighbors = graph.createEdgeExplorer(AccessFilter.outEdges(encoder.getAccessEnc())).setBaseNode(v); + EdgeIterator neighbors = graph.createEdgeExplorer(AccessFilter.outEdges(accessEnc)).setBaseNode(v); while (neighbors.next()) { int w = neighbors.getAdjNode(); double vw = weighting.calcEdgeWeight(neighbors, false); @@ -117,7 +119,7 @@ private void run(long seed) { } } - neighbors = graph.createEdgeExplorer(AccessFilter.outEdges(encoder.getAccessEnc())).setBaseNode(v); + neighbors = graph.createEdgeExplorer(AccessFilter.outEdges(accessEnc)).setBaseNode(v); while (neighbors.next()) { int w = neighbors.getAdjNode(); double vw = weighting.calcEdgeWeight(neighbors, false); diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java index 1944d53b57e..40c75c2011c 100644 --- a/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java +++ b/core/src/test/java/com/graphhopper/routing/lm/LMIssueTest.java @@ -19,11 +19,8 @@ package com.graphhopper.routing.lm; import com.graphhopper.routing.*; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; @@ -31,7 +28,6 @@ import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.RAMDirectory; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -44,7 +40,8 @@ public class LMIssueTest { private Directory dir; private BaseGraph graph; - private FlagEncoder encoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; private Weighting weighting; private LandmarkStorage lm; private EncodingManager encodingManager; @@ -61,12 +58,15 @@ private enum Algo { @BeforeEach public void init() { dir = new RAMDirectory(); - encoder = FlagEncoders.createCar(new PMap("turn_costs=true")); - encodingManager = new EncodingManager.Builder().add(encoder).add(Subnetwork.create("car")).build(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + DecimalEncodedValue turnCostEnc = TurnCost.create("car", 1); + encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).add(Subnetwork.create("car")).build(); graph = new BaseGraph.Builder(encodingManager) + .withTurnCosts(true) .setDir(dir) .create(); - weighting = new FastestWeighting(encoder); + weighting = new FastestWeighting(accessEnc, speedEnc); } private void preProcessGraph() { @@ -115,7 +115,6 @@ public void lm_problem_to_node_of_fallback_approximator(Algo algo) { // \ | | // 3 | // 2 --<---- - DecimalEncodedValue speedEnc = encoder.getAverageSpeedEnc(); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 49.405150, 9.709054); na.setNode(1, 49.403705, 9.700517); @@ -123,13 +122,13 @@ public void lm_problem_to_node_of_fallback_approximator(Algo algo) { na.setNode(3, 49.403009, 9.708364); na.setNode(4, 49.409021, 9.703622); // 30s - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 3).setDistance(1000)).set(speedEnc, 120); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 2).setDistance(1000)).set(speedEnc, 120); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 3).setDistance(1000)).set(speedEnc, 120); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 2).setDistance(1000)).set(speedEnc, 120); // 360s - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(1000)).set(speedEnc, 10); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3).setDistance(1000)).set(speedEnc, 10); // 80s - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1000)).set(speedEnc, 45); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(1000)).set(speedEnc, 45); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1000)).set(speedEnc, 45); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 4).setDistance(1000)).set(speedEnc, 45); preProcessGraph(); int source = 0; @@ -154,7 +153,6 @@ public void lm_issue2(Algo algo) { // \ / // ->- NodeAccess na = graph.getNodeAccess(); - DecimalEncodedValue speedEnc = encoder.getAverageSpeedEnc(); na.setNode(0, 49.406987, 9.709767); na.setNode(1, 49.403612, 9.702953); na.setNode(2, 49.409755, 9.706517); @@ -165,13 +163,13 @@ public void lm_issue2(Algo algo) { na.setNode(7, 49.406965, 9.702660); na.setNode(8, 49.405227, 9.702863); na.setNode(9, 49.409411, 9.709085); - GHUtility.setSpeed(112, true, true, encoder, graph.edge(0, 1).setDistance(623.197000)); - GHUtility.setSpeed(13, true, true, encoder, graph.edge(5, 1).setDistance(741.414000)); - GHUtility.setSpeed(35, true, true, encoder, graph.edge(9, 4).setDistance(1140.835000)); - GHUtility.setSpeed(18, true, true, encoder, graph.edge(5, 6).setDistance(670.689000)); - GHUtility.setSpeed(88, true, false, encoder, graph.edge(5, 9).setDistance(80.731000)); - GHUtility.setSpeed(82, true, true, encoder, graph.edge(0, 9).setDistance(273.948000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 0).setDistance(956.552000)); + GHUtility.setSpeed(112, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(623.197000)); + GHUtility.setSpeed(13, true, true, accessEnc, speedEnc, graph.edge(5, 1).setDistance(741.414000)); + GHUtility.setSpeed(35, true, true, accessEnc, speedEnc, graph.edge(9, 4).setDistance(1140.835000)); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, graph.edge(5, 6).setDistance(670.689000)); + GHUtility.setSpeed(88, true, false, accessEnc, speedEnc, graph.edge(5, 9).setDistance(80.731000)); + GHUtility.setSpeed(82, true, true, accessEnc, speedEnc, graph.edge(0, 9).setDistance(273.948000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 0).setDistance(956.552000)); preProcessGraph(); int source = 5; diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java index 1c221ad22ac..30f95866607 100644 --- a/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java +++ b/core/src/test/java/com/graphhopper/routing/lm/LMPreparationHandlerTest.java @@ -3,9 +3,11 @@ import com.graphhopper.GraphHopperConfig; import com.graphhopper.config.LMProfile; import com.graphhopper.config.Profile; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.storage.BaseGraph; @@ -35,11 +37,12 @@ public void maximumLMWeight() { new LMProfile("conf1").setMaximumLMWeight(65_000), new LMProfile("conf2").setMaximumLMWeight(20_000) ); - FlagEncoder car = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(car); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", false); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); List lmConfigs = Arrays.asList( - new LMConfig("conf1", new FastestWeighting(car)), - new LMConfig("conf2", new ShortestWeighting(car)) + new LMConfig("conf1", new FastestWeighting(accessEnc, speedEnc)), + new LMConfig("conf2", new ShortestWeighting(accessEnc, speedEnc)) ); List preparations = handler.createPreparations(lmConfigs, new BaseGraph.Builder(em).build(), em, null); assertEquals(1, preparations.get(0).getLandmarkStorage().getFactor(), .1); diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMProfileSelectorTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMProfileSelectorTest.java deleted file mode 100644 index 903500f4047..00000000000 --- a/core/src/test/java/com/graphhopper/routing/lm/LMProfileSelectorTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.graphhopper.routing.lm; - -import com.graphhopper.config.CHProfile; -import com.graphhopper.config.LMProfile; -import com.graphhopper.config.Profile; -import com.graphhopper.routing.ProfileResolver; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; -import com.graphhopper.util.PMap; -import com.graphhopper.util.Parameters; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - - -public class LMProfileSelectorTest { - - private static final String MULTIPLE_MATCHES_ERROR = "There are multiple LM profiles matching your request. Use the `weighting`, `vehicle` and `turn_costs` parameters to be more specific"; - private static final String NO_MATCH_ERROR = "Cannot find matching LM profile for your request"; - - private EncodingManager encodingManager; - private Profile fastCar; - private Profile fastCarEdge; - private Profile fastBike; - private Profile shortBikeEdge; - - @BeforeEach - public void setup() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - FlagEncoder bikeEncoder = FlagEncoders.createBike(); - encodingManager = EncodingManager.create(carEncoder, bikeEncoder); - fastCar = new Profile("fast_car").setVehicle("car").setWeighting("fastest").setTurnCosts(false); - fastCarEdge = new Profile("fast_car_edge").setVehicle("car").setWeighting("fastest").setTurnCosts(true); - fastBike = new Profile("fast_bike").setVehicle("bike").setWeighting("fastest").setTurnCosts(false); - shortBikeEdge = new Profile("short_bike_edge").setVehicle("bike").setWeighting("shortest").setTurnCosts(true); - } - - @Test - public void singleProfile() { - List profiles = Arrays.asList( - fastCar - ); - List lmProfiles = Arrays.asList( - new LMProfile("fast_car") - ); - // as long as we do not request something that does not fit the existing profile we have a match - assertProfileFound(profiles.get(0), profiles, lmProfiles, null, null, null, null); - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", null, null, null); - assertProfileFound(profiles.get(0), profiles, lmProfiles, null, "fastest", null, null); - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", "fastest", null, null); - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", "fastest", false, null); - - // requesting edge_based when the profile is not edge_based leads to a non-match - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, null, null, true, null); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, "car", null, true, null); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, null, "fastest", true, null); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, "car", "fastest", true, null); - - // requesting u_turn_costs should not lead to a non-match - assertProfileFound(profiles.get(0), profiles, lmProfiles, null, null, null, 54); - assertProfileFound(profiles.get(0), profiles, lmProfiles, null, null, false, 54); - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", null, false, 54); - assertProfileFound(profiles.get(0), profiles, lmProfiles, null, "fastest", false, 54); - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", "fastest", false, 54); - - // if we request something that does not fit we do not get a match - String error = assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, "bike", null, null, null); - assertTrue(error.contains("requested: *|bike|turn_costs=*"), error); - assertTrue(error.contains("available: [fastest|car|turn_costs=false]"), error); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, "car", "shortest", null, null); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, "car", null, true, null); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, null, "shortest", null, null); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, "truck", "short_fastest", null, null); - } - - @Test - public void multipleProfiles() { - List profiles = Arrays.asList( - fastCar, - fastBike - ); - List lmProfiles = Arrays.asList( - new LMProfile("fast_car"), - new LMProfile("fast_bike") - ); - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", null, null, null); - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", "fastest", null, null); - assertProfileFound(profiles.get(1), profiles, lmProfiles, "bike", null, null, null); - assertProfileFound(profiles.get(1), profiles, lmProfiles, "bike", "fastest", null, null); - - // not specific enough - String error = assertLMProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, lmProfiles, null, "fastest", null, null); - assertTrue(error.contains("requested: fastest|*|turn_costs=*"), error); - assertTrue(error.contains("matched: [fastest|car|turn_costs=false, fastest|bike|turn_costs=false]"), error); - assertTrue(error.contains("available: [fastest|car|turn_costs=false, fastest|bike|turn_costs=false]"), error); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, null, "shortest", null, null); - - // u_turn_costs is set, but lm should not really care - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", null, null, 54); - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", null, false, 64); - } - - @Test - public void withAndWithoutTurnCosts() { - List profiles = Arrays.asList( - fastCar, - fastBike, - fastCarEdge, - shortBikeEdge - ); - List lmProfiles = Arrays.asList( - new LMProfile("fast_car"), - new LMProfile("fast_bike"), - new LMProfile("fast_car_edge"), - new LMProfile("short_bike_edge") - ); - // edge_based can be used to select between otherwise identical profiles - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", null, false, null); - assertProfileFound(profiles.get(2), profiles, lmProfiles, "car", null, true, null); - - // in case there are two matching profiles and they are only different by turn_costs=true/false the one with - // turn costs is preferred (just like for CH) - assertProfileFound(profiles.get(2), profiles, lmProfiles, "car", null, null, null); - - // not being specific enough leads to multiple matching profiles error - assertLMProfileSelectionError(MULTIPLE_MATCHES_ERROR, profiles, lmProfiles, null, "fastest", null, null); - - // we get an error if we request turn_costs that are not supported - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, "bike", "fastest", true, null); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, "bike", "shortest", false, null); - assertLMProfileSelectionError(NO_MATCH_ERROR, profiles, lmProfiles, null, "shortest", false, null); - - // vehicle&weighting are derived from the available profiles in case they are not given - assertProfileFound(profiles.get(0), profiles, lmProfiles, "car", null, false, null); - assertProfileFound(profiles.get(1), profiles, lmProfiles, "bike", null, false, null); - assertProfileFound(profiles.get(2), profiles, lmProfiles, null, "fastest", true, null); - assertProfileFound(profiles.get(3), profiles, lmProfiles, null, "shortest", true, null); - } - - private void assertProfileFound(Profile expectedProfile, List profiles, List lmProfiles, String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) { - PMap hintsMap = createHintsMap(vehicle, weighting, edgeBased, uTurnCosts); - try { - Profile selectedProfile = new ProfileResolver(encodingManager, profiles, Collections.emptyList(), lmProfiles).selectProfileLM(hintsMap); - assertEquals(expectedProfile, selectedProfile); - } catch (IllegalArgumentException e) { - fail("no profile found\nexpected: " + expectedProfile + "\nerror: " + e.getMessage()); - } - } - - private String assertLMProfileSelectionError(String expectedError, List profiles, List lmProfiles, String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) { - PMap hintsMap = createHintsMap(vehicle, weighting, edgeBased, uTurnCosts); - try { - new ProfileResolver(encodingManager, profiles, Collections.emptyList(), lmProfiles).selectProfileLM(hintsMap); - fail("There should have been an error"); - return ""; - } catch (IllegalArgumentException e) { - assertTrue(e.getMessage().contains(expectedError), - "There should have been an error message containing:\n'" + expectedError + "'\nbut was:\n'" + e.getMessage() + "'"); - return e.getMessage(); - } - } - - private PMap createHintsMap(String vehicle, String weighting, Boolean edgeBased, Integer uTurnCosts) { - PMap hintsMap = new PMap(); - if (weighting != null) - hintsMap.putObject("weighting", weighting); - if (vehicle != null) - hintsMap.putObject("vehicle", vehicle); - if (edgeBased != null) - hintsMap.putObject(Parameters.Routing.EDGE_BASED, edgeBased); - if (uTurnCosts != null) - hintsMap.putObject(Parameters.Routing.U_TURN_COSTS, uTurnCosts); - return hintsMap; - } -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java b/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java index 62bd1d9eb74..cac17ab4678 100644 --- a/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java +++ b/core/src/test/java/com/graphhopper/routing/lm/LandmarkStorageTest.java @@ -18,13 +18,10 @@ package com.graphhopper.routing.lm; import com.graphhopper.routing.RoutingAlgorithmTest; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.subnetwork.PrepareRoutingSubnetworks; import com.graphhopper.routing.util.AreaIndex; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; @@ -48,15 +45,17 @@ */ public class LandmarkStorageTest { private BaseGraph graph; - private FlagEncoder encoder; private BooleanEncodedValue subnetworkEnc; private EncodingManager encodingManager; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; @BeforeEach public void setUp() { - encoder = FlagEncoders.createCar(); subnetworkEnc = Subnetwork.create("car"); - encodingManager = new EncodingManager.Builder().add(encoder).add(subnetworkEnc).build(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(subnetworkEnc).build(); graph = new BaseGraph.Builder(encodingManager).create(); } @@ -70,7 +69,7 @@ public void tearDown() { public void testInfiniteWeight() { Directory dir = new RAMDirectory(); EdgeIteratorState edge = graph.edge(0, 1); - int res = new LandmarkStorage(graph, encodingManager, dir, new LMConfig("c1", new FastestWeighting(encoder) { + int res = new LandmarkStorage(graph, encodingManager, dir, new LMConfig("c1", new FastestWeighting(accessEnc, speedEnc) { @Override public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { return Integer.MAX_VALUE * 2L; @@ -79,7 +78,7 @@ public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { assertEquals(Integer.MAX_VALUE, res); dir = new RAMDirectory(); - res = new LandmarkStorage(graph, encodingManager, dir, new LMConfig("c2", new FastestWeighting(encoder) { + res = new LandmarkStorage(graph, encodingManager, dir, new LMConfig("c2", new FastestWeighting(accessEnc, speedEnc) { @Override public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { return Double.POSITIVE_INFINITY; @@ -90,9 +89,9 @@ public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { @Test public void testSetGetWeight() { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(40.1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(40.1)); Directory dir = new RAMDirectory(); - LandmarkStorage lms = new LandmarkStorage(graph, encodingManager, dir, new LMConfig("c1", new FastestWeighting(encoder)), 4). + LandmarkStorage lms = new LandmarkStorage(graph, encodingManager, dir, new LMConfig("c1", new FastestWeighting(accessEnc, speedEnc)), 4). setMaximumWeight(LandmarkStorage.PRECISION); lms._getInternalDA().create(2000); // 2^16=65536, use -1 for infinity and -2 for maximum @@ -116,14 +115,14 @@ public void testSetGetWeight() { @Test public void testWithSubnetworks() { // 0-1-2..4-5->6 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10.1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10.2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10.1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10.2)); - graph.edge(2, 4).set(encoder.getAccessEnc(), false, false); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(10.5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 6).setDistance(10.6)); + graph.edge(2, 4).set(accessEnc, false, false); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(10.5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 6).setDistance(10.6)); - Weighting weighting = new FastestWeighting(encoder); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc); // 1 means => 2 allowed edge keys => excludes the node 6 subnetworkRemoval(weighting, 1); @@ -139,13 +138,13 @@ public void testWithSubnetworks() { @Test public void testWithStronglyConnectedComponent() { // 0 - 1 - 2 = 3 - 4 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10.1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(10.2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(10.3)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(3, 2).setDistance(10.2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(10.4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10.1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10.2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10.3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(3, 2).setDistance(10.2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4).setDistance(10.4)); - Weighting weighting = new FastestWeighting(encoder); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc); // 3 nodes => 6 allowed edge keys but still do not exclude 3 & 4 as strongly connected and not a too small subnetwork! subnetworkRemoval(weighting, 4); @@ -169,14 +168,14 @@ private void subnetworkRemoval(Weighting weighting, int minNodeSize) { public void testWithOnewaySubnetworks() { // 0 -- 1 -> 2 -> 3 // 4 -- 5 ->/ - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(10.1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(10.2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 3).setDistance(10.3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10.1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10.2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10.3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(10.5)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(5, 2).setDistance(10.2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(4, 5).setDistance(10.5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(5, 2).setDistance(10.2)); - Weighting weighting = new FastestWeighting(encoder); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc); // 1 allowed node => 2 allowed edge keys (exclude 2 and 3 because they are separate too small oneway subnetworks) subnetworkRemoval(weighting, 1); @@ -192,11 +191,11 @@ public void testWithOnewaySubnetworks() { @Test public void testWeightingConsistence1() { // create an indifferent problem: shortest weighting can pass the speed==0 edge but fastest cannot (?) - graph.edge(0, 1).setDistance(10.1).set(encoder.getAccessEnc(), true, true); - GHUtility.setSpeed(30, true, true, encoder, graph.edge(1, 2).setDistance(10)); - graph.edge(2, 3).setDistance(10.1).set(encoder.getAccessEnc(), true, true); + graph.edge(0, 1).setDistance(10.1).set(accessEnc, true, true); + GHUtility.setSpeed(30, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(10)); + graph.edge(2, 3).setDistance(10.1).set(accessEnc, true, true); - LandmarkStorage storage = new LandmarkStorage(graph, encodingManager, new RAMDirectory(), new LMConfig("car", new FastestWeighting(encoder)), 2); + LandmarkStorage storage = new LandmarkStorage(graph, encodingManager, new RAMDirectory(), new LMConfig("car", new FastestWeighting(accessEnc, speedEnc)), 2); storage.setMinimumNodes(2); storage.createLandmarks(); @@ -206,11 +205,11 @@ public void testWeightingConsistence1() { @Test public void testWeightingConsistence2() { - GHUtility.setSpeed(30, true, true, encoder, graph.edge(0, 1).setDistance(10)); - graph.edge(2, 3).setDistance(10.1).set(encoder.getAccessEnc(), true, true); - GHUtility.setSpeed(30, true, true, encoder, graph.edge(2, 3).setDistance(10)); + GHUtility.setSpeed(30, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(10)); + graph.edge(2, 3).setDistance(10.1).set(accessEnc, true, true); + GHUtility.setSpeed(30, true, true, accessEnc, speedEnc, graph.edge(2, 3).setDistance(10)); - LandmarkStorage storage = new LandmarkStorage(graph, encodingManager, new RAMDirectory(), new LMConfig("car", new FastestWeighting(encoder)), 2); + LandmarkStorage storage = new LandmarkStorage(graph, encodingManager, new RAMDirectory(), new LMConfig("car", new FastestWeighting(accessEnc, speedEnc)), 2); storage.setMinimumNodes(2); storage.createLandmarks(); @@ -221,9 +220,9 @@ public void testWeightingConsistence2() { @Test public void testWithBorderBlocking() { - RoutingAlgorithmTest.initBiGraph(graph, encoder); + RoutingAlgorithmTest.initBiGraph(graph, accessEnc, speedEnc); - LandmarkStorage storage = new LandmarkStorage(graph, encodingManager, new RAMDirectory(), new LMConfig("car", new FastestWeighting(encoder)), 2); + LandmarkStorage storage = new LandmarkStorage(graph, encodingManager, new RAMDirectory(), new LMConfig("car", new FastestWeighting(accessEnc, speedEnc)), 2); final SplitArea right = new SplitArea(emptyList()); final SplitArea left = new SplitArea(emptyList()); final AreaIndex areaIndex = new AreaIndex(emptyList()) { diff --git a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java index b9df8f81a32..d4b3b956497 100644 --- a/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java +++ b/core/src/test/java/com/graphhopper/routing/lm/PrepareLandmarksTest.java @@ -21,11 +21,11 @@ import com.graphhopper.routing.AlgorithmOptions; import com.graphhopper.routing.Path; import com.graphhopper.routing.RoutingAlgorithm; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.QueryGraph; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; @@ -56,17 +56,19 @@ * @author Peter Karich */ public class PrepareLandmarksTest { + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; + private EncodingManager encodingManager; private BaseGraph graph; - private FlagEncoder encoder; private TraversalMode tm; - private EncodingManager encodingManager; @BeforeEach public void setUp() { - encoder = FlagEncoders.createCar(); - tm = TraversalMode.NODE_BASED; - encodingManager = new EncodingManager.Builder().add(encoder).add(Subnetwork.create("car")).build(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + encodingManager = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(Subnetwork.create("car")).build(); graph = new BaseGraph.Builder(encodingManager).create(); + tm = TraversalMode.NODE_BASED; } @Test @@ -77,8 +79,6 @@ public void testLandmarkStorageAndRouting() { Random rand = new Random(0); int width = 15, height = 15; - DecimalEncodedValue avSpeedEnc = encoder.getAverageSpeedEnc(); - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); for (int hIndex = 0; hIndex < height; hIndex++) { for (int wIndex = 0; wIndex < width; wIndex++) { int node = wIndex + hIndex * width; @@ -86,11 +86,11 @@ public void testLandmarkStorageAndRouting() { // do not connect first with last column! double speed = 20 + rand.nextDouble() * 30; if (wIndex + 1 < width) - graph.edge(node, node + 1).set(accessEnc, true, true).set(avSpeedEnc, speed); + graph.edge(node, node + 1).set(accessEnc, true, true).set(speedEnc, speed); // avoid dead ends if (hIndex + 1 < height) - graph.edge(node, node + width).set(accessEnc, true, true).set(avSpeedEnc, speed); + graph.edge(node, node + width).set(accessEnc, true, true).set(speedEnc, speed); updateDistancesFor(graph, node, -hIndex / 50.0, wIndex / 50.0); } @@ -100,7 +100,7 @@ public void testLandmarkStorageAndRouting() { index.prepareIndex(); int lm = 5, activeLM = 2; - Weighting weighting = new FastestWeighting(encoder); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc); LMConfig lmConfig = new LMConfig("car", weighting); LandmarkStorage store = new LandmarkStorage(graph, encodingManager, dir, lmConfig, lm); store.setMinimumNodes(2); @@ -156,7 +156,7 @@ public void testLandmarkStorageAndRouting() { assertEquals(expectedPath.getWeight(), path.getWeight(), .1); assertEquals(expectedPath.calcNodes(), path.calcNodes()); - assertEquals(expectedAlgo.getVisitedNodes() - 135, oneDirAlgoWithLandmarks.getVisitedNodes()); + assertEquals(expectedAlgo.getVisitedNodes() - 73, oneDirAlgoWithLandmarks.getVisitedNodes()); // landmarks with bidir A* RoutingAlgorithm biDirAlgoWithLandmarks = new LMRoutingAlgorithmFactory(lms).createAlgo(graph, weighting, @@ -164,7 +164,7 @@ public void testLandmarkStorageAndRouting() { path = biDirAlgoWithLandmarks.calcPath(41, 183); assertEquals(expectedPath.getWeight(), path.getWeight(), .1); assertEquals(expectedPath.calcNodes(), path.calcNodes()); - assertEquals(expectedAlgo.getVisitedNodes() - 162, biDirAlgoWithLandmarks.getVisitedNodes()); + assertEquals(expectedAlgo.getVisitedNodes() - 95, biDirAlgoWithLandmarks.getVisitedNodes()); // landmarks with A* and a QueryGraph. We expect slightly less optimal as two more cycles needs to be traversed // due to the two more virtual nodes but this should not harm in practise @@ -179,18 +179,18 @@ public void testLandmarkStorageAndRouting() { expectedPath = expectedAlgo.calcPath(fromSnap.getClosestNode(), toSnap.getClosestNode()); assertEquals(expectedPath.getWeight(), path.getWeight(), .1); assertEquals(expectedPath.calcNodes(), path.calcNodes()); - assertEquals(expectedAlgo.getVisitedNodes() - 135, qGraphOneDirAlgo.getVisitedNodes()); + assertEquals(expectedAlgo.getVisitedNodes() - 73, qGraphOneDirAlgo.getVisitedNodes()); } @Test public void testStoreAndLoad() { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(80_000)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(80_000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(80_000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(80_000)); String fileStr = "./target/tmp-lm"; Helper.removeDir(new File(fileStr)); Directory dir = new RAMDirectory(fileStr, true).create(); - Weighting weighting = new FastestWeighting(encoder); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc); LMConfig lmConfig = new LMConfig("car", weighting); PrepareLandmarks plm = new PrepareLandmarks(dir, graph, encodingManager, lmConfig, 2); plm.setMinimumNodes(2); diff --git a/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java b/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java index 6503e73b47d..62dfeb90c65 100644 --- a/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java +++ b/core/src/test/java/com/graphhopper/routing/querygraph/QueryGraphTest.java @@ -20,10 +20,10 @@ import com.carrotsearch.hppc.IntArrayList; import com.carrotsearch.hppc.IntObjectMap; import com.graphhopper.routing.HeadingResolver; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; @@ -51,13 +51,15 @@ */ public class QueryGraphTest { private EncodingManager encodingManager; - private FlagEncoder encoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; private BaseGraph g; @BeforeEach public void setUp() { - encoder = FlagEncoders.createCar(); - encodingManager = EncodingManager.create(encoder); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); g = new BaseGraph.Builder(encodingManager).create(); } @@ -76,8 +78,8 @@ void initGraph(Graph g) { na.setNode(0, 1, 0); na.setNode(1, 1, 2.5); na.setNode(2, 0, 0); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 2).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)). + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)). setWayGeometry(Helper.createPointList(1.5, 1, 1.5, 1.5)); } @@ -157,8 +159,8 @@ public void testFillVirtualEdges() { na.setNode(1, 1, 2.5); na.setNode(2, 0, 0); na.setNode(3, 0, 1); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 2).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)).setWayGeometry(Helper.createPointList(1.5, 1, 1.5, 1.5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)).setWayGeometry(Helper.createPointList(1.5, 1, 1.5, 1.5)); g.edge(1, 3); final int baseNode = 1; @@ -238,7 +240,7 @@ public void testOneWay() { NodeAccess na = g.getNodeAccess(); na.setNode(0, 0, 0); na.setNode(1, 0, 1); - GHUtility.setSpeed(60, true, false, encoder, g.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)); EdgeIteratorState edge = GHUtility.getEdge(g, 0, 1); Snap res1 = createLocationResult(0.1, 0.1, edge, 0, EDGE); @@ -301,10 +303,10 @@ public void testLoopStreet_Issue151() { // | | // x--- // - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(3, 4).setDistance(10)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 3).setDistance(20)).setWayGeometry(Helper.createPointList(-0.001, 0.001, -0.001, 0.002)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(10)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 3).setDistance(20)).setWayGeometry(Helper.createPointList(-0.001, 0.001, -0.001, 0.002)); updateDistancesFor(g, 0, 0, 0); updateDistancesFor(g, 1, 0, 0.001); updateDistancesFor(g, 3, 0, 0.002); @@ -333,9 +335,9 @@ public void testOneWayLoop_Issue162() { NodeAccess na = g.getNodeAccess(); na.setNode(0, 0, 0); na.setNode(1, 0, -0.001); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)); - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - DecimalEncodedValue avSpeedEnc = encoder.getAverageSpeedEnc(); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)); + BooleanEncodedValue accessEnc = this.accessEnc; + DecimalEncodedValue avSpeedEnc = speedEnc; // in the case of identical nodes the wayGeometry defines the direction! EdgeIteratorState edge = g.edge(0, 0). setDistance(100). @@ -389,9 +391,10 @@ public void testAvoidDuplicateVirtualNodesIfIdentical() { assertEquals(3, res1.getClosestNode()); assertEquals(3, res2.getClosestNode()); - // force skip due to **tower** node snapping in phase 2, but no virtual edges should be created for res1 + // force skip due to **tower** node snapping in phase 2 (QueryOverlayBuilder.buildVirtualEdges -> Snap.considerEqual) + // and no virtual edges should be created for res1 edgeState = GHUtility.getEdge(g, 0, 1); - res1 = createLocationResult(1, 0, edgeState, 0, EDGE); + res1 = createLocationResult(1, 0, edgeState, 0, TOWER); // now create virtual edges edgeState = GHUtility.getEdge(g, 0, 2); res2 = createLocationResult(0.5, 0, edgeState, 0, EDGE); @@ -402,6 +405,24 @@ public void testAvoidDuplicateVirtualNodesIfIdentical() { assertEquals(GHUtility.asSet(1, 3), GHUtility.getNeighbors(iter)); } + @Test + void towerSnapWhenCrossingPointIsOnEdgeButCloseToTower() { + g.getNodeAccess().setNode(0, 49.000000, 11.00100); + g.getNodeAccess().setNode(1, 49.000000, 11.00200); + g.getNodeAccess().setNode(2, 49.000300, 11.00200); + g.edge(0, 1).set(accessEnc, true, true); + g.edge(1, 2).set(accessEnc, true, true); + LocationIndexTree locationIndex = new LocationIndexTree(g, new RAMDirectory()); + locationIndex.prepareIndex(); + Snap snap = locationIndex.findClosest(49.0000010, 11.00800, EdgeFilter.ALL_EDGES); + // Our query point is quite far away from the edge and further away from the tower node than from the crossing + // point along the edge. But since the crossing point is very near to the tower node we still want it to be a + // tower-snap to prevent a virtual node with a very short virtual edge + assertEquals(Snap.Position.TOWER, snap.getSnappedPosition()); + QueryGraph queryGraph = QueryGraph.create(g, snap); + assertEquals(g.getNodes(), queryGraph.getNodes()); + } + @Test public void testGetEdgeProps() { initGraph(g); @@ -435,8 +456,8 @@ public Snap createLocationResult(double lat, double lon, @Test public void testIteration_Issue163() { - EdgeFilter outEdgeFilter = AccessFilter.outEdges(encodingManager.getEncoder("car").getAccessEnc()); - EdgeFilter inEdgeFilter = AccessFilter.inEdges(encodingManager.getEncoder("car").getAccessEnc()); + EdgeFilter outEdgeFilter = AccessFilter.outEdges(accessEnc); + EdgeFilter inEdgeFilter = AccessFilter.inEdges(accessEnc); EdgeExplorer inExplorer = g.createEdgeExplorer(inEdgeFilter); EdgeExplorer outExplorer = g.createEdgeExplorer(outEdgeFilter); @@ -451,7 +472,7 @@ public void testIteration_Issue163() { */ g.getNodeAccess().setNode(nodeA, 1, 0); g.getNodeAccess().setNode(nodeB, 1, 10); - GHUtility.setSpeed(60, true, false, encoder, g.edge(nodeA, nodeB).setDistance(10)). + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(nodeA, nodeB).setDistance(10)). setWayGeometry(Helper.createPointList(1.5, 3, 1.5, 7)); // assert the behavior for classic edgeIterator @@ -459,8 +480,8 @@ public void testIteration_Issue163() { // setup snaps EdgeIteratorState it = GHUtility.getEdge(g, nodeA, nodeB); - Snap snap1 = createLocationResult(1.5, 3, it, 1, Snap.Position.EDGE); - Snap snap2 = createLocationResult(1.5, 7, it, 2, Snap.Position.EDGE); + Snap snap1 = createLocationResult(1.5, 3, it, 1, PILLAR); + Snap snap2 = createLocationResult(1.5, 7, it, 2, PILLAR); QueryGraph q = lookup(Arrays.asList(snap1, snap2)); int nodeC = snap1.getClosestNode(); @@ -495,20 +516,21 @@ private void assertEdgeIdsStayingEqual(EdgeExplorer inExplorer, EdgeExplorer out @Test public void testTurnCostsProperlyPropagated_Issue282() { - FlagEncoder encoder = FlagEncoders.createCar(new PMap("max_turn_costs=15")); - EncodingManager em = EncodingManager.create(encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + DecimalEncodedValue turnCostEnc = TurnCost.create("car", 15); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); BaseGraph graphWithTurnCosts = new BaseGraph.Builder(em).withTurnCosts(true).create(); TurnCostStorage turnExt = graphWithTurnCosts.getTurnCostStorage(); - DecimalEncodedValue turnCostEnc = em.getDecimalEncodedValue(TurnCost.key(encoder.toString())); NodeAccess na = graphWithTurnCosts.getNodeAccess(); na.setNode(0, .00, .00); na.setNode(1, .00, .01); na.setNode(2, .01, .01); - EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, encoder, graphWithTurnCosts.edge(0, 1).setDistance(10)); - EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, encoder, graphWithTurnCosts.edge(2, 1).setDistance(10)); + EdgeIteratorState edge0 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graphWithTurnCosts.edge(0, 1).setDistance(10)); + EdgeIteratorState edge1 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graphWithTurnCosts.edge(2, 1).setDistance(10)); - Weighting weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graphWithTurnCosts.getTurnCostStorage())); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graphWithTurnCosts.getTurnCostStorage())); // no turn costs initially assertEquals(0, weighting.calcTurnWeight(edge0.getEdge(), 1, edge1.getEdge()), .1); @@ -555,7 +577,7 @@ public void testEnforceHeading() { NodeAccess na = g.getNodeAccess(); na.setNode(0, 0, 0); na.setNode(1, 0, 2); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)). + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)). setWayGeometry(Helper.createPointList(2, 0, 2, 2)); EdgeIteratorState edge = GHUtility.getEdge(g, 0, 1); @@ -623,7 +645,7 @@ public void testUnfavoredEdgeDirections() { // 2 na.setNode(0, 0, 0); na.setNode(1, 0, 2); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)); Snap snap = fakeEdgeSnap(edge, 0, 1, 0); QueryGraph queryGraph = QueryGraph.create(g, snap); @@ -653,7 +675,7 @@ public void testUnfavorVirtualEdgePair() { NodeAccess na = g.getNodeAccess(); na.setNode(0, 0, 0); na.setNode(1, 0, 2); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)). + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)). setWayGeometry(Helper.createPointList(2, 0, 2, 2)); EdgeIteratorState edge = GHUtility.getEdge(g, 0, 1); @@ -686,7 +708,6 @@ public void testInternalAPIOriginalEdgeKey() { EdgeExplorer explorer = g.createEdgeExplorer(); EdgeIterator iter = explorer.setBaseNode(1); assertTrue(iter.next()); - int origEdgeId = iter.getEdge(); Snap res = createLocationResult(2, 1.5, iter, 1, PILLAR); QueryGraph queryGraph = lookup(res); @@ -696,14 +717,14 @@ public void testInternalAPIOriginalEdgeKey() { EdgeExplorer qGraphExplorer = queryGraph.createEdgeExplorer(); iter = qGraphExplorer.setBaseNode(3); assertTrue(iter.next()); + assertEquals(2, iter.getEdge()); assertEquals(0, iter.getAdjNode()); - assertEquals(GHUtility.createEdgeKey(1, 0, origEdgeId, false), - ((VirtualEdgeIteratorState) queryGraph.getEdgeIteratorState(iter.getEdge(), 0)).getOriginalEdgeKey()); + assertEquals(3, ((VirtualEdgeIteratorState) queryGraph.getEdgeIteratorState(iter.getEdge(), 0)).getOriginalEdgeKey()); assertTrue(iter.next()); + assertEquals(3, iter.getEdge()); assertEquals(1, iter.getAdjNode()); - assertEquals(GHUtility.createEdgeKey(0, 1, origEdgeId, false), - ((VirtualEdgeIteratorState) queryGraph.getEdgeIteratorState(iter.getEdge(), 1)).getOriginalEdgeKey()); + assertEquals(2, ((VirtualEdgeIteratorState) queryGraph.getEdgeIteratorState(iter.getEdge(), 1)).getOriginalEdgeKey()); } @Test @@ -713,12 +734,12 @@ public void testWayGeometry_edge() { NodeAccess na = g.getNodeAccess(); na.setNode(0, 0, 0); na.setNode(1, 0.3, 0.3); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)). + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)). setWayGeometry(Helper.createPointList(0.1, 0.1, 0.2, 0.2)); LocationIndexTree locationIndex = new LocationIndexTree(g, new RAMDirectory()); locationIndex.prepareIndex(); - Snap snap = locationIndex.findClosest(0.15, 0.15, AccessFilter.allEdges(encoder.getAccessEnc())); + Snap snap = locationIndex.findClosest(0.15, 0.15, AccessFilter.allEdges(accessEnc)); assertTrue(snap.isValid()); assertEquals(EDGE, snap.getSnappedPosition(), "this test was supposed to test the Position.EDGE case"); QueryGraph queryGraph = lookup(snap); @@ -755,12 +776,12 @@ public void testWayGeometry_pillar() { NodeAccess na = g.getNodeAccess(); na.setNode(0, 0, 0); na.setNode(1, 0.5, 0.1); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(10)). + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10)). setWayGeometry(Helper.createPointList(0.1, 0.1, 0.2, 0.2)); LocationIndexTree locationIndex = new LocationIndexTree(g, new RAMDirectory()); locationIndex.prepareIndex(); - Snap snap = locationIndex.findClosest(0.2, 0.21, AccessFilter.allEdges(encoder.getAccessEnc())); + Snap snap = locationIndex.findClosest(0.2, 0.21, AccessFilter.allEdges(accessEnc)); assertTrue(snap.isValid()); assertEquals(PILLAR, snap.getSnappedPosition(), "this test was supposed to test the Position.PILLAR case"); QueryGraph queryGraph = lookup(snap); @@ -801,7 +822,7 @@ public void testVirtualEdgeDistance() { dist += distCalc.calcDist(0, 0, 1, 0); dist += distCalc.calcDist(1, 0, 1, 1); dist += distCalc.calcDist(1, 1, 0, 1); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(dist)). + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(dist)). setWayGeometry(Helper.createPointList(1, 0, 1, 1)); LocationIndexTree index = new LocationIndexTree(g, new RAMDirectory()); index.prepareIndex(); @@ -824,15 +845,15 @@ public void testVirtualEdgeIds() { // virtual nodes: 2 // 0 - x - 1 // virtual edges: 1 2 - FlagEncoder encoder = FlagEncoders.createCar(new PMap().putObject("speed_two_directions", true)); - EncodingManager encodingManager = EncodingManager.create(encoder); - DecimalEncodedValue speedEnc = encoder.getAverageSpeedEnc(); - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + BaseGraph g = new BaseGraph.Builder(em).create(); NodeAccess na = g.getNodeAccess(); na.setNode(0, 50.00, 10.10); na.setNode(1, 50.00, 10.20); double dist = DistanceCalcEarth.DIST_EARTH.calcDist(na.getLat(0), na.getLon(0), na.getLat(1), na.getLon(1)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(dist)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(dist)); edge.set(speedEnc, 50); edge.setReverse(speedEnc, 100); @@ -898,16 +919,16 @@ public void testVirtualEdgeIds_reverse() { // virtual nodes: 2 // 0 - x - 1 // virtual edges: 1 2 - FlagEncoder encoder = FlagEncoders.createCar(new PMap().putObject("speed_two_directions", true)); - EncodingManager encodingManager = EncodingManager.create(encoder); - DecimalEncodedValue speedEnc = encoder.getAverageSpeedEnc(); - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + BaseGraph g = new BaseGraph.Builder(em).create(); NodeAccess na = g.getNodeAccess(); na.setNode(0, 50.00, 10.10); na.setNode(1, 50.00, 10.20); double dist = DistanceCalcEarth.DIST_EARTH.calcDist(na.getLat(0), na.getLon(0), na.getLat(1), na.getLon(1)); // this time we store the edge the other way - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 0).setDistance(dist)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 0).setDistance(dist)); edge.set(speedEnc, 100, 50); // query graph diff --git a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java index 714c0668250..793cc1b45bd 100644 --- a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java +++ b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java @@ -18,21 +18,15 @@ package com.graphhopper.routing.subnetwork; import com.carrotsearch.hppc.IntArrayList; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.Subnetwork; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.AllEdgesIterator; -import com.graphhopper.routing.util.DefaultFlagEncoderFactory; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.storage.BaseGraph; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -41,15 +35,14 @@ import static com.graphhopper.routing.weighting.TurnCostProvider.NO_TURN_COST_PROVIDER; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; /** * @author Peter Karich */ public class PrepareRoutingSubnetworksTest { - private static BaseGraph createSubnetworkTestStorage(EncodingManager encodingManager) { - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + private static BaseGraph createSubnetworkTestStorage(EncodingManager encodingManager, BooleanEncodedValue accessEnc1, DecimalEncodedValue speedEnc1, BooleanEncodedValue accessEnc2, DecimalEncodedValue speedEnc2) { + BaseGraph g = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); // 5 - 6 // | / // 4 @@ -74,133 +67,144 @@ private static BaseGraph createSubnetworkTestStorage(EncodingManager encodingMan // edge 3-4 gets no speed/access by default if (iter.getEdge() == 0) continue; - for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) { - iter.set(encoder.getAverageSpeedEnc(), 10); - iter.set(encoder.getAccessEnc(), true, true); - } + iter.set(accessEnc1, true, true); + iter.set(speedEnc1, 10); + if (accessEnc2 != null) + iter.set(accessEnc2, true, true); + if (speedEnc2 != null) + iter.set(speedEnc2, 10); } return g; } @Test public void testPrepareSubnetworks_oneVehicle() { - EncodingManager em = createEncodingManager("car"); - FlagEncoder encoder = em.getEncoder("car"); - BaseGraph g = createSubnetworkTestStorage(em); - PrepareRoutingSubnetworks instance = new PrepareRoutingSubnetworks(g, Collections.singletonList(createJob(em, encoder, NO_TURN_COST_PROVIDER))); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + BooleanEncodedValue subnetworkEnc = Subnetwork.create("car"); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).add(subnetworkEnc).build(); + BaseGraph g = createSubnetworkTestStorage(em, accessEnc, speedEnc, null, null); + PrepareRoutingSubnetworks instance = new PrepareRoutingSubnetworks(g, Collections.singletonList(createJob(subnetworkEnc, accessEnc, speedEnc, NO_TURN_COST_PROVIDER))); // this will make the upper small network a subnetwork instance.setMinNetworkSize(4); assertEquals(3, instance.doWork()); - assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, encoder)); + assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, subnetworkEnc)); // this time we lower the threshold and the upper network won't be set to be a subnetwork - g = createSubnetworkTestStorage(em); - instance = new PrepareRoutingSubnetworks(g, Collections.singletonList(createJob(em, encoder, NO_TURN_COST_PROVIDER))); + g = createSubnetworkTestStorage(em, accessEnc, speedEnc, null, null); + instance = new PrepareRoutingSubnetworks(g, Collections.singletonList(createJob(subnetworkEnc, accessEnc, speedEnc, NO_TURN_COST_PROVIDER))); instance.setMinNetworkSize(3); assertEquals(0, instance.doWork()); - assertEquals(IntArrayList.from(), getSubnetworkEdges(g, encoder)); + assertEquals(IntArrayList.from(), getSubnetworkEdges(g, subnetworkEnc)); } @Test public void testPrepareSubnetworks_twoVehicles() { - EncodingManager em = createEncodingManager("car,bike"); - FlagEncoder carEncoder = em.getEncoder("car"); - FlagEncoder bikeEncoder = em.getEncoder("bike"); - BaseGraph g = createSubnetworkTestStorage(em); + BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + BooleanEncodedValue carSubnetworkEnc = Subnetwork.create("car"); + BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); + DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); + BooleanEncodedValue bikeSubnetworkEnc = Subnetwork.create("bike"); + EncodingManager em = EncodingManager.start() + .add(carAccessEnc).add(carSpeedEnc).add(carSubnetworkEnc) + .add(bikeAccessEnc).add(bikeSpeedEnc).add(bikeSubnetworkEnc) + .build(); + BaseGraph g = createSubnetworkTestStorage(em, carAccessEnc, carSpeedEnc, bikeAccessEnc, bikeSpeedEnc); // first we only block the middle edge for cars. this way a subnetwork should be created but only for car EdgeIteratorState edge = GHUtility.getEdge(g, 3, 4); - GHUtility.setSpeed(10, false, false, carEncoder, edge); - GHUtility.setSpeed(5, true, true, bikeEncoder, edge); + GHUtility.setSpeed(10, false, false, carAccessEnc, carSpeedEnc, edge); + GHUtility.setSpeed(5, true, true, bikeAccessEnc, bikeSpeedEnc, edge); List prepareJobs = Arrays.asList( - createJob(em, carEncoder, NO_TURN_COST_PROVIDER), - createJob(em, bikeEncoder, NO_TURN_COST_PROVIDER) + createJob(carSubnetworkEnc, carAccessEnc, carSpeedEnc, NO_TURN_COST_PROVIDER), + createJob(bikeSubnetworkEnc, bikeAccessEnc, bikeSpeedEnc, NO_TURN_COST_PROVIDER) ); PrepareRoutingSubnetworks instance = new PrepareRoutingSubnetworks(g, prepareJobs); instance.setMinNetworkSize(5); assertEquals(3, instance.doWork()); - assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, carEncoder)); - assertEquals(IntArrayList.from(), getSubnetworkEdges(g, bikeEncoder)); + assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, carSubnetworkEnc)); + assertEquals(IntArrayList.from(), getSubnetworkEdges(g, bikeSubnetworkEnc)); // now we block the edge for both vehicles -> there should be a subnetwork for both vehicles - g = createSubnetworkTestStorage(em); + g = createSubnetworkTestStorage(em, carAccessEnc, carSpeedEnc, bikeAccessEnc, bikeSpeedEnc); edge = GHUtility.getEdge(g, 3, 4); - GHUtility.setSpeed(10, false, false, carEncoder, edge); - GHUtility.setSpeed(5, false, false, bikeEncoder, edge); + GHUtility.setSpeed(10, false, false, carAccessEnc, carSpeedEnc, edge); + GHUtility.setSpeed(5, false, false, bikeAccessEnc, bikeSpeedEnc, edge); instance = new PrepareRoutingSubnetworks(g, prepareJobs); instance.setMinNetworkSize(5); assertEquals(6, instance.doWork()); - assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, carEncoder)); - assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, bikeEncoder)); + assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, carSubnetworkEnc)); + assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, bikeSubnetworkEnc)); } @Test public void testPrepareSubnetwork_withTurnCosts() { - EncodingManager em = createEncodingManager("car|turn_costs=true"); - FlagEncoder encoder = em.fetchEdgeEncoders().iterator().next(); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + DecimalEncodedValue turnCostEnc = TurnCost.create("car", 1); + BooleanEncodedValue subnetworkEnc = Subnetwork.create("car"); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).add(subnetworkEnc).addTurnCostEncodedValue(turnCostEnc).build(); // since the middle edge is blocked the upper component is a subnetwork (regardless of turn costs) - BaseGraph g = createSubnetworkTestStorage(em); + BaseGraph g = createSubnetworkTestStorage(em, accessEnc, speedEnc, null, null); PrepareRoutingSubnetworks instance = new PrepareRoutingSubnetworks(g, Collections.singletonList( - createJob(em, encoder, new DefaultTurnCostProvider(encoder, g.getTurnCostStorage(), 0)))); + createJob(subnetworkEnc, accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, g.getTurnCostStorage(), 0)))); instance.setMinNetworkSize(4); assertEquals(3, instance.doWork()); - assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, encoder)); + assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, subnetworkEnc)); // if we open the edge it won't be a subnetwork anymore - g = createSubnetworkTestStorage(em); + g = createSubnetworkTestStorage(em, accessEnc, speedEnc, null, null); EdgeIteratorState edge = GHUtility.getEdge(g, 3, 4); - GHUtility.setSpeed(10, true, true, encoder, edge); + GHUtility.setSpeed(10, true, true, accessEnc, speedEnc, edge); instance = new PrepareRoutingSubnetworks(g, Collections.singletonList( - createJob(em, encoder, new DefaultTurnCostProvider(encoder, g.getTurnCostStorage(), 0)))); + createJob(subnetworkEnc, accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, g.getTurnCostStorage(), 0)))); instance.setMinNetworkSize(4); assertEquals(0, instance.doWork()); - assertEquals(IntArrayList.from(), getSubnetworkEdges(g, encoder)); + assertEquals(IntArrayList.from(), getSubnetworkEdges(g, subnetworkEnc)); // ... and now for something interesting: if we open the edge *and* apply turn restrictions it will be a // subnetwork again - g = createSubnetworkTestStorage(em); + g = createSubnetworkTestStorage(em, accessEnc, speedEnc, null, null); edge = GHUtility.getEdge(g, 3, 4); - GHUtility.setSpeed(10, true, true, encoder, edge); - DecimalEncodedValue turnCostEnc = em.getDecimalEncodedValue(TurnCost.key(encoder.toString())); - g.getTurnCostStorage().set(turnCostEnc, 0, 4, 7, 1); - g.getTurnCostStorage().set(turnCostEnc, 0, 4, 9, 1); + GHUtility.setSpeed(10, true, true, accessEnc, speedEnc, edge); + g.getTurnCostStorage().set(turnCostEnc, 0, 4, 7, Double.POSITIVE_INFINITY); + g.getTurnCostStorage().set(turnCostEnc, 0, 4, 9, Double.POSITIVE_INFINITY); instance = new PrepareRoutingSubnetworks(g, Collections.singletonList( - createJob(em, encoder, new DefaultTurnCostProvider(encoder, g.getTurnCostStorage(), 0)))); + createJob(subnetworkEnc, accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, g.getTurnCostStorage(), 0)))); instance.setMinNetworkSize(4); assertEquals(3, instance.doWork()); - assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, encoder)); - + assertEquals(IntArrayList.from(7, 8, 9), getSubnetworkEdges(g, subnetworkEnc)); } - - private BaseGraph createSubnetworkTestStorageWithOneWays(EncodingManager em, FlagEncoder encoder) { - if (em.fetchEdgeEncoders().size() > 1) - fail("Warning: This method only sets access/speed for a single encoder, but the given encoding manager has multiple encoders"); + private BaseGraph createSubnetworkTestStorageWithOneWays(EncodingManager em, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { BaseGraph g = new BaseGraph.Builder(em).create(); // 0 - 1 - 2 - 3 - 4 <- 5 - 6 - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(5, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(5, 6).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(5, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 6).setDistance(1)); // 7 -> 8 - 9 - 10 - GHUtility.setSpeed(60, true, false, encoder, g.edge(7, 8).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(8, 9).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(9, 10).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(7, 8).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 9).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(9, 10).setDistance(1)); return g; } @Test public void testPrepareSubnetworks_withOneWays() { - EncodingManager em = createEncodingManager("car"); - FlagEncoder encoder = em.fetchEdgeEncoders().iterator().next(); - BaseGraph g = createSubnetworkTestStorageWithOneWays(em, encoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + BooleanEncodedValue subnetworkEnc = Subnetwork.create("car"); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).add(subnetworkEnc).build(); + BaseGraph g = createSubnetworkTestStorageWithOneWays(em, accessEnc, speedEnc); assertEquals(11, g.getNodes()); - PrepareRoutingSubnetworks.PrepareJob job = createJob(em, encoder, NO_TURN_COST_PROVIDER); + PrepareRoutingSubnetworks.PrepareJob job = createJob(subnetworkEnc, accessEnc, speedEnc, NO_TURN_COST_PROVIDER); PrepareRoutingSubnetworks instance = new PrepareRoutingSubnetworks(g, Collections.singletonList(job)). setMinNetworkSize(2); int subnetworkEdges = instance.doWork(); @@ -209,9 +213,9 @@ public void testPrepareSubnetworks_withOneWays() { // note that the subnetworkEV per profile is one bit per *edge*. Before we used the encoder$access with 2 bits // and got more fine grained response here (8 removed *edgeKeys*) assertEquals(3, subnetworkEdges); - assertEquals(IntArrayList.from(4, 5, 6), getSubnetworkEdges(g, encoder)); + assertEquals(IntArrayList.from(4, 5, 6), getSubnetworkEdges(g, subnetworkEnc)); - g = createSubnetworkTestStorageWithOneWays(em, encoder); + g = createSubnetworkTestStorageWithOneWays(em, accessEnc, speedEnc); assertEquals(11, g.getNodes()); instance = new PrepareRoutingSubnetworks(g, Collections.singletonList(job)). @@ -220,56 +224,43 @@ public void testPrepareSubnetworks_withOneWays() { // due to the larger min network size this time also the (8,9,10) component is a subnetwork assertEquals(5, subnetworkEdges); - assertEquals(IntArrayList.from(4, 5, 6, 7, 8), getSubnetworkEdges(g, encoder)); + assertEquals(IntArrayList.from(4, 5, 6, 7, 8), getSubnetworkEdges(g, subnetworkEnc)); } // Previous two-pass implementation failed on 1 -> 2 -> 0 @Test public void testNodeOrderingRegression() { // 1 -> 2 -> 0 - 3 - 4 - 5 - EncodingManager em = createEncodingManager("car"); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + BooleanEncodedValue subnetworkEnc = Subnetwork.create("car"); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).add(subnetworkEnc).build(); BaseGraph g = new BaseGraph.Builder(em).create(); - FlagEncoder encoder = em.fetchEdgeEncoders().iterator().next(); - GHUtility.setSpeed(60, true, false, encoder, g.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(2, 0).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(4, 5).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(1, 2).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(2, 0).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 3).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(1)); - PrepareRoutingSubnetworks.PrepareJob job = createJob(em, encoder, NO_TURN_COST_PROVIDER); + PrepareRoutingSubnetworks.PrepareJob job = createJob(subnetworkEnc, accessEnc, speedEnc, NO_TURN_COST_PROVIDER); PrepareRoutingSubnetworks instance = new PrepareRoutingSubnetworks(g, Collections.singletonList(job)). setMinNetworkSize(2); int subnetworkEdges = instance.doWork(); assertEquals(2, subnetworkEdges); - assertEquals(IntArrayList.from(0, 1), getSubnetworkEdges(g, encoder)); + assertEquals(IntArrayList.from(0, 1), getSubnetworkEdges(g, subnetworkEnc)); } - private static IntArrayList getSubnetworkEdges(BaseGraph graph, FlagEncoder encoder) { - BooleanEncodedValue subnetworkEnc = encoder.getBooleanEncodedValue(Subnetwork.key(encoder.toString())); + private static IntArrayList getSubnetworkEdges(BaseGraph graph, BooleanEncodedValue subnetworkEnc) { IntArrayList result = new IntArrayList(); AllEdgesIterator iter = graph.getAllEdges(); - while (iter.next()) { - if (iter.get(subnetworkEnc)) { + while (iter.next()) + if (iter.get(subnetworkEnc)) result.add(iter.getEdge()); - } - } return result; } - private static EncodingManager createEncodingManager(String flagEncodersStr) { - EncodingManager.Builder builder = new EncodingManager.Builder(); - for (String encoderStr : flagEncodersStr.split(",")) { - encoderStr = encoderStr.trim(); - FlagEncoder encoder = new DefaultFlagEncoderFactory().createFlagEncoder(encoderStr.split("\\|")[0], new PMap(encoderStr)); - builder.add(encoder); - builder.add(Subnetwork.create(encoder.toString())); - } - return builder.build(); - } - - private static PrepareRoutingSubnetworks.PrepareJob createJob(EncodingManager em, FlagEncoder encoder, TurnCostProvider turnCostProvider) { - return new PrepareRoutingSubnetworks.PrepareJob(em.getBooleanEncodedValue(Subnetwork.key(encoder.toString())), - new FastestWeighting(encoder, turnCostProvider)); + private static PrepareRoutingSubnetworks.PrepareJob createJob(BooleanEncodedValue subnetworkEnc, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc, TurnCostProvider turnCostProvider) { + return new PrepareRoutingSubnetworks.PrepareJob(subnetworkEnc, new FastestWeighting(accessEnc, speedEnc, turnCostProvider)); } } diff --git a/core/src/test/java/com/graphhopper/routing/util/AbstractBikeTagParserTester.java b/core/src/test/java/com/graphhopper/routing/util/AbstractBikeTagParserTester.java deleted file mode 100644 index cedfa2cd4b7..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/AbstractBikeTagParserTester.java +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderNode; -import com.graphhopper.reader.ReaderRelation; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.*; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.Helper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.text.DateFormat; -import java.util.Date; - -import static com.graphhopper.routing.util.PriorityCode.*; -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Karich - * @author ratrun - */ -public abstract class AbstractBikeTagParserTester { - protected BikeCommonTagParser parser; - protected TagParserManager encodingManager; - protected BooleanEncodedValue roundaboutEnc; - protected DecimalEncodedValue priorityEnc; - protected DecimalEncodedValue avgSpeedEnc; - - @BeforeEach - public void setUp() { - encodingManager = TagParserManager.create(parser = createBikeTagParser()); - roundaboutEnc = encodingManager.getBooleanEncodedValue(Roundabout.KEY); - priorityEnc = encodingManager.getDecimalEncodedValue(EncodingManager.getKey(parser, "priority")); - avgSpeedEnc = parser.getAverageSpeedEnc(); - } - - protected abstract BikeCommonTagParser createBikeTagParser(); - - protected void assertPriority(int expectedPrio, ReaderWay way) { - IntsRef relFlags = encodingManager.handleRelationTags(new ReaderRelation(0), encodingManager.createRelationFlags()); - IntsRef edgeFlags = encodingManager.handleWayTags(way, relFlags); - DecimalEncodedValue enc = encodingManager.getDecimalEncodedValue(EncodingManager.getKey(parser.toString(), "priority")); - assertEquals(PriorityCode.getValue(expectedPrio), enc.getDecimal(false, edgeFlags), 0.01); - } - - protected void assertPriorityAndSpeed(int expectedPrio, double expectedSpeed, ReaderWay way) { - assertPriorityAndSpeed(expectedPrio, expectedSpeed, way, new ReaderRelation(0)); - } - - protected void assertPriorityAndSpeed(int expectedPrio, double expectedSpeed, ReaderWay way, ReaderRelation rel) { - IntsRef relFlags = encodingManager.handleRelationTags(rel, encodingManager.createRelationFlags()); - IntsRef edgeFlags = encodingManager.handleWayTags(way, relFlags); - DecimalEncodedValue enc = encodingManager.getDecimalEncodedValue(EncodingManager.getKey(parser.toString(), "priority")); - assertEquals(PriorityCode.getValue(expectedPrio), enc.getDecimal(false, edgeFlags), 0.01); - assertEquals(expectedSpeed, parser.getAverageSpeedEnc().getDecimal(false, edgeFlags), 0.1); - assertEquals(expectedSpeed, parser.getAverageSpeedEnc().getDecimal(true, edgeFlags), 0.1); - } - - protected double getSpeedFromFlags(ReaderWay way) { - IntsRef relFlags = encodingManager.createRelationFlags(); - IntsRef flags = encodingManager.handleWayTags(way, relFlags); - return avgSpeedEnc.getDecimal(false, flags); - } - - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "motorway"); - assertTrue(parser.getAccess(way).canSkip()); - - way.setTag("highway", "motorway"); - way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("highway", "footway"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("bicycle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.setTag("highway", "footway"); - way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("highway", "pedestrian"); - way.setTag("bicycle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.setTag("highway", "pedestrian"); - way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("bicycle", "yes"); - way.setTag("highway", "cycleway"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "path"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("highway", "path"); - way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - way.clearTags(); - - way.setTag("highway", "track"); - way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - way.clearTags(); - - way.setTag("highway", "track"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("mtb", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "path"); - way.setTag("foot", "official"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("bicycle", "official"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("motorroad", "yes"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "track"); - way.setTag("ford", "yes"); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "secondary"); - way.setTag("access", "no"); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("bicycle", "dismount"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "secondary"); - way.setTag("vehicle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("bicycle", "dismount"); - assertTrue(parser.getAccess(way).isWay()); - - - way.clearTags(); - way.setTag("highway", "cycleway"); - way.setTag("cycleway", "track"); - way.setTag("railway", "abandoned"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "platform"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "platform"); - way.setTag("bicycle", "dismount"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "platform"); - way.setTag("bicycle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("bicycle:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access", "no"); - way.setTag("bicycle:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "track"); - way.setTag("vehicle", "forestry"); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - } - - @Test - public void testRelation() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "track"); - way.setTag("bicycle", "yes"); - way.setTag("foot", "yes"); - way.setTag("motor_vehicle", "agricultural"); - way.setTag("surface", "gravel"); - way.setTag("tracktype", "grade3"); - - ReaderRelation rel = new ReaderRelation(0); - rel.setTag("type", "route"); - rel.setTag("network", "rcn"); - rel.setTag("route", "bicycle"); - - ReaderRelation rel2 = new ReaderRelation(1); - rel2.setTag("type", "route"); - rel2.setTag("network", "lcn"); - rel2.setTag("route", "bicycle"); - - // two relation tags => we currently cannot store a list, so pick the lower ordinal 'regional' - // Example https://www.openstreetmap.org/way/213492914 => two hike 84544, 2768803 and two bike relations 3162932, 5254650 - IntsRef relFlags = encodingManager.handleRelationTags(rel2, encodingManager.handleRelationTags(rel, encodingManager.createRelationFlags())); - IntsRef edgeFlags = encodingManager.handleWayTags(way, relFlags); - EnumEncodedValue enc = encodingManager.getEnumEncodedValue(RouteNetwork.key("bike"), RouteNetwork.class); - assertEquals(RouteNetwork.REGIONAL, enc.getEnum(false, edgeFlags)); - } - - @Test - public void testTramStations() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "secondary"); - way.setTag("railway", "rail"); - assertTrue(parser.getAccess(way).isWay()); - - way = new ReaderWay(1); - way.setTag("highway", "secondary"); - way.setTag("railway", "station"); - assertTrue(parser.getAccess(way).isWay()); - - way = new ReaderWay(1); - way.setTag("highway", "secondary"); - way.setTag("railway", "station"); - way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("bicycle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way = new ReaderWay(1); - way.setTag("railway", "platform"); - IntsRef flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertNotEquals(true, flags.isEmpty()); - - way = new ReaderWay(1); - way.setTag("highway", "track"); - way.setTag("railway", "platform"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertNotEquals(true, flags.isEmpty()); - - way = new ReaderWay(1); - way.setTag("highway", "track"); - way.setTag("railway", "platform"); - way.setTag("bicycle", "no"); - - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(flags.isEmpty()); - } - - @Test - public void testAvoidTunnel() { - ReaderWay osmWay = new ReaderWay(1); - osmWay.setTag("highway", "residential"); - assertPriority(PREFER.getValue(), osmWay); - - osmWay.setTag("tunnel", "yes"); - assertPriority(UNCHANGED.getValue(), osmWay); - - osmWay.setTag("highway", "secondary"); - osmWay.setTag("tunnel", "yes"); - assertPriority(AVOID_MORE.getValue(), osmWay); - - osmWay.setTag("bicycle", "designated"); - assertPriority(PREFER.getValue(), osmWay); - } - - @Test - public void testTram() { - ReaderWay way = new ReaderWay(1); - // very dangerous - way.setTag("highway", "secondary"); - way.setTag("railway", "tram"); - assertPriority(AVOID_MORE.getValue(), way); - - // should be safe now - way.setTag("bicycle", "designated"); - assertPriority(PREFER.getValue(), way); - } - - @Test - public void testService() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "service"); - assertPriorityAndSpeed(PREFER.getValue(), 14, way); - - way.setTag("service", "parking_aisle"); - assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), 6, way); - } - - @Test - public void testSacScale() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "service"); - way.setTag("sac_scale", "hiking"); - // allow - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("sac_scale", "alpine_hiking"); - assertTrue(parser.getAccess(way).canSkip()); - } - - @Test - public void testReduceToMaxSpeed() { - ReaderWay way = new ReaderWay(12); - way.setTag("maxspeed", "90"); - assertEquals(12, parser.applyMaxSpeed(way, 12), 1e-2); - } - - @Test - public void testPreferenceForSlowSpeed() { - ReaderWay osmWay = new ReaderWay(1); - osmWay.setTag("highway", "tertiary"); - assertPriority(PREFER.getValue(), osmWay); - } - - @Test - public void testHandleWayTagsCallsHandlePriority() { - ReaderWay osmWay = new ReaderWay(1); - osmWay.setTag("highway", "cycleway"); - IntsRef edgeFlags = parser.handleWayTags(encodingManager.createEdgeFlags(), osmWay); - DecimalEncodedValue priorityEnc = encodingManager.getDecimalEncodedValue(EncodingManager.getKey(parser, "priority")); - assertEquals(PriorityCode.getValue(VERY_NICE.getValue()), priorityEnc.getDecimal(false, edgeFlags), 1e-3); - } - - @Test - public void testAvoidMotorway() { - ReaderWay osmWay = new ReaderWay(1); - osmWay.setTag("highway", "motorway"); - osmWay.setTag("bicycle", "yes"); - assertPriority(AVOID.getValue(), osmWay); - } - - @Test - public void testLockedGate() { - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("locked", "yes"); - assertTrue(parser.isBarrier(node)); - } - - @Test - public void testNoBike() { - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("bicycle", "no"); - assertTrue(parser.isBarrier(node)); - } - - @Test - public void testBarrierAccess() { - // by default allow access through the gate for bike & foot! - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - // no barrier! - assertFalse(parser.isBarrier(node)); - - node.setTag("bicycle", "yes"); - // no barrier! - assertFalse(parser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - // barrier! - assertTrue(parser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "yes"); - node.setTag("bicycle", "no"); - // barrier! - assertTrue(parser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - node.setTag("foot", "yes"); - // barrier! - assertTrue(parser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - node.setTag("bicycle", "yes"); - // no barrier! - assertFalse(parser.isBarrier(node)); - } - - @Test - public void testBarrierAccessFord() { - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("ford", "yes"); - // barrier! - assertTrue(parser.isBarrier(node)); - - node.setTag("bicycle", "yes"); - // no barrier! - assertFalse(parser.isBarrier(node)); - } - - @Test - public void testFerries() { - ReaderWay way = new ReaderWay(1); - - way.clearTags(); - way.setTag("route", "ferry"); - assertTrue(parser.getAccess(way).isFerry()); - way.setTag("bicycle", "no"); - assertFalse(parser.getAccess(way).isFerry()); - - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "yes"); - assertFalse(parser.getAccess(way).isFerry()); - - // #1122 - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("bicycle", "yes"); - way.setTag("access", "private"); - assertTrue(parser.getAccess(way).canSkip()); - - // #1562, test if ferry route with bicycle - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("bicycle", "designated"); - assertTrue(parser.getAccess(way).isFerry()); - - way.setTag("bicycle", "official"); - assertTrue(parser.getAccess(way).isFerry()); - - way.setTag("bicycle", "permissive"); - assertTrue(parser.getAccess(way).isFerry()); - - way.setTag("foot", "yes"); - assertTrue(parser.getAccess(way).isFerry()); - - way.setTag("bicycle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.setTag("bicycle", "designated"); - way.setTag("access", "private"); - assertTrue(parser.getAccess(way).canSkip()); - - // test if when foot is set is invalid - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "yes"); - assertTrue(parser.getAccess(way).canSkip()); - } -} diff --git a/core/src/test/java/com/graphhopper/routing/util/AccessFilterTest.java b/core/src/test/java/com/graphhopper/routing/util/AccessFilterTest.java index 053d1997371..1376773011e 100644 --- a/core/src/test/java/com/graphhopper/routing/util/AccessFilterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/AccessFilterTest.java @@ -20,6 +20,7 @@ import com.carrotsearch.hppc.IntHashSet; import com.carrotsearch.hppc.IntSet; import com.graphhopper.routing.ch.PrepareEncoder; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.storage.*; @@ -29,8 +30,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class AccessFilterTest { - private final FlagEncoder encoder = FlagEncoders.createCar(); - private final EncodingManager encodingManager = EncodingManager.create(encoder); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + private final DecimalEncodedValue turnCostEnc = TurnCost.create("car", 1); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); private final BaseGraph graph = new BaseGraph.Builder(encodingManager) .withTurnCosts(true) .create(); @@ -40,16 +43,16 @@ public void testAccept_fwdLoopShortcut_acceptedByInExplorer() { // 0-1 // \| // 2 - GHUtility.setSpeed(60, true, false, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(1, 2).setDistance(2)); - GHUtility.setSpeed(60, true, false, encoder, graph.edge(2, 0).setDistance(3)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(0, 1).setDistance(1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(1, 2).setDistance(2)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, graph.edge(2, 0).setDistance(3)); graph.freeze(); // add loop shortcut in 'fwd' direction - CHConfig chConfig = CHConfig.edgeBased("profile", new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage()))); + CHConfig chConfig = CHConfig.edgeBased("profile", new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()))); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); CHStorageBuilder chBuilder = new CHStorageBuilder(chStore); chBuilder.setIdentityLevels(); - chBuilder.addShortcutEdgeBased(0, 0, PrepareEncoder.getScFwdDir(), 5, 0, 2, 0, 2); + chBuilder.addShortcutEdgeBased(0, 0, PrepareEncoder.getScFwdDir(), 5, 0, 2, 0, 5); RoutingCHGraph chGraph = RoutingCHGraphImpl.fromGraph(graph, chStore, chConfig); RoutingCHEdgeExplorer outExplorer = chGraph.createOutEdgeExplorer(); RoutingCHEdgeExplorer inExplorer = chGraph.createInEdgeExplorer(); diff --git a/core/src/test/java/com/graphhopper/routing/util/spatialrules/AreaIndexTest.java b/core/src/test/java/com/graphhopper/routing/util/AreaIndexTest.java similarity index 92% rename from core/src/test/java/com/graphhopper/routing/util/spatialrules/AreaIndexTest.java rename to core/src/test/java/com/graphhopper/routing/util/AreaIndexTest.java index ec0a2b63cfd..1b3ddd76e55 100644 --- a/core/src/test/java/com/graphhopper/routing/util/spatialrules/AreaIndexTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/AreaIndexTest.java @@ -16,25 +16,20 @@ * limitations under the License. */ -package com.graphhopper.routing.util.spatialrules; +package com.graphhopper.routing.util; -import com.graphhopper.routing.util.AreaIndex; -import com.graphhopper.routing.util.CustomArea; -import com.graphhopper.util.StopWatch; -import org.junit.jupiter.api.Disabled; +import com.graphhopper.routing.ev.Country; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.Polygon; -import java.io.IOException; import java.util.*; import java.util.stream.Collectors; import static com.graphhopper.util.GHUtility.readCountries; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; class AreaIndexTest { @@ -140,12 +135,10 @@ public void testOverlap() { @Test public void testCountries() { AreaIndex countryIndex = createCountryIndex(); - // Berlin - assertEquals("Germany", countryIndex.query(52.5243700, 13.4105300).get(0).getProperties().get("name:en")); - // Paris - assertEquals("France", countryIndex.query(48.864716, 2.349014).get(0).getProperties().get("name:en")); - // Austria - assertEquals("Austria", countryIndex.query(48.204484, 16.107888).get(0).getProperties().get("name:en")); + assertEquals("DEU", countryIndex.query(52.52437, 13.41053).get(0).getProperties().get(Country.ISO_ALPHA3)); + assertEquals("FRA", countryIndex.query(48.86471, 2.349014).get(0).getProperties().get(Country.ISO_ALPHA3)); + assertEquals("USA", countryIndex.query(35.67514, -105.94665).get(0).getProperties().get(Country.ISO_ALPHA3)); + assertEquals("AUT", countryIndex.query(48.20448, 16.10788).get(0).getProperties().get(Country.ISO_ALPHA3)); } private AreaIndex createCountryIndex() { diff --git a/core/src/test/java/com/graphhopper/routing/util/Bike2WeightTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/Bike2WeightTagParserTest.java deleted file mode 100644 index 7e6ec37b6f2..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/Bike2WeightTagParserTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.Graph; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.storage.NodeAccess; -import com.graphhopper.util.*; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Karich - */ -public class Bike2WeightTagParserTest extends BikeTagParserTest { - - @Override - protected BikeTagParser createBikeTagParser() { - return new Bike2WeightTagParser(new PMap("block_fords=true")); - } - - private Graph initExampleGraph() { - BaseGraph gs = new BaseGraph.Builder(encodingManager.getEncodingManager()).set3D(true).create(); - NodeAccess na = gs.getNodeAccess(); - // 50--(0.0001)-->49--(0.0004)-->55--(0.0005)-->60 - na.setNode(0, 51.1, 12.001, 50); - na.setNode(1, 51.1, 12.002, 60); - EdgeIteratorState edge = gs.edge(0, 1). - setWayGeometry(Helper.createPointList3D(51.1, 12.0011, 49, 51.1, 12.0015, 55)); - GHUtility.setSpeed(10, 15, parser, edge.setDistance(100)); - return gs; - } - - @Test - public void testApplyWayTags() { - Graph graph = initExampleGraph(); - EdgeIteratorState edge = GHUtility.getEdge(graph, 0, 1); - ReaderWay way = new ReaderWay(1); - parser.applyWayTags(way, edge); - - IntsRef flags = edge.getFlags(); - // decrease speed - assertEquals(2, avgSpeedEnc.getDecimal(false, flags), 1e-1); - // increase speed but use maximum speed (calculated was 24) - assertEquals(18, avgSpeedEnc.getDecimal(true, flags), 1e-1); - } - - @Test - public void testUnchangedForStepsBridgeAndTunnel() { - Graph graph = initExampleGraph(); - EdgeIteratorState edge = GHUtility.getEdge(graph, 0, 1); - IntsRef oldFlags = IntsRef.deepCopyOf(edge.getFlags()); - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "steps"); - parser.applyWayTags(way, edge); - - assertEquals(oldFlags, edge.getFlags()); - } - - @Test - public void testSetSpeed0_issue367() { - IntsRef edgeFlags = GHUtility.setSpeed(10, 10, parser, encodingManager.createEdgeFlags()); - assertEquals(10, avgSpeedEnc.getDecimal(false, edgeFlags), .1); - assertEquals(10, avgSpeedEnc.getDecimal(true, edgeFlags), .1); - - parser.setSpeed(false, edgeFlags, 0); - - assertEquals(0, avgSpeedEnc.getDecimal(false, edgeFlags), .1); - assertEquals(10, avgSpeedEnc.getDecimal(true, edgeFlags), .1); - assertFalse(parser.getAccessEnc().getBool(false, edgeFlags)); - assertTrue(parser.getAccessEnc().getBool(true, edgeFlags)); - } - - @Test - public void testRoutingFailsWithInvalidGraph_issue665() { - BaseGraph graph = new BaseGraph.Builder(encodingManager.getEncodingManager()).set3D(true).create(); - ReaderWay way = new ReaderWay(0); - way.setTag("route", "ferry"); - way.setTag("edge_distance", 500.0); - - assertNotEquals(EncodingManager.Access.CAN_SKIP, parser.getAccess(way)); - IntsRef wayFlags = encodingManager.handleWayTags(way, encodingManager.createRelationFlags()); - graph.edge(0, 1).setDistance(247).setFlags(wayFlags); - - assertTrue(isGraphValid(graph, parser)); - } - - private boolean isGraphValid(Graph graph, FlagEncoder encoder) { - EdgeExplorer explorer = graph.createEdgeExplorer(); - - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - // iterator at node 0 considers the edge 0-1 to be undirected - EdgeIterator iter0 = explorer.setBaseNode(0); - iter0.next(); - boolean iter0flag - = iter0.getBaseNode() == 0 && iter0.getAdjNode() == 1 - && iter0.get(accessEnc) && iter0.getReverse(accessEnc); - - // iterator at node 1 considers the edge 1-0 to be directed - EdgeIterator iter1 = explorer.setBaseNode(1); - iter1.next(); - boolean iter1flag - = iter1.getBaseNode() == 1 && iter1.getAdjNode() == 0 - && iter1.get(accessEnc) && iter1.getReverse(accessEnc); - - return iter0flag && iter1flag; - } -} diff --git a/core/src/test/java/com/graphhopper/routing/util/Car4WDTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/Car4WDTagParserTest.java deleted file mode 100644 index d486272e692..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/Car4WDTagParserTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.util.Helper; -import com.graphhopper.util.PMap; -import org.junit.jupiter.api.Test; - -import java.text.DateFormat; -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author Peter Karich - * @author zstadler - */ -public class Car4WDTagParserTest extends CarTagParserTest { - - @Override - CarTagParser createParser() { - return new Car4WDTagParser(new PMap("speed_two_directions=true|block_fords=true")); - } - - @Override - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(1); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("highway", "service"); - assertTrue(parser.getAccess(way).isWay()); - way.setTag("access", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "track"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("motorcar", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - // for now allow grade1+2+3 for every country, see #253 - way.clearTags(); - way.setTag("highway", "track"); - way.setTag("tracktype", "grade2"); - assertTrue(parser.getAccess(way).isWay()); - // This is the only difference from a "car" - way.setTag("tracktype", "grade4"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "delivery"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "unclassified"); - way.setTag("ford", "yes"); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("motorcar", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("route", "ferry"); - assertTrue(parser.getAccess(way).isFerry()); - way.setTag("motorcar", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "yes"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("access", "yes"); - way.setTag("motor_vehicle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "yes"); - way.setTag("motor_vehicle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "no"); - way.setTag("motorcar", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "emergency"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("motor_vehicle", "emergency"); - assertTrue(parser.getAccess(way).canSkip()); - - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access", "no"); - way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).isWay()); - } -} diff --git a/core/src/test/java/com/graphhopper/routing/util/CarTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/CarTagParserTest.java deleted file mode 100644 index 1a5a0f623ce..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/CarTagParserTest.java +++ /dev/null @@ -1,603 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderNode; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.Roundabout; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.Helper; -import com.graphhopper.util.PMap; -import org.junit.jupiter.api.Test; - -import java.text.DateFormat; -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Karich - */ -public class CarTagParserTest { - final CarTagParser parser = createParser(); - private final TagParserManager em = new TagParserManager.Builder(). - add(parser). - add(FlagEncoders.createBike()).add(FlagEncoders.createFoot()).build(); - - private final BooleanEncodedValue roundaboutEnc = em.getBooleanEncodedValue(Roundabout.KEY); - private final DecimalEncodedValue avSpeedEnc = parser.getAverageSpeedEnc(); - private final BooleanEncodedValue accessEnc = parser.getAccessEnc(); - - CarTagParser createParser() { - return new CarTagParser(new PMap("speed_two_directions=true|block_fords=true")); - } - - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(1); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("highway", "service"); - assertTrue(parser.getAccess(way).isWay()); - way.setTag("access", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "track"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("motorcar", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - // for now allow grade1+2+3 for every country, see #253 - way.clearTags(); - way.setTag("highway", "track"); - way.setTag("tracktype", "grade2"); - assertTrue(parser.getAccess(way).isWay()); - way.setTag("tracktype", "grade4"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "delivery"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "unclassified"); - way.setTag("ford", "yes"); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("motorcar", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("access", "yes"); - way.setTag("motor_vehicle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "yes"); - way.setTag("motor_vehicle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "no"); - way.setTag("motorcar", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "emergency"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("motor_vehicle", "emergency"); - assertTrue(parser.getAccess(way).canSkip()); - - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access", "no"); - way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("service", "emergency_access"); - assertTrue(parser.getAccess(way).canSkip()); - } - - @Test - public void testMilitaryAccess() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "track"); - way.setTag("access", "military"); - assertTrue(parser.getAccess(way).canSkip()); - } - - @Test - public void testFordAccess() { - ReaderNode node = new ReaderNode(0, 0.0, 0.0); - node.setTag("ford", "yes"); - - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "unclassified"); - way.setTag("ford", "yes"); - - // Node and way are initially blocking - assertTrue(parser.isBlockFords()); - assertTrue(parser.getAccess(way).canSkip()); - assertTrue(parser.isBarrier(node)); - - CarTagParser tmpEncoder = new CarTagParser(new PMap("block_fords=false")); - EncodingManager.create(tmpEncoder); - assertTrue(tmpEncoder.getAccess(way).isWay()); - assertFalse(tmpEncoder.isBarrier(node)); - } - - @Test - public void testOneway() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "primary"); - IntsRef flags = parser.handleWayTags(em.createEdgeFlags(), way); - assertTrue(accessEnc.getBool(false, flags)); - assertTrue(accessEnc.getBool(true, flags)); - way.setTag("oneway", "yes"); - flags = parser.handleWayTags(em.createEdgeFlags(), way); - assertTrue(accessEnc.getBool(false, flags)); - assertFalse(accessEnc.getBool(true, flags)); - way.clearTags(); - - way.setTag("highway", "tertiary"); - flags = parser.handleWayTags(em.createEdgeFlags(), way); - assertTrue(accessEnc.getBool(false, flags)); - assertTrue(accessEnc.getBool(true, flags)); - way.clearTags(); - - way.setTag("highway", "tertiary"); - way.setTag("vehicle:forward", "no"); - flags = parser.handleWayTags(em.createEdgeFlags(), way); - assertFalse(accessEnc.getBool(false, flags)); - assertTrue(accessEnc.getBool(true, flags)); - way.clearTags(); - - way.setTag("highway", "tertiary"); - way.setTag("vehicle:backward", "no"); - flags = parser.handleWayTags(em.createEdgeFlags(), way); - assertTrue(accessEnc.getBool(false, flags)); - assertFalse(accessEnc.getBool(true, flags)); - way.clearTags(); - - // This is no one way - way.setTag("highway", "tertiary"); - way.setTag("vehicle:backward", "designated"); - flags = parser.handleWayTags(em.createEdgeFlags(), way); - assertTrue(accessEnc.getBool(false, flags)); - assertTrue(accessEnc.getBool(true, flags)); - way.clearTags(); - } - - @Test - public void testSetAccess() { - IntsRef edgeFlags = em.createEdgeFlags(); - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - assertTrue(accessEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(true, edgeFlags)); - - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, false); - assertTrue(accessEnc.getBool(false, edgeFlags)); - assertFalse(accessEnc.getBool(true, edgeFlags)); - - accessEnc.setBool(false, edgeFlags, false); - accessEnc.setBool(true, edgeFlags, true); - assertFalse(accessEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(true, edgeFlags)); - - accessEnc.setBool(false, edgeFlags, false); - accessEnc.setBool(true, edgeFlags, false); - assertFalse(accessEnc.getBool(true, edgeFlags)); - } - - @Test - public void testMaxSpeed() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "trunk"); - way.setTag("maxspeed", "500"); - IntsRef relFlags = em.createRelationFlags(); - IntsRef edgeFlags = em.handleWayTags(way, relFlags); - assertEquals(140, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way = new ReaderWay(1); - way.setTag("highway", "primary"); - way.setTag("maxspeed:backward", "10"); - way.setTag("maxspeed:forward", "20"); - edgeFlags = em.handleWayTags(way, relFlags); - assertEquals(10, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way = new ReaderWay(1); - way.setTag("highway", "primary"); - way.setTag("maxspeed:forward", "20"); - edgeFlags = em.handleWayTags(way, relFlags); - assertEquals(20, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way = new ReaderWay(1); - way.setTag("highway", "primary"); - way.setTag("maxspeed:backward", "20"); - edgeFlags = em.handleWayTags(way, relFlags); - assertEquals(20, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way = new ReaderWay(1); - way.setTag("highway", "motorway"); - way.setTag("maxspeed", "none"); - edgeFlags = em.handleWayTags(way, relFlags); - assertEquals(135, avSpeedEnc.getDecimal(false, edgeFlags), .1); - } - - @Test - public void testSpeed() { - // limit bigger than default road speed - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "trunk"); - way.setTag("maxspeed", "110"); - IntsRef edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(100, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way.clearTags(); - way.setTag("highway", "residential"); - way.setTag("surface", "cobblestone"); - edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(30, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way.clearTags(); - way.setTag("highway", "track"); - edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(15, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way.clearTags(); - way.setTag("highway", "track"); - way.setTag("tracktype", "grade1"); - edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(20, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way.clearTags(); - way.setTag("highway", "secondary"); - way.setTag("surface", "compacted"); - edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(30, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way.clearTags(); - way.setTag("highway", "secondary"); - way.setTag("motorroad", "yes"); - edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(90, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way.clearTags(); - way.setTag("highway", "motorway"); - way.setTag("motorroad", "yes"); // this tag should be ignored - edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(100, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - way.clearTags(); - way.setTag("highway", "motorway_link"); - way.setTag("motorroad", "yes"); // this tag should be ignored - edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(70, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - try { - avSpeedEnc.setDecimal(false, em.createEdgeFlags(), -1); - assertTrue(false); - } catch (IllegalArgumentException ex) { - } - } - - @Test - public void testSetSpeed() { - IntsRef edgeFlags = em.createEdgeFlags(); - avSpeedEnc.setDecimal(false, edgeFlags, 10); - assertEquals(10, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - } - - @Test - public void testSetSpeed0_issue367() { - IntsRef edgeFlags = em.createEdgeFlags(); - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - - parser.setSpeed(false, edgeFlags, parser.avgSpeedEnc.getSmallestNonZeroValue() - 0.01); - - // one direction effects the other direction as one encoder for speed but this is not true for access - assertEquals(0, avSpeedEnc.getDecimal(false, edgeFlags), .1); - assertEquals(0, avSpeedEnc.getDecimal(true, edgeFlags), .1); - assertFalse(accessEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(true, edgeFlags)); - - // so always call this method with reverse=true too - parser.setSpeed(true, edgeFlags, parser.avgSpeedEnc.getSmallestNonZeroValue() - 0.01); - assertFalse(accessEnc.getBool(true, edgeFlags)); - } - - @Test - public void testRoundabout() { - IntsRef edgeFlags = em.createEdgeFlags(); - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - roundaboutEnc.setBool(false, edgeFlags, true); - assertTrue(roundaboutEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(true, edgeFlags)); - - roundaboutEnc.setBool(false, edgeFlags, false); - assertFalse(roundaboutEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(true, edgeFlags)); - - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "motorway"); - edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertTrue(accessEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(true, edgeFlags)); - assertFalse(roundaboutEnc.getBool(false, edgeFlags)); - } - - @Test - public void testRailway() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "secondary"); - way.setTag("railway", "rail"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "path"); - way.setTag("railway", "abandoned"); - assertTrue(parser.getAccess(way).canSkip()); - - way.setTag("highway", "track"); - assertTrue(parser.getAccess(way).isWay()); - - // this is fully okay as sometimes old rails are on the road - way.setTag("highway", "primary"); - way.setTag("railway", "historic"); - assertTrue(parser.getAccess(way).isWay()); - - way.setTag("motorcar", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way = new ReaderWay(1); - way.setTag("highway", "secondary"); - way.setTag("railway", "tram"); - // but allow tram to be on the same way - assertTrue(parser.getAccess(way).isWay()); - } - - @Test - public void testFerry() { - ReaderWay way = new ReaderWay(1); - way.setTag("route", "shuttle_train"); - way.setTag("motorcar", "yes"); - way.setTag("bicycle", "no"); - // Provide the duration value in seconds: - way.setTag("way_distance", 50000.0); - way.setTag("speed_from_duration", 50 / (35.0 / 60)); - way.setTag("duration:seconds", 35L * 60); - // accept - assertTrue(parser.getAccess(way).isFerry()); - IntsRef edgeFlags = em.createEdgeFlags(); - // calculate speed from tags: speed_from_duration * 1.4 (+ rounded using the speed factor) - parser.handleWayTags(edgeFlags, way); - assertEquals(60, parser.getAverageSpeedEnc().getDecimal(false, edgeFlags)); - - //Test for very short and slow 0.5km/h still realistic ferry - way = new ReaderWay(1); - way.setTag("route", "ferry"); - way.setTag("motorcar", "yes"); - // Provide the duration of 12 minutes in seconds: - way.setTag("duration:seconds", 12L * 60); - way.setTag("way_distance", 100.0); - way.setTag("speed_from_duration", 0.1 / (12.0 / 60)); - // accept - assertTrue(parser.getAccess(way).isFerry()); - // We can't store 0.5km/h, but we expect the lowest possible speed (5km/h) - edgeFlags = em.createEdgeFlags(); - parser.handleWayTags(edgeFlags, way); - assertEquals(5, parser.getAverageSpeedEnc().getDecimal(false, edgeFlags)); - - edgeFlags = em.createEdgeFlags(); - avSpeedEnc.setDecimal(false, edgeFlags, 2.5); - assertEquals(5, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - - //Test for missing duration - way = new ReaderWay(1); - way.setTag("route", "ferry"); - way.setTag("motorcar", "yes"); - way.setTag("edge_distance", 100.0); - // accept - assertTrue(parser.getAccess(way).isFerry()); - parser.handleWayTags(edgeFlags, way); - // We use the unknown speed - assertEquals(5, parser.getAverageSpeedEnc().getDecimal(false, edgeFlags)); - - way.clearTags(); - way.setTag("route", "ferry"); - assertTrue(parser.getAccess(way).isFerry()); - way.setTag("motorcar", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "yes"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "designated"); - way.setTag("motor_vehicle", "designated"); - assertTrue(parser.getAccess(way).isFerry()); - - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("access", "no"); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("vehicle", "yes"); - assertTrue(parser.getAccess(way).isFerry()); - } - - @Test - public void testBarrierAccess() { - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "lift_gate"); - node.setTag("access", "yes"); - // no barrier! - assertFalse(parser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "lift_gate"); - node.setTag("bicycle", "yes"); - // no barrier! - assertFalse(parser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "lift_gate"); - node.setTag("access", "yes"); - node.setTag("bicycle", "yes"); - // should this be a barrier for motorcars too? - // assertTrue(encoder.handleNodeTags(node) == true); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "lift_gate"); - node.setTag("access", "no"); - node.setTag("motorcar", "yes"); - // no barrier! - assertFalse(parser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "bollard"); - // barrier! - assertTrue(parser.isBarrier(node)); - - CarTagParser tmpEncoder = new CarTagParser(); - EncodingManager.create(tmpEncoder); - - // Test if cattle_grid is not blocking - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "cattle_grid"); - assertFalse(tmpEncoder.isBarrier(node)); - } - - @Test - public void testChainBarrier() { - // by default allow access through the gate for bike & foot! - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "chain"); - assertFalse(parser.isBarrier(node)); - node.setTag("motor_vehicle", "no"); - assertTrue(parser.isBarrier(node)); - node.setTag("motor_vehicle", "yes"); - assertFalse(parser.isBarrier(node)); - } - - @Test - public void testMaxValue() { - CarTagParser instance = new CarTagParser(10, 0.5, 0); - EncodingManager em = EncodingManager.create(instance); - DecimalEncodedValue avSpeedEnc = em.getDecimalEncodedValue(EncodingManager.getKey(instance, "average_speed")); - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "motorway_link"); - way.setTag("maxspeed", "60 mph"); - IntsRef edgeFlags = instance.handleWayTags(em.createEdgeFlags(), way); - - // double speed = AbstractFlagEncoder.parseSpeed("60 mph"); - // => 96.56 * 0.9 => 86.9 - assertEquals(86.9, avSpeedEnc.getDecimal(false, edgeFlags), 1e-1); - assertEquals(86.9, avSpeedEnc.getDecimal(true, edgeFlags), 1e-1); - - // test that maxPossibleValue is not exceeded - way = new ReaderWay(2); - way.setTag("highway", "motorway_link"); - way.setTag("maxspeed", "70 mph"); - edgeFlags = instance.handleWayTags(em.createEdgeFlags(), way); - assertEquals(101.5, avSpeedEnc.getDecimal(false, edgeFlags), .1); - } - - @Test - public void testRegisterOnlyOnceAllowed() { - FlagEncoder instance = FlagEncoders.createCar(); - EncodingManager.create(instance); - assertThrows(IllegalStateException.class, () -> EncodingManager.create(instance)); - } - - @Test - public void testSetToMaxSpeed() { - ReaderWay way = new ReaderWay(12); - way.setTag("maxspeed", "90"); - assertEquals(90, VehicleTagParser.getMaxSpeed(way), 1e-2); - } - - @Test - public void testCombination() { - ReaderWay way = new ReaderWay(123); - way.setTag("highway", "cycleway"); - way.setTag("sac_scale", "hiking"); - - assertEquals(EncodingManager.Access.CAN_SKIP, parser.getAccess(way)); - assertNotEquals(EncodingManager.Access.CAN_SKIP, ((BikeTagParser) em.getEncoder("bike")).getAccess(way)); - IntsRef edgeFlags = em.handleWayTags(way, em.createRelationFlags()); - assertFalse(accessEnc.getBool(true, edgeFlags)); - assertFalse(accessEnc.getBool(false, edgeFlags)); - BooleanEncodedValue bikeAccessEnc = em.getEncoder("bike").getAccessEnc(); - assertTrue(bikeAccessEnc.getBool(true, edgeFlags)); - assertTrue(bikeAccessEnc.getBool(false, edgeFlags)); - } - - @Test - public void testApplyBadSurfaceSpeed() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "secondary"); - way.setTag("surface", "unpaved"); - assertEquals(30, parser.applyBadSurfaceSpeed(way, 90), 1e-1); - } - - @Test - public void testIssue_1256() { - ReaderWay way = new ReaderWay(1); - way.setTag("route", "ferry"); - way.setTag("edge_distance", 257.0); - - // default is 5km/h minimum speed for car - IntsRef edgeFlags = em.createEdgeFlags(); - parser.handleWayTags(edgeFlags, way); - assertEquals(5, parser.getAverageSpeedEnc().getDecimal(false, edgeFlags), .1); - - // for a smaller speed factor the minimum speed is also smaller - CarTagParser lowFactorCar = new CarTagParser(10, 1, 0); - EncodingManager lowFactorEm = EncodingManager.create(lowFactorCar); - edgeFlags = lowFactorEm.createEdgeFlags(); - lowFactorCar.handleWayTags(edgeFlags, way); - assertEquals(1, lowFactorCar.getAverageSpeedEnc().getDecimal(false, edgeFlags), .1); - } -} diff --git a/core/src/test/java/com/graphhopper/routing/util/CurvatureCalculatorTest.java b/core/src/test/java/com/graphhopper/routing/util/CurvatureCalculatorTest.java new file mode 100644 index 00000000000..3405025ac6d --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/CurvatureCalculatorTest.java @@ -0,0 +1,53 @@ +package com.graphhopper.routing.util; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.ArrayEdgeIntAccess; +import com.graphhopper.routing.ev.Curvature; +import com.graphhopper.util.PointList; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CurvatureCalculatorTest { + + private final EncodingManager em = EncodingManager.start().add(Curvature.create()).build(); + + @Test + public void testCurvature() { + CurvatureCalculator calculator = new CurvatureCalculator(em.getDecimalEncodedValue(Curvature.KEY)); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + calculator.handleWayTags(edgeId, intAccess, getStraightWay(), null); + double valueStraight = em.getDecimalEncodedValue(Curvature.KEY).getDecimal(false, edgeId, intAccess); + + intAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + calculator.handleWayTags(edgeId, intAccess, getCurvyWay(), null); + double valueCurvy = em.getDecimalEncodedValue(Curvature.KEY).getDecimal(false, edgeId, intAccess); + + assertTrue(valueCurvy < valueStraight, "The bendiness of the straight road is smaller than the one of the curvy road"); + } + + private ReaderWay getStraightWay() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + PointList pointList = new PointList(); + pointList.add(50.9, 13.13); + pointList.add(50.899, 13.13); + way.setTag("point_list", pointList); + way.setTag("edge_distance", 100d); + return way; + } + + private ReaderWay getCurvyWay() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + PointList pointList = new PointList(); + pointList.add(50.9, 13.13); + pointList.add(50.899, 13.129); + pointList.add(50.899, 13.13); + way.setTag("point_list", pointList); + way.setTag("edge_distance", 160d); + return way; + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java b/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java index 2620ca833fc..2ecac791e91 100644 --- a/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/EncodingManagerTest.java @@ -17,6 +17,14 @@ */ package com.graphhopper.routing.util; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.RoadAccess; +import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.routing.util.parsers.BikeAccessParser; +import com.graphhopper.routing.util.parsers.CarAccessParser; +import com.graphhopper.routing.util.parsers.FootAccessParser; +import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -29,68 +37,47 @@ public class EncodingManagerTest { @Test - public void duplicateNamesNotAllowed() { - assertThrows(IllegalArgumentException.class, () -> EncodingManager.create("car,car")); - } - - @Test - public void testEncoderAcceptNoException() { - EncodingManager manager = EncodingManager.create("car"); - assertTrue(manager.hasEncoder("car")); - assertFalse(manager.hasEncoder("foot")); + public void testSupportFords() { + EncodingManager manager = new EncodingManager.Builder() + .add(VehicleEncodedValues.car(new PMap())) + .add(VehicleEncodedValues.bike(new PMap())) + .add(VehicleEncodedValues.foot(new PMap())). + build(); + + // 1) default -> no block fords + assertFalse(new CarAccessParser(manager, new PMap()).isBlockFords()); + assertFalse(new BikeAccessParser(manager, new PMap()).isBlockFords()); + assertFalse(new FootAccessParser(manager, new PMap()).isBlockFords()); + + // 2) true + assertTrue(new CarAccessParser(manager, new PMap("block_fords=true")).isBlockFords()); + assertTrue(new BikeAccessParser(manager, new PMap("block_fords=true")).isBlockFords()); + assertTrue(new FootAccessParser(manager, new PMap("block_fords=true")).isBlockFords()); + + // 3) false + assertFalse(new CarAccessParser(manager, new PMap("block_fords=false")).isBlockFords()); + assertFalse(new BikeAccessParser(manager, new PMap("block_fords=false")).isBlockFords()); + assertFalse(new FootAccessParser(manager, new PMap("block_fords=false")).isBlockFords()); } @Test - public void testWrongEncoders() { - try { - FlagEncoder foot = FlagEncoders.createFoot(); - EncodingManager.create(foot, foot); - fail("There should have been an exception"); - } catch (Exception ex) { - assertEquals("FlagEncoder already exists: foot", ex.getMessage()); - } + public void testRegisterOnlyOnceAllowed() { + DecimalEncodedValueImpl speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager.start().add(speedEnc).build(); + assertThrows(IllegalStateException.class, () -> EncodingManager.start().add(speedEnc).build()); } @Test - public void testSupportFords() { - // 1) no encoder crossing fords - String flagEncoderStrings = "car,bike,foot"; - EncodingManager manager = EncodingManager.create(flagEncoderStrings); - - assertFalse(((CarTagParser) manager.getEncoder("car")).isBlockFords()); - assertFalse(((BikeTagParser) manager.getEncoder("bike")).isBlockFords()); - assertFalse(((FootTagParser) manager.getEncoder("foot")).isBlockFords()); - - // 2) two encoders crossing fords - flagEncoderStrings = "car, bike|block_fords=true, foot|block_fords=false"; - manager = EncodingManager.create(flagEncoderStrings); - - assertFalse(((CarTagParser) manager.getEncoder("car")).isBlockFords()); - assertTrue(((BikeTagParser) manager.getEncoder("bike")).isBlockFords()); - assertFalse(((FootTagParser) manager.getEncoder("foot")).isBlockFords()); - - // 2) Try combined with another tag - flagEncoderStrings = "car|turn_costs=true|block_fords=true, bike, foot|block_fords=false"; - manager = EncodingManager.create(flagEncoderStrings); - - assertTrue(((CarTagParser) manager.getEncoder("car")).isBlockFords()); - assertFalse(((BikeTagParser) manager.getEncoder("bike")).isBlockFords()); - assertFalse(((FootTagParser) manager.getEncoder("foot")).isBlockFords()); + public void testGetVehicles() { + EncodingManager em = EncodingManager.start() + .add(VehicleAccess.create("car")) + .add(VehicleAccess.create("bike")).add(VehicleSpeed.create("bike", 4, 2, true)) + .add(VehicleSpeed.create("roads", 5, 5, false)) + .add(VehicleAccess.create("hike")).add(new DecimalEncodedValueImpl("whatever_hike_average_speed_2022", 5, 5, true)) + .add(RoadAccess.create()) + .build(); + // only for bike+hike there is access+'speed' + assertEquals(Arrays.asList("bike", "hike"), em.getVehicles()); } - @Test - public void validEV() { - for (String str : Arrays.asList("blup_test", "test", "test12", "tes$0", "car_test_test", "small_car$average_speed")) { - assertTrue(EncodingManager.isValidEncodedValue(str), str); - } - - for (String str : Arrays.asList("Test", "12test", "test|3", "car__test", "blup_te.st_", "car___test", "car$$access", - "test{34", "truck__average_speed", "blup.test", "test,21", "täst", "blup.two.three", "blup..test")) { - assertFalse(EncodingManager.isValidEncodedValue(str), str); - } - - for (String str : Arrays.asList("break", "switch")) { - assertFalse(EncodingManager.isValidEncodedValue(str), str); - } - } } diff --git a/core/src/test/java/com/graphhopper/routing/util/FootTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/FootTagParserTest.java deleted file mode 100644 index 4e8581daa17..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/FootTagParserTest.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderNode; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.*; -import org.junit.jupiter.api.Test; - -import java.text.DateFormat; -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Karich - */ -public class FootTagParserTest { - private final EncodingManager encodingManager = EncodingManager.create("car,bike,foot"); - private final FootTagParser footParser = (FootTagParser) encodingManager.getEncoder("foot"); - private final DecimalEncodedValue footAvgSpeedEnc = footParser.getAverageSpeedEnc(); - private final BooleanEncodedValue footAccessEnc = footParser.getAccessEnc(); - private final DecimalEncodedValue carAvSpeedEnc = encodingManager.getEncoder("car").getAverageSpeedEnc(); - private final BooleanEncodedValue carAccessEnc = encodingManager.getEncoder("car").getAccessEnc(); - - @Test - public void testGetSpeed() { - IntsRef fl = encodingManager.createEdgeFlags(); - footAccessEnc.setBool(false, fl, true); - footAccessEnc.setBool(true, fl, true); - footAvgSpeedEnc.setDecimal(false, fl, 10); - assertEquals(10, footAvgSpeedEnc.getDecimal(false, fl), 1e-1); - } - - @Test - public void testSteps() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "service"); - IntsRef flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertEquals(FootTagParser.MEAN_SPEED, footAvgSpeedEnc.getDecimal(false, flags), 1e-1); - - way.setTag("highway", "steps"); - flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(FootTagParser.MEAN_SPEED > footAvgSpeedEnc.getDecimal(false, flags)); - } - - @Test - public void testCombined() { - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - EdgeIteratorState edge = g.edge(0, 1); - edge.set(footAvgSpeedEnc, 10.0).set(footAccessEnc, true, true); - edge.set(carAvSpeedEnc, 100.0).set(carAccessEnc, true, false); - - assertEquals(10, edge.get(footAvgSpeedEnc), 1e-1); - assertTrue(edge.get(footAccessEnc)); - assertTrue(edge.getReverse(footAccessEnc)); - - assertEquals(100, edge.get(carAvSpeedEnc), 1e-1); - assertTrue(edge.get(carAccessEnc)); - assertFalse(edge.getReverse(carAccessEnc)); - - IntsRef raw = encodingManager.createEdgeFlags(); - footAvgSpeedEnc.setDecimal(false, raw, 10); - footAccessEnc.setBool(false, raw, true); - footAccessEnc.setBool(true, raw, true); - assertEquals(0, carAvSpeedEnc.getDecimal(false, raw), 1e-1); - } - - @Test - public void testGraph() { - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - g.edge(0, 1).setDistance(10).set(footAvgSpeedEnc, 10.0).set(footAccessEnc, true, true); - g.edge(0, 2).setDistance(10).set(footAvgSpeedEnc, 5.0).set(footAccessEnc, true, true); - g.edge(1, 3).setDistance(10).set(footAvgSpeedEnc, 10.0).set(footAccessEnc, true, true); - EdgeExplorer out = g.createEdgeExplorer(AccessFilter.outEdges(footParser.getAccessEnc())); - assertEquals(GHUtility.asSet(1, 2), GHUtility.getNeighbors(out.setBaseNode(0))); - assertEquals(GHUtility.asSet(0, 3), GHUtility.getNeighbors(out.setBaseNode(1))); - assertEquals(GHUtility.asSet(0), GHUtility.getNeighbors(out.setBaseNode(2))); - } - - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "motorway"); - way.setTag("sidewalk", "yes"); - assertTrue(footParser.getAccess(way).isWay()); - way.setTag("sidewalk", "left"); - assertTrue(footParser.getAccess(way).isWay()); - - way.setTag("sidewalk", "none"); - assertTrue(footParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("sidewalk", "left"); - way.setTag("access", "private"); - assertTrue(footParser.getAccess(way).canSkip()); - way.clearTags(); - - way.setTag("highway", "pedestrian"); - assertTrue(footParser.getAccess(way).isWay()); - - way.setTag("highway", "footway"); - assertTrue(footParser.getAccess(way).isWay()); - - way.setTag("highway", "platform"); - assertTrue(footParser.getAccess(way).isWay()); - - way.setTag("highway", "motorway"); - assertTrue(footParser.getAccess(way).canSkip()); - - way.setTag("highway", "path"); - assertTrue(footParser.getAccess(way).isWay()); - - way.setTag("bicycle", "official"); - assertTrue(footParser.getAccess(way).isWay()); - way.setTag("foot", "no"); - assertTrue(footParser.getAccess(way).canSkip()); - - way.setTag("foot", "official"); - assertTrue(footParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "no"); - assertTrue(footParser.getAccess(way).canSkip()); - way.setTag("foot", "yes"); - assertTrue(footParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("vehicle", "no"); - assertTrue(footParser.getAccess(way).isWay()); - way.setTag("foot", "no"); - assertTrue(footParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("motorroad", "yes"); - assertTrue(footParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "cycleway"); - assertTrue(footParser.getAccess(way).isWay()); - way.setTag("foot", "no"); - assertTrue(footParser.getAccess(way).canSkip()); - way.setTag("access", "yes"); - assertTrue(footParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("foot", "yes"); - way.setTag("access", "no"); - assertTrue(footParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("route", "ferry"); - assertTrue(footParser.getAccess(way).isFerry()); - way.setTag("foot", "no"); - assertTrue(footParser.getAccess(way).canSkip()); - - // #1562, test if ferry route with foot - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "yes"); - assertTrue(footParser.getAccess(way).isFerry()); - - way.setTag("foot", "designated"); - assertTrue(footParser.getAccess(way).isFerry()); - - way.setTag("foot", "official"); - assertTrue(footParser.getAccess(way).isFerry()); - - way.setTag("foot", "permissive"); - assertTrue(footParser.getAccess(way).isFerry()); - - way.setTag("foot", "no"); - assertTrue(footParser.getAccess(way).canSkip()); - - way.setTag("foot", "designated"); - way.setTag("access", "private"); - assertTrue(footParser.getAccess(way).canSkip()); - - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(footParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("access", "no"); - way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(footParser.getAccess(way).isWay()); - } - - @Test - public void testRailPlatformIssue366() { - ReaderWay way = new ReaderWay(1); - way.setTag("railway", "platform"); - IntsRef flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertFalse(flags.isEmpty()); - - way.clearTags(); - way.setTag("highway", "track"); - way.setTag("railway", "platform"); - flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertFalse(flags.isEmpty()); - - way.clearTags(); - // only tram, no highway => no access - way.setTag("railway", "tram"); - flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(flags.isEmpty()); - } - - @Test - public void testPier() { - ReaderWay way = new ReaderWay(1); - way.setTag("man_made", "pier"); - IntsRef flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertFalse(flags.isEmpty()); - } - - @Test - public void testFerrySpeed() { - ReaderWay way = new ReaderWay(1); - way.setTag("route", "ferry"); - way.setTag("duration:seconds", 1800L); - way.setTag("edge_distance", 30000.0); - way.setTag("speed_from_duration", 30 / 0.5); - // the speed is truncated to maxspeed (=15) - IntsRef edgeFlags = encodingManager.createEdgeFlags(); - footParser.handleWayTags(edgeFlags, way); - assertEquals(15, footParser.getAverageSpeedEnc().getDecimal(false, edgeFlags)); - } - - @Test - public void testMixSpeedAndSafe() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "motorway"); - IntsRef flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertEquals(0, flags.ints[0]); - - way.setTag("sidewalk", "yes"); - flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertEquals(5, footAvgSpeedEnc.getDecimal(false, flags), 1e-1); - - way.clearTags(); - way.setTag("highway", "track"); - flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertEquals(5, footAvgSpeedEnc.getDecimal(false, flags), 1e-1); - } - - @Test - public void testPriority() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "cycleway"); - assertEquals(PriorityCode.UNCHANGED.getValue(), footParser.handlePriority(way, null)); - - way.setTag("highway", "primary"); - assertEquals(PriorityCode.AVOID.getValue(), footParser.handlePriority(way, null)); - - way.setTag("highway", "track"); - way.setTag("bicycle", "official"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), footParser.handlePriority(way, null)); - - way.setTag("highway", "track"); - way.setTag("bicycle", "designated"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), footParser.handlePriority(way, null)); - - way.setTag("highway", "cycleway"); - way.setTag("bicycle", "designated"); - way.setTag("foot", "designated"); - assertEquals(PriorityCode.PREFER.getValue(), footParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "primary"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.UNCHANGED.getValue(), footParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "cycleway"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.UNCHANGED.getValue(), footParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("bicycle", "official"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), footParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "trunk"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.AVOID.getValue(), footParser.handlePriority(way, null)); - way.setTag("sidewalk", "none"); - assertEquals(PriorityCode.AVOID.getValue(), footParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "residential"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.PREFER.getValue(), footParser.handlePriority(way, null)); - } - - @Test - public void testSlowHiking() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "track"); - way.setTag("sac_scale", "hiking"); - IntsRef flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertEquals(FootTagParser.MEAN_SPEED, footAvgSpeedEnc.getDecimal(false, flags), 1e-1); - - way.setTag("highway", "track"); - way.setTag("sac_scale", "mountain_hiking"); - flags = footParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertEquals(FootTagParser.SLOW_SPEED, footAvgSpeedEnc.getDecimal(false, flags), 1e-1); - } - - @Test - public void testBarrierAccess() { - // by default allow access through the gate for bike & foot! - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - // no barrier! - assertFalse(footParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "yes"); - // no barrier! - assertFalse(footParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - // barrier! - assertTrue(footParser.isBarrier(node)); - - node.setTag("bicycle", "yes"); - // no barrier!? - // assertTrue(footEncoder.handleNodeTags(node) == false); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - node.setTag("foot", "yes"); - // no barrier! - assertFalse(footParser.isBarrier(node)); - - node.setTag("locked", "yes"); - // barrier! - assertTrue(footParser.isBarrier(node)); - - node.clearTags(); - node.setTag("barrier", "yes"); - node.setTag("access", "no"); - assertTrue(footParser.isBarrier(node)); - } - - @Test - public void testChainBarrier() { - // by default allow access through the gate for bike & foot! - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "chain"); - assertFalse(footParser.isBarrier(node)); - node.setTag("foot", "no"); - assertTrue(footParser.isBarrier(node)); - } - - @Test - public void testFord() { - // by default do not block access due to fords! - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("ford", "no"); - assertFalse(footParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("ford", "yes"); - // no barrier! - assertFalse(footParser.isBarrier(node)); - - // barrier! - node.setTag("foot", "no"); - assertTrue(footParser.isBarrier(node)); - - FootTagParser tmpEncoder = new FootTagParser(new PMap("block_fords=true")); - EncodingManager.create(tmpEncoder); - node = new ReaderNode(1, -1, -1); - node.setTag("ford", "no"); - assertFalse(tmpEncoder.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("ford", "yes"); - assertTrue(tmpEncoder.isBarrier(node)); - } - - @Test - public void testBlockByDefault() { - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - // potential barriers are no barrier by default - assertFalse(footParser.isBarrier(node)); - node.setTag("access", "no"); - assertTrue(footParser.isBarrier(node)); - - // absolute barriers always block - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "fence"); - assertTrue(footParser.isBarrier(node)); - node.setTag("barrier", "fence"); - node.setTag("access", "yes"); - assertFalse(footParser.isBarrier(node)); - - // pass potential barriers per default (if no other access tag exists) - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - assertFalse(footParser.isBarrier(node)); - node.setTag("access", "yes"); - assertFalse(footParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "fence"); - assertTrue(footParser.isBarrier(node)); - - // don't block potential barriers: barrier:cattle_grid should not block here - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "cattle_grid"); - assertFalse(footParser.isBarrier(node)); - } - - @Test - public void maxSpeed() { - FlagEncoder encoder = FlagEncoders.createFoot(new PMap().putObject("speed_bits", 4).putObject("speed_factor", 2)); - // The foot max speed is supposed to be 15km/h, but for speed_bits=4,speed_factor=2 as we use here 15 cannot - // be stored. In fact, when we set the speed of an edge to 15 and call the getter afterwards we get a value of 16 - // because of the internal (scaled) integer representation: - EncodingManager em = EncodingManager.create(encoder); - BaseGraph graph = new BaseGraph.Builder(em).create(); - EdgeIteratorState edge = graph.edge(0, 1).setDistance(100).set(encoder.getAverageSpeedEnc(), 15); - assertEquals(16, edge.get(encoder.getAverageSpeedEnc())); - - // ... because of this we have to make sure the max speed is set to a value that cannot be exceeded even when - // such conversion occurs. in our case it must be 16 not 15! - assertEquals(16, encoder.getMaxSpeed()); - } -} diff --git a/core/src/test/java/com/graphhopper/routing/util/HeadingEdgeFilterTest.java b/core/src/test/java/com/graphhopper/routing/util/HeadingEdgeFilterTest.java index 80054eacb8c..b720b89fddd 100644 --- a/core/src/test/java/com/graphhopper/routing/util/HeadingEdgeFilterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/HeadingEdgeFilterTest.java @@ -1,5 +1,9 @@ package com.graphhopper.routing.util; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.storage.BaseGraph; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.shapes.GHPoint; @@ -12,14 +16,14 @@ class HeadingEdgeFilterTest { @Test public void getHeading() { GHPoint point = new GHPoint(55.67093, 12.577294); - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).build(); - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).build(); + BaseGraph g = new BaseGraph.Builder(em).create(); EdgeIteratorState edge = g.edge(0, 1); g.getNodeAccess().setNode(0, 55.671044, 12.5771583); g.getNodeAccess().setNode(1, 55.6704136, 12.5784324); - // GHUtility.setSpeed(50, 0, carEncoder, edge.getFlags()); - + // GHUtility.setSpeed(50, 0, carAccessEnc, carSpeedEnc, edge.getFlags()); assertEquals(131.2, HeadingEdgeFilter.getHeadingOfGeometryNearPoint(edge, point, 20), .1); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/HikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/HikeTagParserTest.java deleted file mode 100644 index f65c7b893a9..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/HikeTagParserTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author Peter Karich - */ -public class HikeTagParserTest { - private final EncodingManager encodingManager = EncodingManager.create("car,hike"); - private final HikeTagParser hikeParser = (HikeTagParser) encodingManager.getEncoder("hike"); - - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(0); - way.setTag("highway", "tertiary"); - way.setTag("access", "no"); - way.setTag("sidewalk", "both"); - way.setTag("foot", "no"); - assertTrue(hikeParser.getAccess(way).canSkip()); - } - - @Test - public void testPriority() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "cycleway"); - assertEquals(PriorityCode.UNCHANGED.getValue(), hikeParser.handlePriority(way, null)); - - way.setTag("highway", "primary"); - assertEquals(PriorityCode.AVOID.getValue(), hikeParser.handlePriority(way, null)); - - way.setTag("highway", "track"); - way.setTag("bicycle", "official"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), hikeParser.handlePriority(way, null)); - - way.setTag("highway", "track"); - way.setTag("bicycle", "designated"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), hikeParser.handlePriority(way, null)); - - way.setTag("highway", "cycleway"); - way.setTag("bicycle", "designated"); - way.setTag("foot", "designated"); - assertEquals(PriorityCode.PREFER.getValue(), hikeParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "primary"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.AVOID.getValue(), hikeParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "cycleway"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.UNCHANGED.getValue(), hikeParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("bicycle", "official"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), hikeParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "trunk"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.BAD.getValue(), hikeParser.handlePriority(way, null)); - way.setTag("sidewalk", "none"); - assertEquals(PriorityCode.BAD.getValue(), hikeParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "residential"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.PREFER.getValue(), hikeParser.handlePriority(way, null)); - } - -} diff --git a/core/src/test/java/com/graphhopper/routing/util/MotorcycleTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/MotorcycleTagParserTest.java deleted file mode 100644 index cc25e7b22b6..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/MotorcycleTagParserTest.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.Graph; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.storage.NodeAccess; -import com.graphhopper.util.*; -import com.graphhopper.util.shapes.GHPoint; -import org.junit.jupiter.api.Test; - -import java.text.DateFormat; -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Karich - */ -public class MotorcycleTagParserTest { - private final EncodingManager em = EncodingManager.create("motorcycle,foot"); - private final MotorcycleTagParser parser = (MotorcycleTagParser) em.getEncoder("motorcycle"); - private final BooleanEncodedValue accessEnc = parser.getAccessEnc(); - - private Graph initExampleGraph() { - BaseGraph gs = new BaseGraph.Builder(em).set3D(true).create(); - NodeAccess na = gs.getNodeAccess(); - // 50--(0.0001)-->49--(0.0004)-->55--(0.0005)-->60 - na.setNode(0, 51.1, 12.001, 50); - na.setNode(1, 51.1, 12.002, 60); - EdgeIteratorState edge = gs.edge(0, 1). - setWayGeometry(Helper.createPointList3D(51.1, 12.0011, 49, 51.1, 12.0015, 55)); - edge.setDistance(100); - - edge.set(accessEnc, true, true).set(parser.getAverageSpeedEnc(), 10.0, 15.0); - return gs; - } - - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(1); - assertTrue(parser.getAccess(way).canSkip()); - way.setTag("highway", "service"); - assertTrue(parser.getAccess(way).isWay()); - way.setTag("access", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "track"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "delivery"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "unclassified"); - way.setTag("ford", "yes"); - assertTrue(parser.getAccess(way).isWay()); - way.setTag("motorcycle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("route", "ferry"); - assertTrue(parser.getAccess(way).isFerry()); - way.setTag("motorcycle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "yes"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("access", "yes"); - way.setTag("motor_vehicle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "yes"); - way.setTag("motor_vehicle", "no"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "emergency"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("motor_vehicle", "emergency"); - assertTrue(parser.getAccess(way).canSkip()); - - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("access", "no"); - way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(parser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("service", "emergency_access"); - assertTrue(parser.getAccess(way).canSkip()); - } - - @Test - public void testHandleWayTags() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "service"); - assertTrue(parser.getAccess(way).isWay()); - IntsRef edgeFlags = parser.handleWayTags(em.createEdgeFlags(), way); - assertEquals(20, parser.avgSpeedEnc.getDecimal(false, edgeFlags), .1); - assertEquals(20, parser.avgSpeedEnc.getDecimal(true, edgeFlags), .1); - } - - @Test - public void testSetSpeed0_issue367() { - IntsRef edgeFlags = em.createEdgeFlags(); - accessEnc.setBool(false, edgeFlags, true); - accessEnc.setBool(true, edgeFlags, true); - parser.getAverageSpeedEnc().setDecimal(false, edgeFlags, 10); - parser.getAverageSpeedEnc().setDecimal(true, edgeFlags, 10); - - assertEquals(10, parser.getAverageSpeedEnc().getDecimal(false, edgeFlags), .1); - assertEquals(10, parser.getAverageSpeedEnc().getDecimal(true, edgeFlags), .1); - - parser.setSpeed(false, edgeFlags, 0); - assertEquals(0, parser.avgSpeedEnc.getDecimal(false, edgeFlags), .1); - assertEquals(10, parser.avgSpeedEnc.getDecimal(true, edgeFlags), .1); - assertFalse(accessEnc.getBool(false, edgeFlags)); - assertTrue(accessEnc.getBool(true, edgeFlags)); - } - - @Test - public void testCurvature() { - Graph graph = initExampleGraph(); - EdgeIteratorState edge = GHUtility.getEdge(graph, 0, 1); - - double bendinessOfStraightWay = getBendiness(edge, 100.0); - double bendinessOfCurvyWay = getBendiness(edge, 10.0); - - assertTrue(bendinessOfCurvyWay < bendinessOfStraightWay, "The bendiness of the straight road is smaller than the one of the curvy road"); - } - - private double getBendiness(EdgeIteratorState edge, double beelineDistance) { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "primary"); - // set point_list such that it yields the requested beelineDistance - GHPoint point = new GHPoint(11.3, 45.2); - GHPoint toPoint = DistanceCalcEarth.DIST_EARTH.projectCoordinate(point.lat, point.lon, beelineDistance, 90); - PointList pointList = new PointList(); - pointList.add(point); - pointList.add(toPoint); - way.setTag("point_list", pointList); - - assertTrue(parser.getAccess(way).isWay()); - IntsRef flags = parser.handleWayTags(em.createEdgeFlags(), way); - edge.setFlags(flags); - parser.applyWayTags(way, edge); - DecimalEncodedValue curvatureEnc = parser.getDecimalEncodedValue(EncodingManager.getKey(parser, "curvature")); - return edge.get(curvatureEnc); - } -} diff --git a/core/src/test/java/com/graphhopper/routing/util/NameSimilarityEdgeFilterTest.java b/core/src/test/java/com/graphhopper/routing/util/NameSimilarityEdgeFilterTest.java index 41ddc7ed379..290fd95fc9e 100644 --- a/core/src/test/java/com/graphhopper/routing/util/NameSimilarityEdgeFilterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/NameSimilarityEdgeFilterTest.java @@ -17,12 +17,19 @@ */ package com.graphhopper.routing.util; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; +import com.graphhopper.search.KVStorage; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; -import com.graphhopper.util.*; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.PointList; import com.graphhopper.util.shapes.GHPoint; import org.junit.jupiter.api.Test; +import static com.graphhopper.search.KVStorage.KeyValue.STREET_NAME; +import static com.graphhopper.search.KVStorage.KeyValue.createKV; import static org.junit.jupiter.api.Assertions.*; /** @@ -36,6 +43,7 @@ public class NameSimilarityEdgeFilterTest { public void testAccept() { EdgeFilter edgeFilter = createNameSimilarityEdgeFilter("Laufamholzstraße 154 Nürnberg"); EdgeIteratorState edge = createTestEdgeIterator("Laufamholzstraße, ST1333"); + edge.getName(); assertTrue(edgeFilter.accept(edge)); edge = createTestEdgeIterator("Hauptstraße"); @@ -81,8 +89,7 @@ public void testAccept() { @Test public void testDistanceFiltering() { - FlagEncoder encoder = FlagEncoders.createCar(); - BaseGraph g = new BaseGraph.Builder(EncodingManager.create(encoder)).create(); + BaseGraph g = new BaseGraph.Builder(1).create(); NodeAccess na = g.getNodeAccess(); GHPoint pointFarAway = new GHPoint(49.458629, 11.146124); @@ -98,11 +105,11 @@ public void testDistanceFiltering() { na.setNode(nodeID200, point200mAway.lat, point200mAway.lon); // Check that it matches a street 50m away - EdgeIteratorState edge1 = g.edge(nodeId50, farAwayId).setName("Wentworth Street"); + EdgeIteratorState edge1 = g.edge(nodeId50, farAwayId).setKeyValues(createKV(STREET_NAME, "Wentworth Street")); assertTrue(createNameSimilarityEdgeFilter("Wentworth Street").accept(edge1)); // Check that it doesn't match streets 200m away - EdgeIteratorState edge2 = g.edge(nodeID200, farAwayId).setName("Wentworth Street"); + EdgeIteratorState edge2 = g.edge(nodeID200, farAwayId).setKeyValues(createKV(STREET_NAME, "Wentworth Street")); assertFalse(createNameSimilarityEdgeFilter("Wentworth Street").accept(edge2)); } @@ -226,42 +233,7 @@ public void testAcceptWithTypos() { * so distance is not used when matching */ private NameSimilarityEdgeFilter createNameSimilarityEdgeFilter(String pointHint) { - return new NameSimilarityEdgeFilter(new EdgeFilter() { - @Override - public boolean accept(EdgeIteratorState edgeState) { - return true; - } - }, pointHint, basePoint, 100); - } - - private EdgeIteratorState createTestEdgeIterator(final String name, final int baseNodeId, final int adjNodeId) { - return new GHUtility.DisabledEdgeIterator() { - @Override - public String getName() { - return name; - } - - @Override - public int getBaseNode() { - return baseNodeId; - } - - @Override - public int getAdjNode() { - return adjNodeId; - } - - @Override - public PointList fetchWayGeometry(FetchMode type) { - PointList list = new PointList(); - list.add(basePoint); - return list; - } - }; - } - - private EdgeIteratorState createTestEdgeIterator(final String name) { - return createTestEdgeIterator(name, 0, 0); + return new NameSimilarityEdgeFilter(edgeState -> true, pointHint, basePoint, 100); } @Test @@ -272,8 +244,9 @@ public void curvedWayGeometry_issue2319() { // ----- // // 2 -- 3 - FlagEncoder encoder = FlagEncoders.createCar(new PMap().putObject("speed_two_directions", true)); - EncodingManager em = EncodingManager.create(encoder); + SimpleBooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); PointList pointList = new PointList(20, false); pointList.add(43.844377, -79.264005); @@ -302,14 +275,18 @@ public void curvedWayGeometry_issue2319() { pointList.add(43.842711, -79.264588); graph.getNodeAccess().setNode(0, 43.844521, -79.263976); graph.getNodeAccess().setNode(1, 43.842775, -79.264649); - EdgeIteratorState doubtfire = graph.edge(0, 1).setWayGeometry(pointList).set(encoder.getAccessEnc(), true, true).set(encoder.getAverageSpeedEnc(), 60, 60).setName("Doubtfire Crescent"); - EdgeIteratorState golden = graph.edge(0, 1).set(encoder.getAccessEnc(), true, true).set(encoder.getAverageSpeedEnc(), 60, 60).setName("Golden Avenue"); + + EdgeIteratorState doubtfire = graph.edge(0, 1).setWayGeometry(pointList).set(accessEnc, true, true). + set(speedEnc, 60, 60).setKeyValues(createKV(STREET_NAME, "Doubtfire Crescent")); + EdgeIteratorState golden = graph.edge(0, 1).set(accessEnc, true, true).set(speedEnc, 60, 60). + setKeyValues(createKV(STREET_NAME, "Golden Avenue")); graph.getNodeAccess().setNode(2, 43.841501560244744, -79.26366394602502); graph.getNodeAccess().setNode(3, 43.842247922172724, -79.2605663670726); PointList pointList2 = new PointList(1, false); pointList2.add(43.84191413615452, -79.261912128223); - EdgeIteratorState denison = graph.edge(2, 3).setWayGeometry(pointList2).set(encoder.getAccessEnc(), true, true).set(encoder.getAverageSpeedEnc(), 60, 60).setName("Denison Street"); + EdgeIteratorState denison = graph.edge(2, 3).setWayGeometry(pointList2).set(accessEnc, true, true). + set(speedEnc, 60, 60).setKeyValues(createKV(STREET_NAME, "Denison Street")); double qlat = 43.842122; double qLon = -79.262162; @@ -327,4 +304,14 @@ public void curvedWayGeometry_issue2319() { assertTrue(filter.accept(doubtfire)); } + private EdgeIteratorState createTestEdgeIterator(String name) { + PointList pointList = new PointList(); + pointList.add(basePoint); + EdgeIteratorState edge = new BaseGraph.Builder(1).create().edge(0, 0) + .setWayGeometry(pointList); + if (name != null) + edge.setKeyValues(KVStorage.KeyValue.createKV(KVStorage.KeyValue.STREET_NAME, name)); + return edge; + } + } diff --git a/core/src/test/java/com/graphhopper/routing/util/RoadsTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/RoadsTagParserTest.java deleted file mode 100644 index c2417160027..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/RoadsTagParserTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.storage.IntsRef; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -class RoadsTagParserTest { - - private final EncodingManager encodingManager = EncodingManager.create("roads"); - private final RoadsTagParser roadsEncoder = (RoadsTagParser) encodingManager.getEncoder("roads"); - - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(1); - assertTrue(roadsEncoder.getAccess(way).canSkip()); - - way.setTag("highway", "motorway"); - assertTrue(roadsEncoder.getAccess(way).isWay()); - way.setTag("highway", "footway"); - assertTrue(roadsEncoder.getAccess(way).isWay()); - } - - @Test - public void testSpeed() { - ReaderWay way = new ReaderWay(1); - IntsRef flags = roadsEncoder.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(roadsEncoder.getAverageSpeedEnc().getDecimal(false, flags) > 200); - } - -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/SlopeCalculatorTest.java b/core/src/test/java/com/graphhopper/routing/util/SlopeCalculatorTest.java new file mode 100644 index 00000000000..0a66a3b2811 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/SlopeCalculatorTest.java @@ -0,0 +1,96 @@ +package com.graphhopper.routing.util; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.PointList; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SlopeCalculatorTest { + + @Test + void simpleElevation() { + DecimalEncodedValue averageEnc = AverageSlope.create(); + DecimalEncodedValue maxEnc = MaxSlope.create(); + new EncodingManager.Builder().add(averageEnc).add(maxEnc).build(); + SlopeCalculator creator = new SlopeCalculator(maxEnc, averageEnc); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + ReaderWay way = new ReaderWay(1L); + PointList pointList = new PointList(5, true); + pointList.add(51.0, 12.001, 0); + pointList.add(51.0, 12.002, 3.5); // ~70m + pointList.add(51.0, 12.003, 4); // ~140m + pointList.add(51.0, 12.004, 2); // ~210m + way.setTag("point_list", pointList); + creator.handleWayTags(edgeId, edgeIntAccess, way, IntsRef.EMPTY); + + assertEquals(Math.round(2.0 / 210 * 100), averageEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-3); + assertEquals(-Math.round(2.0 / 210 * 100), averageEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-3); + + assertEquals(Math.round(1.75 / 105 * 100), maxEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-3); + assertEquals(Math.round(1.75 / 105 * 100), maxEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-3); + } + + @Test + public void testAveragingOfMaxSlope() { + // point=49.977518%2C11.564285&point=49.979878%2C11.563663&profile=bike + DecimalEncodedValue averageEnc = AverageSlope.create(); + DecimalEncodedValue maxEnc = MaxSlope.create(); + new EncodingManager.Builder().add(averageEnc).add(maxEnc).build(); + SlopeCalculator creator = new SlopeCalculator(maxEnc, averageEnc); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + ReaderWay way = new ReaderWay(1L); + PointList pointList = new PointList(5, true); + pointList.add(51.0, 12.0010, 10); + pointList.add(51.0, 12.0014, 8); // 28m + pointList.add(51.0, 12.0034, 8); // 140m + pointList.add(51.0, 12.0054, 0); // 140m + pointList.add(51.0, 12.0070, 7); // 112m + way.setTag("point_list", pointList); + creator.handleWayTags(edgeId, intAccess, way, IntsRef.EMPTY); + + assertEquals(Math.round(8.0 / 210 * 100), maxEnc.getDecimal(false, edgeId, intAccess), 1e-3); + assertEquals(Math.round(8.0 / 210 * 100), maxEnc.getDecimal(true, edgeId, intAccess), 1e-3); + } + + @Test + public void test2() { + PointList pointList = new PointList(5, true); + pointList.add(47.7281561, 11.9993135, 1163.0); + pointList.add(47.7282782, 11.9991944, 1163.0); + pointList.add(47.7283135, 11.9991135, 1178.0); + ReaderWay way = new ReaderWay(1); + way.setTag("point_list", pointList); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + DecimalEncodedValue averageEnc = AverageSlope.create(); + DecimalEncodedValue maxEnc = MaxSlope.create(); + new EncodingManager.Builder().add(averageEnc).add(maxEnc).build(); + SlopeCalculator creator = new SlopeCalculator(maxEnc, averageEnc); + creator.handleWayTags(edgeId, intAccess, way, IntsRef.EMPTY); + assertEquals(31, maxEnc.getDecimal(false, edgeId, intAccess), 1e-3); + assertEquals(31, averageEnc.getDecimal(false, edgeId, intAccess), 1e-3); + } + + @Test + public void test2D() { + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + PointList pointList = new PointList(5, false); + pointList.add(47.7283135, 11.9991135); + ReaderWay way = new ReaderWay(1); + way.setTag("point_list", pointList); + DecimalEncodedValue averageEnc = AverageSlope.create(); + DecimalEncodedValue maxEnc = MaxSlope.create(); + new EncodingManager.Builder().add(averageEnc).add(maxEnc).build(); + + SlopeCalculator creator = new SlopeCalculator(maxEnc, averageEnc); + creator.handleWayTags(edgeId, intAccess, way, IntsRef.EMPTY); + assertEquals(0, maxEnc.getDecimal(false, edgeId, intAccess), 1e-3); + assertEquals(0, averageEnc.getDecimal(false, edgeId, intAccess), 1e-3); + } +} diff --git a/core/src/test/java/com/graphhopper/routing/util/SnapPreventionEdgeFilterTest.java b/core/src/test/java/com/graphhopper/routing/util/SnapPreventionEdgeFilterTest.java index a29a5459a61..ae55b75487e 100644 --- a/core/src/test/java/com/graphhopper/routing/util/SnapPreventionEdgeFilterTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/SnapPreventionEdgeFilterTest.java @@ -3,8 +3,8 @@ import com.graphhopper.routing.ev.EnumEncodedValue; import com.graphhopper.routing.ev.RoadClass; import com.graphhopper.routing.ev.RoadEnvironment; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.GHUtility; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.EdgeIteratorState; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -21,17 +21,18 @@ public void accept() { EnumEncodedValue rcEnc = em.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); EnumEncodedValue reEnc = em.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class); SnapPreventionEdgeFilter filter = new SnapPreventionEdgeFilter(trueFilter, rcEnc, reEnc, Arrays.asList("motorway", "ferry")); + BaseGraph graph = new BaseGraph.Builder(em).create(); + EdgeIteratorState edge = graph.edge(0, 1).setDistance(1); - IntsRef intsRef = em.createEdgeFlags(); - assertTrue(filter.accept(GHUtility.createMockedEdgeIteratorState(1, intsRef))); - reEnc.setEnum(false, intsRef, RoadEnvironment.FERRY); - assertFalse(filter.accept(GHUtility.createMockedEdgeIteratorState(1, intsRef))); - reEnc.setEnum(false, intsRef, RoadEnvironment.FORD); - assertTrue(filter.accept(GHUtility.createMockedEdgeIteratorState(1, intsRef))); + assertTrue(filter.accept(edge)); + edge.set(reEnc, RoadEnvironment.FERRY); + assertFalse(filter.accept(edge)); + edge.set(reEnc, RoadEnvironment.FORD); + assertTrue(filter.accept(edge)); - rcEnc.setEnum(false, intsRef, RoadClass.RESIDENTIAL); - assertTrue(filter.accept(GHUtility.createMockedEdgeIteratorState(1, intsRef))); - rcEnc.setEnum(false, intsRef, RoadClass.MOTORWAY); - assertFalse(filter.accept(GHUtility.createMockedEdgeIteratorState(1, intsRef))); + edge.set(rcEnc, RoadClass.RESIDENTIAL); + assertTrue(filter.accept(edge)); + edge.set(rcEnc, RoadClass.MOTORWAY); + assertFalse(filter.accept(edge)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/TagParserManagerTest.java b/core/src/test/java/com/graphhopper/routing/util/TagParserManagerTest.java deleted file mode 100644 index 849808c688e..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/TagParserManagerTest.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderRelation; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.Roundabout; -import com.graphhopper.routing.ev.RouteNetwork; -import com.graphhopper.storage.IntsRef; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class TagParserManagerTest { - @Test - public void testToDetailsString() { - FlagEncoder encoder = new VehicleTagParser("new_encoder", 1, 2.0, true, 0) { - @Override - public TransportationMode getTransportationMode() { - return TransportationMode.BIKE; - } - - @Override - protected String getPropertiesString() { - return "my_properties"; - } - - @Override - public EncodingManager.Access getAccess(ReaderWay way) { - return EncodingManager.Access.WAY; - } - - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way) { - return edgeFlags; - } - }; - - EncodingManager subject = EncodingManager.create(encoder); - - assertEquals("new_encoder|my_properties", subject.toFlagEncodersAsString()); - } - - @Test - public void testCombineRelations() { - ReaderWay osmWay = new ReaderWay(1); - osmWay.setTag("highway", "track"); - ReaderRelation osmRel = new ReaderRelation(1); - - BikeTagParser defaultBike = new BikeTagParser(); - BikeTagParser lessRelationCodes = new BikeTagParser("less_relation_bits") { - @Override - public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way) { - if (bikeRouteEnc.getEnum(false, edgeFlags) != RouteNetwork.MISSING) - priorityEnc.setDecimal(false, edgeFlags, PriorityCode.getFactor(2)); - return edgeFlags; - } - }; - TagParserManager manager = new TagParserManager.Builder().add(lessRelationCodes).add(defaultBike).build(); - - // relation code is PREFER - osmRel.setTag("route", "bicycle"); - osmRel.setTag("network", "lcn"); - IntsRef relFlags = manager.handleRelationTags(osmRel, manager.createRelationFlags()); - IntsRef edgeFlags = manager.handleWayTags(osmWay, relFlags); - - assertTrue(defaultBike.priorityEnc.getDecimal(false, edgeFlags) - > lessRelationCodes.priorityEnc.getDecimal(false, edgeFlags)); - } - - @Test - public void testMixBikeTypesAndRelationCombination() { - ReaderWay osmWay = new ReaderWay(1); - osmWay.setTag("highway", "track"); - osmWay.setTag("tracktype", "grade1"); - - ReaderRelation osmRel = new ReaderRelation(1); - - BikeTagParser bikeEncoder = new BikeTagParser(); - MountainBikeTagParser mtbEncoder = new MountainBikeTagParser(); - TagParserManager manager = TagParserManager.create(bikeEncoder, mtbEncoder); - - // relation code for network rcn is NICE for bike and PREFER for mountainbike - osmRel.setTag("route", "bicycle"); - osmRel.setTag("network", "rcn"); - IntsRef relFlags = manager.handleRelationTags(osmRel, manager.createRelationFlags()); - IntsRef edgeFlags = manager.handleWayTags(osmWay, relFlags); - - // bike: uninfluenced speed for grade but via network => NICE - // mtb: uninfluenced speed only PREFER - assertTrue(bikeEncoder.priorityEnc.getDecimal(false, edgeFlags) - > mtbEncoder.priorityEnc.getDecimal(false, edgeFlags)); - } - - @Test - public void testCompatibilityBug() { - TagParserManager manager2 = TagParserManager.create(new DefaultFlagEncoderFactory(), "bike2"); - ReaderWay osmWay = new ReaderWay(1); - osmWay.setTag("highway", "footway"); - osmWay.setTag("name", "test"); - - BikeTagParser singleBikeEnc = (BikeTagParser) manager2.getEncoder("bike2"); - IntsRef flags = manager2.handleWayTags(osmWay, manager2.createRelationFlags()); - double singleSpeed = singleBikeEnc.avgSpeedEnc.getDecimal(false, flags); - assertEquals(4, singleSpeed, 1e-3); - assertEquals(singleSpeed, singleBikeEnc.avgSpeedEnc.getDecimal(true, flags), 1e-3); - - TagParserManager manager = TagParserManager.create(new DefaultFlagEncoderFactory(), "bike2,bike,foot"); - FootTagParser foot = (FootTagParser) manager.getEncoder("foot"); - BikeTagParser bike = (BikeTagParser) manager.getEncoder("bike2"); - - flags = manager.handleWayTags(osmWay, manager.createRelationFlags()); - assertEquals(singleSpeed, bike.avgSpeedEnc.getDecimal(false, flags), 1e-2); - assertEquals(singleSpeed, bike.avgSpeedEnc.getDecimal(true, flags), 1e-2); - - assertEquals(5, foot.avgSpeedEnc.getDecimal(false, flags), 1e-2); - assertEquals(5, foot.avgSpeedEnc.getDecimal(true, flags), 1e-2); - } - - @Test - public void testSharedEncodedValues() { - TagParserManager manager = TagParserManager.create("car,foot,bike,motorcycle,mtb"); - - BooleanEncodedValue roundaboutEnc = manager.getBooleanEncodedValue(Roundabout.KEY); - for (FlagEncoder tmp : manager.fetchEdgeEncoders()) { - BooleanEncodedValue accessEnc = tmp.getAccessEnc(); - - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "primary"); - way.setTag("junction", "roundabout"); - IntsRef edgeFlags = manager.handleWayTags(way, manager.createRelationFlags()); - assertTrue(accessEnc.getBool(false, edgeFlags)); - if (!(tmp instanceof FootTagParser)) - assertFalse(accessEnc.getBool(true, edgeFlags), tmp.toString()); - assertTrue(roundaboutEnc.getBool(false, edgeFlags), tmp.toString()); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("junction", "circular"); - edgeFlags = manager.handleWayTags(way, manager.createRelationFlags()); - assertTrue(accessEnc.getBool(false, edgeFlags)); - if (!(tmp instanceof FootTagParser)) - assertFalse(accessEnc.getBool(true, edgeFlags), tmp.toString()); - assertTrue(roundaboutEnc.getBool(false, edgeFlags), tmp.toString()); - } - } - -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/WheelchairTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/WheelchairTagParserTest.java deleted file mode 100644 index c87e6a5c8b2..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/WheelchairTagParserTest.java +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.routing.util; - -import com.graphhopper.reader.ReaderNode; -import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.storage.NodeAccess; -import com.graphhopper.util.EdgeExplorer; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.Helper; -import org.junit.jupiter.api.Test; - -import java.text.DateFormat; -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author don-philipe - */ -public class WheelchairTagParserTest { - private final EncodingManager encodingManager = EncodingManager.create("car,wheelchair"); - private final WheelchairTagParser wheelchairParser = (WheelchairTagParser) encodingManager.getEncoder("wheelchair"); - private final DecimalEncodedValue wheelchairAvSpeedEnc = wheelchairParser.getAverageSpeedEnc(); - private final BooleanEncodedValue wheelchairAccessEnc = wheelchairParser.getAccessEnc(); - private final DecimalEncodedValue carAvSpeedEnc = encodingManager.getEncoder("car").getAverageSpeedEnc(); - private final BooleanEncodedValue carAccessEnc = encodingManager.getEncoder("car").getAccessEnc(); - - @Test - public void testGetSpeed() { - IntsRef fl = encodingManager.createEdgeFlags(); - wheelchairAccessEnc.setBool(false, fl, true); - wheelchairAccessEnc.setBool(true, fl, true); - wheelchairAvSpeedEnc.setDecimal(false, fl, 10); - assertEquals(10, wheelchairAvSpeedEnc.getDecimal(false, fl), .1); - } - - @Test - public void testCombined() { - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - FlagEncoder carEncoder = encodingManager.getEncoder("car"); - EdgeIteratorState edge = g.edge(0, 1); - edge.set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); - edge.set(carAvSpeedEnc, 100.0).set(carAccessEnc, true, false); - - assertEquals(10, edge.get(wheelchairAvSpeedEnc), .1); - assertTrue(edge.get(wheelchairAccessEnc)); - assertTrue(edge.getReverse(wheelchairAccessEnc)); - - assertEquals(100, edge.get(carAvSpeedEnc), .1); - assertTrue(edge.get(carAccessEnc)); - assertFalse(edge.getReverse(carAccessEnc)); - - IntsRef raw = encodingManager.createEdgeFlags(); - wheelchairAvSpeedEnc.setDecimal(false, raw, 10); - wheelchairAccessEnc.setBool(false, raw, true); - wheelchairAccessEnc.setBool(true, raw, true); - assertEquals(0, carAvSpeedEnc.getDecimal(false, raw), .1); - } - - @Test - public void testGraph() { - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - g.edge(0, 1).setDistance(10).set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); - g.edge(0, 2).setDistance(10).set(wheelchairAvSpeedEnc, 5.0).set(wheelchairAccessEnc, true, true); - g.edge(1, 3).setDistance(10).set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); - EdgeExplorer out = g.createEdgeExplorer(AccessFilter.outEdges(wheelchairParser.getAccessEnc())); - assertEquals(GHUtility.asSet(1, 2), GHUtility.getNeighbors(out.setBaseNode(0))); - assertEquals(GHUtility.asSet(0, 3), GHUtility.getNeighbors(out.setBaseNode(1))); - assertEquals(GHUtility.asSet(0), GHUtility.getNeighbors(out.setBaseNode(2))); - } - - @Test - public void testAccess() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "motorway"); - way.setTag("sidewalk", "yes"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("sidewalk", "left"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("sidewalk", "none"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("sidewalk", "left"); - way.setTag("access", "private"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.clearTags(); - - way.setTag("highway", "pedestrian"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("highway", "footway"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("highway", "platform"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("highway", "motorway"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.setTag("bicycle", "official"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("foot", "no"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.setTag("foot", "official"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("access", "no"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("foot", "yes"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("vehicle", "no"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("foot", "no"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("motorroad", "yes"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "cycleway"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("foot", "no"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("access", "yes"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "service"); - way.setTag("foot", "yes"); - way.setTag("access", "no"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "track"); - way.setTag("ford", "yes"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("route", "ferry"); - assertTrue(wheelchairParser.getAccess(way).isFerry()); - way.setTag("foot", "no"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - // #1562, test if ferry route with foot - way.clearTags(); - way.setTag("route", "ferry"); - way.setTag("foot", "yes"); - assertTrue(wheelchairParser.getAccess(way).isFerry()); - - way.setTag("foot", "designated"); - assertTrue(wheelchairParser.getAccess(way).isFerry()); - - way.setTag("foot", "official"); - assertTrue(wheelchairParser.getAccess(way).isFerry()); - - way.setTag("foot", "permissive"); - assertTrue(wheelchairParser.getAccess(way).isFerry()); - - way.setTag("foot", "no"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.setTag("foot", "designated"); - way.setTag("access", "private"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("access", "no"); - way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "steps"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - // allow paths as they are used as generic path - way.setTag("highway", "path"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "track"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("sac_scale", "hiking"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "footway"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("incline", "up"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("incline", "3%"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("incline", "9.1%"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("incline", "1°"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("incline", "5°"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("incline", "-4%"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("incline", "-9%"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("incline", "-3°"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("incline", "-6.5°"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.clearTags(); - way.setTag("highway", "footway"); - way.setTag("wheelchair", "no"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("wheelchair", "limited"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "footway"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("kerb", "lowered"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("kerb", "raised"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("kerb", "2cm"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - way.setTag("kerb", "4cm"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("kerb", "20mm"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - // highway tag required - way.clearTags(); - way.setTag("wheelchair", "yes"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("highway", "footway"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - } - - @Test - public void testPier() { - ReaderWay way = new ReaderWay(1); - way.setTag("man_made", "pier"); - IntsRef flags = wheelchairParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertFalse(flags.isEmpty()); - } - - @Test - public void testMixSpeedAndSafe() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "motorway"); - IntsRef flags = wheelchairParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(flags.isEmpty()); - - way.setTag("sidewalk", "yes"); - flags = wheelchairParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertEquals(5, wheelchairAvSpeedEnc.getDecimal(false, flags), .1); - - way.clearTags(); - way.setTag("highway", "track"); - flags = wheelchairParser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertEquals(0, wheelchairAvSpeedEnc.getDecimal(false, flags), .1); - } - - @Test - public void testPriority() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "cycleway"); - assertEquals(PriorityCode.UNCHANGED.getValue(), wheelchairParser.handlePriority(way, null)); - way.setTag("highway", "primary"); - assertEquals(PriorityCode.AVOID.getValue(), wheelchairParser.handlePriority(way, null)); - way.setTag("highway", "secondary"); - assertEquals(PriorityCode.AVOID.getValue(), wheelchairParser.handlePriority(way, null)); - - way.setTag("highway", "track"); - way.setTag("bicycle", "official"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), wheelchairParser.handlePriority(way, null)); - - way.setTag("highway", "track"); - way.setTag("bicycle", "designated"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), wheelchairParser.handlePriority(way, null)); - - way.setTag("highway", "cycleway"); - way.setTag("bicycle", "designated"); - way.setTag("foot", "designated"); - assertEquals(PriorityCode.PREFER.getValue(), wheelchairParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "primary"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.UNCHANGED.getValue(), wheelchairParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "cycleway"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.UNCHANGED.getValue(), wheelchairParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "road"); - way.setTag("bicycle", "official"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), wheelchairParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "trunk"); - way.setTag("sidewalk", "no"); - assertEquals(PriorityCode.AVOID.getValue(), wheelchairParser.handlePriority(way, null)); - way.setTag("sidewalk", "none"); - assertEquals(PriorityCode.AVOID.getValue(), wheelchairParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "residential"); - way.setTag("sidewalk", "yes"); - assertEquals(PriorityCode.PREFER.getValue(), wheelchairParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "footway"); - assertEquals(PriorityCode.PREFER.getValue(), wheelchairParser.handlePriority(way, null)); - way.setTag("wheelchair", "designated"); - assertEquals(PriorityCode.VERY_NICE.getValue(), wheelchairParser.handlePriority(way, null)); - - way.clearTags(); - way.setTag("highway", "footway"); - assertEquals(PriorityCode.PREFER.getValue(), wheelchairParser.handlePriority(way, null)); - way.setTag("wheelchair", "limited"); - assertEquals(PriorityCode.AVOID.getValue(), wheelchairParser.handlePriority(way, null)); - } - - @Test - public void testBarrierAccess() { - // by default allow access through the gate for bike & foot! - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - // no barrier! - assertFalse(wheelchairParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "yes"); - // no barrier! - assertFalse(wheelchairParser.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - // barrier! - assertTrue(wheelchairParser.isBarrier(node)); - - node.setTag("bicycle", "yes"); - // no barrier!? - // assertTrue(wheelchairEncoder.handleNodeTags(node) == false); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "no"); - node.setTag("foot", "yes"); - // no barrier! - assertFalse(wheelchairParser.isBarrier(node)); - - node.setTag("locked", "yes"); - // barrier! - assertTrue(wheelchairParser.isBarrier(node)); - } - - @Test - public void testBlockByDefault() { - WheelchairTagParser tmpWheelchairEncoder = new WheelchairTagParser(); - EncodingManager.create(tmpWheelchairEncoder); - - ReaderNode node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - // passByDefaultBarriers are no barrier by default - assertFalse(tmpWheelchairEncoder.isBarrier(node)); - node.setTag("access", "no"); - assertTrue(tmpWheelchairEncoder.isBarrier(node)); - - // these barriers block - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "fence"); - assertTrue(tmpWheelchairEncoder.isBarrier(node)); - node.setTag("barrier", "wall"); - assertTrue(tmpWheelchairEncoder.isBarrier(node)); - node.setTag("barrier", "handrail"); - assertTrue(tmpWheelchairEncoder.isBarrier(node)); - node.setTag("barrier", "turnstile"); - assertTrue(tmpWheelchairEncoder.isBarrier(node)); - // Explictly allowed access is allowed - node.setTag("barrier", "fence"); - node.setTag("access", "yes"); - assertFalse(tmpWheelchairEncoder.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "gate"); - node.setTag("access", "yes"); - assertFalse(tmpWheelchairEncoder.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "kerb"); - assertFalse(tmpWheelchairEncoder.isBarrier(node)); - node.setTag("wheelchair", "yes"); - assertFalse(tmpWheelchairEncoder.isBarrier(node)); - - node = new ReaderNode(1, -1, -1); - node.setTag("barrier", "fence"); - assertTrue(tmpWheelchairEncoder.isBarrier(node)); - } - - @Test - public void testSurfaces() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "footway"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("surface", "cobblestone"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("surface", "sand"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - way.setTag("surface", "gravel"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.setTag("surface", "asphalt"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.clearTags(); - way.setTag("highway", "service"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("surface", "sand"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.setTag("sidewalk", "left"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("sidewalk:left:surface", "cobblestone"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - } - - @Test - public void testSmoothness() { - ReaderWay way = new ReaderWay(1); - - way.setTag("highway", "residential"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("smoothness", "bad"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - - way.setTag("sidewalk", "both"); - assertTrue(wheelchairParser.getAccess(way).isWay()); - - way.setTag("sidewalk:both:smoothness", "horrible"); - assertTrue(wheelchairParser.getAccess(way).canSkip()); - } - - @Test - public void testApplyWayTags() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).set3D(true).create(); - NodeAccess na = graph.getNodeAccess(); - // incline of 5% over all - na.setNode(0, 51.1, 12.0010, 50); - na.setNode(1, 51.1, 12.0015, 55); - EdgeIteratorState edge01 = graph.edge(0, 1).setWayGeometry(Helper.createPointList3D(51.1, 12.0011, 49, 51.1, 12.0015, 55)); - edge01.setDistance(100); - GHUtility.setSpeed(5, 5, wheelchairParser, edge01); - - // incline of 10% & shorter edge - na.setNode(2, 51.2, 12.1010, 50); - na.setNode(3, 51.2, 12.1015, 60); - EdgeIteratorState edge23 = graph.edge(2, 3).setWayGeometry(Helper.createPointList3D(51.2, 12.1011, 49, 51.2, 12.1015, 55)); - edge23.setDistance(30); - GHUtility.setSpeed(5, 5, wheelchairParser, edge23); - - // incline of 10% & longer edge - na.setNode(4, 51.2, 12.101, 50); - na.setNode(5, 51.2, 12.102, 60); - EdgeIteratorState edge45 = graph.edge(2, 3).setWayGeometry(Helper.createPointList3D(51.2, 12.1011, 49, 51.2, 12.1015, 55)); - edge45.setDistance(100); - GHUtility.setSpeed(5, 5, wheelchairParser, edge45); - - - wheelchairParser.applyWayTags(new ReaderWay(1), edge01); - - assertTrue(edge01.get(wheelchairAccessEnc)); - assertTrue(edge01.getReverse(wheelchairAccessEnc)); - assertEquals(2, edge01.get(wheelchairParser.getAverageSpeedEnc()), 0); - assertEquals(5, edge01.getReverse(wheelchairParser.getAverageSpeedEnc()), 0); - - - wheelchairParser.applyWayTags(new ReaderWay(2), edge23); - - assertTrue(edge23.get(wheelchairAccessEnc)); - assertTrue(edge23.getReverse(wheelchairAccessEnc)); - assertEquals(2, edge23.get(wheelchairParser.getAverageSpeedEnc()), 0); - assertEquals(2, edge23.getReverse(wheelchairParser.getAverageSpeedEnc()), 0); - - - // only exclude longer edges with too large incline: - wheelchairParser.applyWayTags(new ReaderWay(3), edge45); - - assertFalse(edge45.get(wheelchairAccessEnc)); - assertFalse(edge45.getReverse(wheelchairAccessEnc)); - } -} diff --git a/core/src/test/java/com/graphhopper/routing/util/countryrules/CountryRuleTest.java b/core/src/test/java/com/graphhopper/routing/util/countryrules/CountryRuleTest.java index 7585fd9322f..274dcfd5f67 100644 --- a/core/src/test/java/com/graphhopper/routing/util/countryrules/CountryRuleTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/countryrules/CountryRuleTest.java @@ -20,6 +20,9 @@ import com.graphhopper.reader.ReaderWay; import com.graphhopper.routing.ev.RoadAccess; import com.graphhopper.routing.util.TransportationMode; +import com.graphhopper.routing.util.countryrules.europe.AustriaCountryRule; +import com.graphhopper.routing.util.countryrules.europe.GermanyCountryRule; + import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java b/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java new file mode 100644 index 00000000000..2f61be148f2 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/AbstractBikeTagParserTester.java @@ -0,0 +1,659 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderNode; +import com.graphhopper.reader.ReaderRelation; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.OSMParsers; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.routing.util.VehicleTagParsers; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.Helper; +import com.graphhopper.util.PMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.text.DateFormat; +import java.util.Date; + +import static com.graphhopper.routing.util.PriorityCode.*; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Karich + * @author ratrun + */ +public abstract class AbstractBikeTagParserTester { + protected EncodingManager encodingManager; + protected BikeCommonAccessParser accessParser; + protected BikeCommonAverageSpeedParser speedParser; + protected BikeCommonPriorityParser priorityParser; + protected OSMParsers osmParsers; + protected DecimalEncodedValue priorityEnc; + protected DecimalEncodedValue avgSpeedEnc; + protected BooleanEncodedValue accessEnc; + + @BeforeEach + public void setUp() { + encodingManager = createEncodingManager(); + VehicleTagParsers parsers = createBikeTagParsers(encodingManager, new PMap("block_fords=true")); + accessParser = (BikeCommonAccessParser) parsers.getAccessParser(); + speedParser = (BikeCommonAverageSpeedParser) parsers.getSpeedParser(); + priorityParser = (BikeCommonPriorityParser) parsers.getPriorityParser(); + osmParsers = new OSMParsers() + .addRelationTagParser(relConfig -> new OSMBikeNetworkTagParser(encodingManager.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class), relConfig)) + .addWayTagParser(new OSMSmoothnessParser(encodingManager.getEnumEncodedValue(Smoothness.KEY, Smoothness.class))) + .addWayTagParser(accessParser).addWayTagParser(speedParser).addWayTagParser(priorityParser); + priorityEnc = priorityParser.getPriorityEnc(); + avgSpeedEnc = speedParser.getAverageSpeedEnc(); + accessEnc = accessParser.getAccessEnc(); + } + + protected abstract EncodingManager createEncodingManager(); + + protected abstract VehicleTagParsers createBikeTagParsers(EncodedValueLookup lookup, PMap pMap); + + protected void assertPriority(int expectedPrio, ReaderWay way) { + IntsRef relFlags = osmParsers.handleRelationTags(new ReaderRelation(0), osmParsers.createRelationFlags()); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + osmParsers.handleWayTags(edgeId, intAccess, way, relFlags); + assertEquals(PriorityCode.getValue(expectedPrio), priorityEnc.getDecimal(false, edgeId, intAccess), 0.01); + } + + protected void assertPriorityAndSpeed(int expectedPrio, double expectedSpeed, ReaderWay way) { + assertPriorityAndSpeed(expectedPrio, expectedSpeed, way, new ReaderRelation(0)); + } + + protected void assertPriorityAndSpeed(int expectedPrio, double expectedSpeed, ReaderWay way, ReaderRelation rel) { + IntsRef relFlags = osmParsers.handleRelationTags(rel, osmParsers.createRelationFlags()); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + osmParsers.handleWayTags(edgeId, intAccess, way, relFlags); + assertEquals(PriorityCode.getValue(expectedPrio), priorityEnc.getDecimal(false, edgeId, intAccess), 0.01); + assertEquals(expectedSpeed, avgSpeedEnc.getDecimal(false, edgeId, intAccess), 0.1); + assertEquals(expectedSpeed, avgSpeedEnc.getDecimal(true, edgeId, intAccess), 0.1); + } + + protected double getSpeedFromFlags(ReaderWay way) { + IntsRef relFlags = osmParsers.createRelationFlags(); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + osmParsers.handleWayTags(edgeId, intAccess, way, relFlags); + return avgSpeedEnc.getDecimal(false, edgeId, intAccess); + } + + @Test + public void testAccess() { + ReaderWay way = new ReaderWay(1); + + way.setTag("highway", "motorway"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("highway", "motorway"); + way.setTag("bicycle", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "footway"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("bicycle", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("highway", "footway"); + way.setTag("bicycle", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "pedestrian"); + way.setTag("bicycle", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("highway", "pedestrian"); + way.setTag("bicycle", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("bicycle", "yes"); + way.setTag("highway", "cycleway"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "path"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "path"); + way.setTag("bicycle", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + way.clearTags(); + + way.setTag("highway", "track"); + way.setTag("bicycle", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + way.clearTags(); + + way.setTag("highway", "track"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("mtb", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "path"); + way.setTag("foot", "official"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("bicycle", "official"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("motorroad", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "track"); + way.setTag("ford", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("bicycle", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "secondary"); + way.setTag("access", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("bicycle", "dismount"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "secondary"); + way.setTag("vehicle", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("bicycle", "dismount"); + assertTrue(accessParser.getAccess(way).isWay()); + + + way.clearTags(); + way.setTag("highway", "cycleway"); + way.setTag("cycleway", "track"); + way.setTag("railway", "abandoned"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "platform"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "platform"); + way.setTag("bicycle", "dismount"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "platform"); + way.setTag("bicycle", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("bicycle:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("bicycle", "yes"); // the conditional tag even overrules "yes" + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("access", "no"); + way.setTag("bicycle:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "track"); + way.setTag("vehicle", "forestry"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("bicycle", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + } + + @Test + public void testRelation() { + ReaderWay way = new ReaderWay(1); + + way.setTag("highway", "track"); + way.setTag("bicycle", "yes"); + way.setTag("foot", "yes"); + way.setTag("motor_vehicle", "agricultural"); + way.setTag("surface", "gravel"); + way.setTag("tracktype", "grade3"); + + ReaderRelation rel = new ReaderRelation(0); + rel.setTag("type", "route"); + rel.setTag("network", "rcn"); + rel.setTag("route", "bicycle"); + + ReaderRelation rel2 = new ReaderRelation(1); + rel2.setTag("type", "route"); + rel2.setTag("network", "lcn"); + rel2.setTag("route", "bicycle"); + + // two relation tags => we currently cannot store a list, so pick the lower ordinal 'regional' + // Example https://www.openstreetmap.org/way/213492914 => two hike 84544, 2768803 and two bike relations 3162932, 5254650 + IntsRef relFlags = osmParsers.handleRelationTags(rel2, osmParsers.handleRelationTags(rel, osmParsers.createRelationFlags())); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + osmParsers.handleWayTags(edgeId, intAccess, way, relFlags); + EnumEncodedValue enc = encodingManager.getEnumEncodedValue(RouteNetwork.key("bike"), RouteNetwork.class); + assertEquals(RouteNetwork.REGIONAL, enc.getEnum(false, edgeId, intAccess)); + } + + @Test + public void testTramStations() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "secondary"); + way.setTag("railway", "rail"); + assertTrue(accessParser.getAccess(way).isWay()); + + way = new ReaderWay(1); + way.setTag("highway", "secondary"); + way.setTag("railway", "station"); + assertTrue(accessParser.getAccess(way).isWay()); + + way = new ReaderWay(1); + way.setTag("highway", "secondary"); + way.setTag("railway", "station"); + way.setTag("bicycle", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("bicycle", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way = new ReaderWay(1); + way.setTag("railway", "platform"); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + accessParser.handleWayTags(edgeId, intAccess, way, null); + speedParser.handleWayTags(edgeId, intAccess, way, null); + assertEquals(4.0, avgSpeedEnc.getDecimal(false, edgeId, intAccess)); + assertTrue(accessEnc.getBool(false, edgeId, intAccess)); + + way = new ReaderWay(1); + way.setTag("highway", "track"); + way.setTag("railway", "platform"); + accessParser.handleWayTags(edgeId, intAccess, way, null); + assertTrue(accessEnc.getBool(false, edgeId, intAccess)); + + speedParser.handleWayTags(edgeId, intAccess, way, null); + assertEquals(4, avgSpeedEnc.getDecimal(false, edgeId, intAccess)); + + way = new ReaderWay(1); + way.setTag("highway", "track"); + way.setTag("railway", "platform"); + way.setTag("bicycle", "no"); + + intAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + accessParser.handleWayTags(edgeId, intAccess, way); + assertEquals(0.0, avgSpeedEnc.getDecimal(false, edgeId, intAccess)); + assertFalse(accessEnc.getBool(false, edgeId, intAccess)); + } + + @Test + public void testAvoidTunnel() { + ReaderWay osmWay = new ReaderWay(1); + osmWay.setTag("highway", "residential"); + assertPriority(PREFER.getValue(), osmWay); + + osmWay.setTag("tunnel", "yes"); + assertPriority(UNCHANGED.getValue(), osmWay); + + osmWay.setTag("highway", "secondary"); + osmWay.setTag("tunnel", "yes"); + assertPriority(BAD.getValue(), osmWay); + + osmWay.setTag("bicycle", "designated"); + assertPriority(PREFER.getValue(), osmWay); + } + + @Test + public void testTram() { + ReaderWay way = new ReaderWay(1); + // very dangerous + way.setTag("highway", "secondary"); + way.setTag("railway", "tram"); + assertPriority(AVOID_MORE.getValue(), way); + + // should be safe now + way.setTag("bicycle", "designated"); + assertPriority(PREFER.getValue(), way); + } + + @Test + public void testService() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "service"); + assertPriorityAndSpeed(PREFER.getValue(), 12, way); + + way.setTag("service", "parking_aisle"); + assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), 4, way); + } + + @Test + public void testSacScale() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "service"); + way.setTag("sac_scale", "hiking"); + // allow + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("sac_scale", "alpine_hiking"); + assertTrue(accessParser.getAccess(way).canSkip()); + } + + @Test + public void testReduceToMaxSpeed() { + ReaderWay way = new ReaderWay(12); + way.setTag("maxspeed", "90"); + assertEquals(12, speedParser.applyMaxSpeed(way, 12, true), 1e-2); + } + + @Test + public void testPreferenceForSlowSpeed() { + ReaderWay osmWay = new ReaderWay(1); + osmWay.setTag("highway", "tertiary"); + assertPriority(PREFER.getValue(), osmWay); + } + + @Test + public void testHandleWayTagsCallsHandlePriority() { + ReaderWay osmWay = new ReaderWay(1); + osmWay.setTag("highway", "cycleway"); + + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + priorityParser.handleWayTags(edgeId, intAccess, osmWay, null); + assertEquals(PriorityCode.getValue(VERY_NICE.getValue()), priorityEnc.getDecimal(false, edgeId, intAccess), 1e-3); + } + + @Test + public void testAvoidMotorway() { + ReaderWay osmWay = new ReaderWay(1); + osmWay.setTag("highway", "motorway"); + osmWay.setTag("bicycle", "yes"); + assertPriority(AVOID.getValue(), osmWay); + } + + @Test + public void testLockedGate() { + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("locked", "yes"); + assertTrue(accessParser.isBarrier(node)); + } + + @Test + public void testNoBike() { + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("bicycle", "no"); + assertTrue(accessParser.isBarrier(node)); + } + + @Test + public void testBarrierAccess() { + // by default allow access through the gate for bike & foot! + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + + node.setTag("bicycle", "yes"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "no"); + // barrier! + assertTrue(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "yes"); + node.setTag("bicycle", "no"); + // barrier! + assertTrue(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "no"); + node.setTag("foot", "yes"); + // barrier! + assertTrue(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "no"); + node.setTag("bicycle", "yes"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + } + + @Test + public void testBarrierAccessFord() { + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("ford", "yes"); + // barrier! + assertTrue(accessParser.isBarrier(node)); + + node.setTag("bicycle", "yes"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + } + + @Test + public void testFerries() { + ReaderWay way = new ReaderWay(1); + + way.clearTags(); + way.setTag("route", "ferry"); + assertTrue(accessParser.getAccess(way).isFerry()); + way.setTag("bicycle", "no"); + assertFalse(accessParser.getAccess(way).isFerry()); + + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("foot", "yes"); + assertFalse(accessParser.getAccess(way).isFerry()); + + // #1122 + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("bicycle", "yes"); + way.setTag("access", "private"); + assertTrue(accessParser.getAccess(way).canSkip()); + + // #1562, test if ferry route with bicycle + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("bicycle", "designated"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("bicycle", "official"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("bicycle", "permissive"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "yes"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("bicycle", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("bicycle", "designated"); + way.setTag("access", "private"); + assertTrue(accessParser.getAccess(way).canSkip()); + + // test if when foot is set is invalid + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("foot", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + } + + @Test + void privateAndFords() { + // defaults: do not block fords, block private + BikeCommonAccessParser bike = (BikeCommonAccessParser) createBikeTagParsers(encodingManager, new PMap()).getAccessParser(); + assertFalse(bike.isBlockFords()); + assertTrue(bike.restrictedValues.contains("private")); + assertFalse(bike.intendedValues.contains("private")); + ReaderNode node = new ReaderNode(1, 1, 1); + node.setTag("access", "private"); + assertTrue(bike.isBarrier(node)); + + // block fords, unblock private + bike = (BikeCommonAccessParser) createBikeTagParsers(encodingManager, new PMap("block_fords=true|block_private=false")).getAccessParser(); + assertTrue(bike.isBlockFords()); + assertFalse(bike.restrictedValues.contains("private")); + assertTrue(bike.intendedValues.contains("private")); + assertFalse(bike.isBarrier(node)); + } + + @Test + public void testOneway() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "tertiary"); + assertAccess(way, true, true); + + way.setTag("oneway", "yes"); + assertAccess(way, true, false); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("oneway:bicycle", "yes"); + assertAccess(way, true, false); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("oneway", "yes"); + way.setTag("oneway:bicycle", "no"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("oneway", "yes"); + way.setTag("oneway:bicycle", "-1"); + assertAccess(way, false, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("oneway", "yes"); + way.setTag("cycleway:right:oneway", "no"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("oneway", "yes"); + way.setTag("cycleway:right:oneway", "-1"); + assertAccess(way, false, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("vehicle:forward", "no"); + assertAccess(way, false, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("bicycle:forward", "no"); + assertAccess(way, false, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("vehicle:backward", "no"); + assertAccess(way, true, false); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("motor_vehicle:backward", "no"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("oneway", "yes"); + way.setTag("bicycle:backward", "no"); + assertAccess(way, true, false); + + way.setTag("bicycle:backward", "yes"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "residential"); + way.setTag("oneway", "yes"); + way.setTag("bicycle:backward", "yes"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "residential"); + way.setTag("oneway", "-1"); + way.setTag("bicycle:forward", "yes"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("bicycle:forward", "use_sidepath"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("bicycle:forward", "use_sidepath"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("oneway", "yes"); + way.setTag("cycleway", "opposite"); + assertAccess(way, true, true); + + way.clearTags(); + way.setTag("highway", "residential"); + way.setTag("oneway", "yes"); + way.setTag("cycleway:left", "opposite_lane"); + assertAccess(way, true, true); + } + + private void assertAccess(ReaderWay way, boolean fwd, boolean bwd) { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edge = 0; + IntsRef edgeFlags = new IntsRef(1); + IntsRef relationFlags = new IntsRef(1); + accessParser.handleWayTags(edge, edgeIntAccess, way, relationFlags); + if (fwd) assertTrue(accessEnc.getBool(false, edge, edgeIntAccess)); + if (bwd) assertTrue(accessEnc.getBool(true, edge, edgeIntAccess)); + } +} diff --git a/core/src/test/java/com/graphhopper/routing/util/BikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java similarity index 68% rename from core/src/test/java/com/graphhopper/routing/util/BikeTagParserTest.java rename to core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java index cc0abec4b69..654a96f030c 100644 --- a/core/src/test/java/com/graphhopper/routing/util/BikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/BikeTagParserTest.java @@ -15,17 +15,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.graphhopper.routing.util; +package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderNode; import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.routing.util.VehicleEncodedValues; +import com.graphhopper.routing.util.VehicleTagParsers; import com.graphhopper.storage.IntsRef; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; -import static com.graphhopper.routing.util.BikeCommonTagParser.PUSHING_SECTION_SPEED; import static com.graphhopper.routing.util.PriorityCode.*; +import static com.graphhopper.routing.util.parsers.BikeCommonAverageSpeedParser.MIN_SPEED; +import static com.graphhopper.routing.util.parsers.BikeCommonAverageSpeedParser.PUSHING_SECTION_SPEED; import static org.junit.jupiter.api.Assertions.*; /** @@ -35,8 +41,13 @@ public class BikeTagParserTest extends AbstractBikeTagParserTester { @Override - protected BikeTagParser createBikeTagParser() { - return new BikeTagParser(new PMap("block_fords=true")); + protected EncodingManager createEncodingManager() { + return new EncodingManager.Builder().add(VehicleEncodedValues.bike(new PMap())).build(); + } + + @Override + protected VehicleTagParsers createBikeTagParsers(EncodedValueLookup lookup, PMap pMap) { + return VehicleTagParsers.bike(lookup, pMap); } @Test @@ -66,6 +77,11 @@ public void testSpeedAndPriority() { way.setTag("bicycle", "dismount"); assertPriorityAndSpeed(AVOID.getValue(), PUSHING_SECTION_SPEED, way); + way.clearTags(); + way.setTag("highway", "secondary"); + way.setTag("hazmat", "designated"); + assertPriorityAndSpeed(BAD.getValue(), 18, way); + way.clearTags(); way.setTag("highway", "footway"); way.setTag("bicycle", "yes"); @@ -129,7 +145,7 @@ public void testSpeedAndPriority() { assertPriorityAndSpeed(PREFER.getValue(), cyclewaySpeed, way); way.setTag("surface", "unpaved"); - assertPriorityAndSpeed(PREFER.getValue(), 14, way); + assertPriorityAndSpeed(PREFER.getValue(), 12, way); way.setTag("surface", "paved"); assertPriorityAndSpeed(PREFER.getValue(), 18, way); @@ -194,9 +210,9 @@ public void testSpeedAndPriority() { way.clearTags(); way.setTag("highway", "steps"); way.setTag("surface", "wood"); - assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), PUSHING_SECTION_SPEED / 2.0, way); + assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), MIN_SPEED, way); way.setTag("maxspeed", "20"); - assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), PUSHING_SECTION_SPEED / 2.0, way); + assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), MIN_SPEED, way); way.clearTags(); way.setTag("highway", "track"); @@ -256,13 +272,13 @@ public void testSmoothness() { assertEquals(20, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "bad"); - assertEquals(14, getSpeedFromFlags(way), 0.01); + assertEquals(12, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "impassable"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(MIN_SPEED, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "unknown"); - assertEquals(14, getSpeedFromFlags(way), 0.01); + assertEquals(12, getSpeedFromFlags(way), 0.01); way.clearTags(); way.setTag("highway", "residential"); @@ -275,13 +291,13 @@ public void testSmoothness() { way.clearTags(); way.setTag("highway", "track"); way.setTag("tracktype", "grade5"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(4, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "bad"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(2, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "impassable"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(MIN_SPEED, getSpeedFromFlags(way), 0.01); } @Test @@ -315,145 +331,39 @@ public void testWayAcceptance() { ReaderWay way = new ReaderWay(1); way.setTag("highway", "cycleway"); way.setTag("vehicle", "no"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); // Sensless tagging: JOSM does create a warning here. We follow the highway tag: way.setTag("bicycle", "no"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.setTag("bicycle", "designated"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.clearTags(); way.setTag("highway", "motorway"); - assertTrue(parser.getAccess(way).canSkip()); + assertTrue(accessParser.getAccess(way).canSkip()); way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.clearTags(); way.setTag("highway", "residential"); way.setTag("bicycle", "yes"); way.setTag("access", "no"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.clearTags(); way.setTag("highway", "bridleway"); - assertTrue(parser.getAccess(way).canSkip()); + assertTrue(accessParser.getAccess(way).canSkip()); way.setTag("bicycle", "yes"); - assertTrue(parser.getAccess(way).isWay()); - - } - - @Test - public void testOneway() { - ReaderWay way = new ReaderWay(1); - way.setTag("highway", "tertiary"); - IntsRef flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - way.setTag("oneway", "yes"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertFalse(parser.getAccessEnc().getBool(true, flags)); - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("oneway:bicycle", "yes"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertFalse(parser.getAccessEnc().getBool(true, flags)); - way.clearTags(); - - way.setTag("highway", "tertiary"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - way.clearTags(); - - way.setTag("highway", "tertiary"); - way.setTag("vehicle:forward", "no"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertFalse(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - way.clearTags(); + assertTrue(accessParser.getAccess(way).isWay()); - way.setTag("highway", "tertiary"); - way.setTag("bicycle:forward", "no"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertFalse(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - way.clearTags(); - - way.setTag("highway", "tertiary"); - way.setTag("vehicle:backward", "no"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertFalse(parser.getAccessEnc().getBool(true, flags)); way.clearTags(); - - way.setTag("highway", "tertiary"); - way.setTag("motor_vehicle:backward", "no"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - way.clearTags(); - - way.setTag("highway", "tertiary"); - way.setTag("oneway", "yes"); - way.setTag("bicycle:backward", "no"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertFalse(parser.getAccessEnc().getBool(true, flags)); - - way.setTag("bicycle:backward", "yes"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - - way.clearTags(); - way.setTag("highway", "residential"); - way.setTag("oneway", "yes"); - way.setTag("bicycle:backward", "yes"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - - way.clearTags(); - way.setTag("highway", "residential"); - way.setTag("oneway", "-1"); - way.setTag("bicycle:forward", "yes"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("bicycle:forward", "use_sidepath"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("bicycle:forward", "use_sidepath"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - - way.clearTags(); - way.setTag("highway", "tertiary"); - way.setTag("oneway", "yes"); - way.setTag("cycleway", "opposite"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); - - way.clearTags(); - way.setTag("highway", "residential"); - way.setTag("oneway", "yes"); - way.setTag("cycleway:left", "opposite_lane"); - flags = parser.handleWayTags(encodingManager.createEdgeFlags(), way); - assertTrue(parser.getAccessEnc().getBool(false, flags)); - assertTrue(parser.getAccessEnc().getBool(true, flags)); + way.setTag("highway", "track"); + way.setTag("vehicle", "forestry"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("vehicle", "agricultural;forestry"); + assertTrue(accessParser.getAccess(way).canSkip()); } @Test @@ -464,7 +374,12 @@ public void testHandleWayTagsInfluencedByRelation() { // unchanged assertPriorityAndSpeed(UNCHANGED.getValue(), 12, osmWay); - // relation code is + // "lcn=yes" is in fact no relation, but shall be treated the same like a relation with "network=lcn" + osmWay.setTag("lcn", "yes"); + assertPriorityAndSpeed(PREFER.getValue(), 12, osmWay); + osmWay.removeTag("lcn"); + + // relation code is PREFER ReaderRelation osmRel = new ReaderRelation(1); osmRel.setTag("route", "bicycle"); assertPriorityAndSpeed(PREFER.getValue(), 12, osmWay, osmRel); @@ -475,6 +390,8 @@ public void testHandleWayTagsInfluencedByRelation() { // relation code is NICE osmRel.setTag("network", "rcn"); assertPriorityAndSpeed(VERY_NICE.getValue(), 12, osmWay, osmRel); + osmWay.setTag("lcn", "yes"); + assertPriorityAndSpeed(VERY_NICE.getValue(), 12, osmWay, osmRel); // relation code is BEST osmRel.setTag("network", "ncn"); @@ -504,20 +421,20 @@ public void testSacScale() { ReaderWay way = new ReaderWay(1); way.setTag("highway", "path"); way.setTag("sac_scale", "hiking"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.setTag("highway", "path"); way.setTag("sac_scale", "mountain_hiking"); - assertTrue(parser.getAccess(way).canSkip()); + assertTrue(accessParser.getAccess(way).canSkip()); way.setTag("highway", "cycleway"); way.setTag("sac_scale", "hiking"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.setTag("highway", "cycleway"); way.setTag("sac_scale", "mountain_hiking"); // disallow questionable combination as too dangerous - assertTrue(parser.getAccess(way).canSkip()); + assertTrue(accessParser.getAccess(way).canSkip()); } @Test @@ -527,22 +444,30 @@ public void testCalcPriority() { ReaderRelation osmRel = new ReaderRelation(1); osmRel.setTag("route", "bicycle"); osmRel.setTag("network", "icn"); - IntsRef relFlags = encodingManager.handleRelationTags(osmRel, encodingManager.createRelationFlags()); - IntsRef flags = encodingManager.handleWayTags(osmWay, relFlags); - assertEquals(PriorityCode.getValue(BEST.getValue()), priorityEnc.getDecimal(false, flags), .1); + IntsRef relFlags = osmParsers.handleRelationTags(osmRel, osmParsers.createRelationFlags()); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + osmParsers.handleWayTags(edgeId, edgeIntAccess, osmWay, relFlags); + assertEquals(RouteNetwork.INTERNATIONAL, encodingManager.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class).getEnum(false, edgeId, edgeIntAccess)); + assertEquals(PriorityCode.getValue(BEST.getValue()), priorityEnc.getDecimal(false, edgeId, edgeIntAccess), .1); // for some highways the priority is UNCHANGED + osmRel = new ReaderRelation(1); osmWay = new ReaderWay(1); osmWay.setTag("highway", "track"); - flags = encodingManager.handleWayTags(osmWay, encodingManager.createRelationFlags()); - assertEquals(PriorityCode.getValue(UNCHANGED.getValue()), priorityEnc.getDecimal(false, flags), .1); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + osmParsers.handleWayTags(edgeId, edgeIntAccess, osmWay, osmParsers.createRelationFlags()); + assertEquals(RouteNetwork.MISSING, encodingManager.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class).getEnum(false, edgeId, edgeIntAccess)); + assertEquals(PriorityCode.getValue(UNCHANGED.getValue()), priorityEnc.getDecimal(false, edgeId, edgeIntAccess), .1); - // for unknown highways we should probably keep the priority unchanged, but currently it does not matter - // because the access will be false anyway + // unknown highway tags will be excluded but priority will be unchanged osmWay = new ReaderWay(1); osmWay.setTag("highway", "whatever"); - flags = encodingManager.handleWayTags(osmWay, encodingManager.createRelationFlags()); - assertEquals(EXCLUDE.getValue(), priorityEnc.getDecimal(false, flags), .1); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + osmParsers.handleWayTags(edgeId, edgeIntAccess, osmWay, osmParsers.createRelationFlags()); + assertFalse(accessParser.getAccessEnc().getBool(false, edgeId, edgeIntAccess)); + assertEquals(RouteNetwork.MISSING, encodingManager.getEnumEncodedValue(BikeNetwork.KEY, RouteNetwork.class).getEnum(false, edgeId, edgeIntAccess)); + assertEquals(PriorityCode.getValue(UNCHANGED.getValue()), priorityEnc.getDecimal(false, edgeId, edgeIntAccess), .1); } @Test @@ -562,13 +487,13 @@ public void testMaxSpeed() { way = new ReaderWay(1); way.setTag("highway", "secondary"); way.setTag("maxspeed", "10"); - assertPriorityAndSpeed(PREFER.getValue(), 10, way); + assertPriorityAndSpeed(VERY_NICE.getValue(), 10, way); way = new ReaderWay(1); way.setTag("highway", "residential"); way.setTag("maxspeed", "15"); - // todonow!! speed is larger than maxspeed - assertPriorityAndSpeed(PREFER.getValue(), 16, way); + // todo: speed is larger than maxspeed tag due to rounding and storable max speed is 30 + assertPriorityAndSpeed(VERY_NICE.getValue(), 16, way); } // Issue 407 : Always block kissing_gate except for mountainbikes @@ -579,19 +504,19 @@ public void testBarrierAccess() { ReaderNode node = new ReaderNode(1, -1, -1); node.setTag("barrier", "kissing_gate"); // barrier! - assertTrue(parser.isBarrier(node)); + assertTrue(accessParser.isBarrier(node)); // kissing_gate with bicycle tag node = new ReaderNode(1, -1, -1); node.setTag("barrier", "kissing_gate"); node.setTag("bicycle", "yes"); // no barrier! - assertFalse(parser.isBarrier(node)); + assertFalse(accessParser.isBarrier(node)); // Test if cattle_grid is non blocking node = new ReaderNode(1, -1, -1); node.setTag("barrier", "cattle_grid"); - assertFalse(parser.isBarrier(node)); + assertFalse(accessParser.isBarrier(node)); } @Test @@ -629,6 +554,6 @@ public void testClassBicycle() { assertPriority(VERY_NICE.getValue(), way); way.setTag("maxspeed", "15"); - assertPriority(VERY_NICE.getValue(), way); + assertPriority(BEST.getValue(), way); } } diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java new file mode 100644 index 00000000000..c2dbb4da03e --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/CarTagParserTest.java @@ -0,0 +1,733 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderNode; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.routing.util.WayAccess; +import com.graphhopper.util.Helper; +import com.graphhopper.util.PMap; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.text.DateFormat; +import java.util.Arrays; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Karich + */ +public class CarTagParserTest { + private final EncodingManager em = createEncodingManager("car"); + final CarAccessParser parser = createParser(em, new PMap("block_fords=true")); + final CarAverageSpeedParser speedParser = new CarAverageSpeedParser(em, new PMap("block_fords=true")); + private final BooleanEncodedValue roundaboutEnc = em.getBooleanEncodedValue(Roundabout.KEY); + private final BooleanEncodedValue accessEnc = parser.getAccessEnc(); + private final DecimalEncodedValue avSpeedEnc = speedParser.getAverageSpeedEnc(); + + private EncodingManager createEncodingManager(String carName) { + return new EncodingManager.Builder() + .add(VehicleAccess.create(carName)) + .add(VehicleSpeed.create(carName, 5, 5, true)) + .addTurnCostEncodedValue(TurnCost.create(carName, 1)) + .add(VehicleAccess.create("bike")) + .add(VehicleSpeed.create("bike", 4, 2, false)) + .add(VehiclePriority.create("bike", 4, PriorityCode.getFactor(1), false)) + .add(RouteNetwork.create(BikeNetwork.KEY)) + .add(Smoothness.create()) + .build(); + } + + CarAccessParser createParser(EncodedValueLookup lookup, PMap properties) { + CarAccessParser carTagParser = new CarAccessParser(lookup, properties); + carTagParser.init(new DateRangeParser()); + return carTagParser; + } + + @Test + public void testAccess() { + ReaderWay way = new ReaderWay(1); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("highway", "service"); + assertTrue(parser.getAccess(way).isWay()); + way.setTag("access", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "track"); + assertTrue(parser.getAccess(way).isWay()); + + way.setTag("motorcar", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + // for now allow grade1+2+3 for every country, see #253 + way.clearTags(); + way.setTag("highway", "track"); + way.setTag("tracktype", "grade2"); + assertTrue(parser.getAccess(way).isWay()); + way.setTag("tracktype", "grade4"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "delivery"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "unclassified"); + way.setTag("ford", "yes"); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("motorcar", "yes"); + assertTrue(parser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("access", "yes"); + way.setTag("motor_vehicle", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "yes"); + way.setTag("motor_vehicle", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "track"); + way.setTag("motor_vehicle", "agricultural"); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("motor_vehicle", "agricultural;forestry"); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("motor_vehicle", "forestry;agricultural"); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("motor_vehicle", "forestry;agricultural;unknown"); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("motor_vehicle", "yes;forestry;agricultural"); + assertTrue(parser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "no"); + way.setTag("motorcar", "yes"); + assertTrue(parser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "emergency"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("motor_vehicle", "emergency"); + assertTrue(parser.getAccess(way).canSkip()); + + DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("access", "no"); + way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(parser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("access", "yes"); + way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("service", "emergency_access"); + assertTrue(parser.getAccess(way).canSkip()); + } + + @Test + public void testMilitaryAccess() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "track"); + way.setTag("access", "military"); + assertTrue(parser.getAccess(way).canSkip()); + } + + @Test + public void testFordAccess() { + ReaderNode node = new ReaderNode(0, 0.0, 0.0); + node.setTag("ford", "yes"); + + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "unclassified"); + way.setTag("ford", "yes"); + + // Node and way are initially blocking + assertTrue(parser.isBlockFords()); + assertTrue(parser.getAccess(way).canSkip()); + assertTrue(parser.isBarrier(node)); + + CarAccessParser tmpParser = new CarAccessParser(em, new PMap("block_fords=false")); + tmpParser.init(new DateRangeParser()); + assertTrue(tmpParser.getAccess(way).isWay()); + assertFalse(tmpParser.isBarrier(node)); + } + + @Test + public void testOneway() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + way.setTag("oneway", "yes"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(accessEnc.getBool(true, edgeId, edgeIntAccess)); + way.clearTags(); + + way.setTag("highway", "tertiary"); + + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + way.clearTags(); + + way.setTag("highway", "tertiary"); + way.setTag("vehicle:forward", "no"); + + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertFalse(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + way.clearTags(); + + way.setTag("highway", "tertiary"); + way.setTag("vehicle:backward", "no"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(accessEnc.getBool(true, edgeId, edgeIntAccess)); + way.clearTags(); + + // This is no one way + way.setTag("highway", "tertiary"); + way.setTag("vehicle:backward", "designated"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + way.clearTags(); + } + + @Test + public void shouldBlockPrivate() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("access", "private"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertFalse(accessEnc.getBool(false, edgeId, edgeIntAccess)); + + final CarAccessParser parser = createParser(em, new PMap("block_private=false")); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(parser.getAccessEnc().getBool(false, edgeId, edgeIntAccess)); + + way.setTag("highway", "primary"); + way.setTag("motor_vehicle", "permit"); // currently handled like "private", see #2712 + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(parser.getAccessEnc().getBool(false, edgeId, edgeIntAccess)); + } + + @Test + public void testSetAccess() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, false); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(accessEnc.getBool(true, edgeId, edgeIntAccess)); + + accessEnc.setBool(false, edgeId, edgeIntAccess, false); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + assertFalse(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + + accessEnc.setBool(false, edgeId, edgeIntAccess, false); + accessEnc.setBool(true, edgeId, edgeIntAccess, false); + assertFalse(accessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @Test + public void testMaxSpeed() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "trunk"); + way.setTag("maxspeed", "500"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(140, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("maxspeed:backward", "10"); + way.setTag("maxspeed:forward", "20"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(20, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + assertEquals(10, avSpeedEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-1); + + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("maxspeed:forward", "20"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(20, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("maxspeed:backward", "20"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(65, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + assertEquals(20, avSpeedEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-1); + + way = new ReaderWay(1); + way.setTag("highway", "motorway"); + way.setTag("maxspeed", "none"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(135, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + + way = new ReaderWay(1); + way.setTag("highway", "motorway_link"); + way.setTag("maxspeed", "70 mph"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(100, avSpeedEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-1); + } + + @Test + public void testSpeed() { + // limit bigger than default road speed + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "trunk"); + way.setTag("maxspeed", "110"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(100, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.clearTags(); + way.setTag("highway", "residential"); + way.setTag("surface", "cobblestone"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(30, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.clearTags(); + way.setTag("highway", "track"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(15, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.clearTags(); + way.setTag("highway", "track"); + way.setTag("tracktype", "grade1"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(20, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.clearTags(); + way.setTag("highway", "secondary"); + way.setTag("surface", "compacted"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + + assertEquals(30, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.clearTags(); + way.setTag("highway", "secondary"); + way.setTag("motorroad", "yes"); // motorroad should not influence speed. only access for non-motor vehicles + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + + assertEquals(60, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.clearTags(); + way.setTag("highway", "motorway"); + way.setTag("motorroad", "yes"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(100, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.clearTags(); + way.setTag("highway", "motorway_link"); + way.setTag("motorroad", "yes"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(70, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + try { + avSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, -1); + assertTrue(false); + } catch (IllegalArgumentException ex) { + } + } + + @Test + public void testSetSpeed() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + avSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 10); + assertEquals(10, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + } + + @Test + public void testSetSpeed0_issue367_issue1234() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + speedParser.setSpeed(false, edgeId, edgeIntAccess, 30); + speedParser.setSpeed(true, edgeId, edgeIntAccess, 40); + + // round down only for very low speed values + speedParser.setSpeed(false, edgeId, edgeIntAccess, 0.09); + assertEquals(0, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + + // this is independent from the speed + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + + // and does not affect the reverse direction: + assertEquals(40, avSpeedEnc.getDecimal(true, edgeId, edgeIntAccess), .1); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + + // for low speed values (and low precision of the EncodedValue) it can happen that the speed is increased: + speedParser.setSpeed(false, edgeId, edgeIntAccess, 1); + assertEquals(avSpeedEnc.getSmallestNonZeroValue(), avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @Test + public void testRoundabout() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + accessEnc.setBool(false, edgeId, edgeIntAccess, true); + accessEnc.setBool(true, edgeId, edgeIntAccess, true); + roundaboutEnc.setBool(false, edgeId, edgeIntAccess, true); + assertTrue(roundaboutEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + + roundaboutEnc.setBool(false, edgeId, edgeIntAccess, false); + assertFalse(roundaboutEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "motorway"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + parser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(accessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(accessEnc.getBool(true, edgeId, edgeIntAccess)); + assertFalse(roundaboutEnc.getBool(false, edgeId, edgeIntAccess)); + } + + @Test + public void testRailway() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "secondary"); + way.setTag("railway", "rail"); + assertTrue(parser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "path"); + way.setTag("railway", "abandoned"); + assertTrue(parser.getAccess(way).canSkip()); + + way.setTag("highway", "track"); + assertTrue(parser.getAccess(way).isWay()); + + // this is fully okay as sometimes old rails are on the road + way.setTag("highway", "primary"); + way.setTag("railway", "historic"); + assertTrue(parser.getAccess(way).isWay()); + + way.setTag("motorcar", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way = new ReaderWay(1); + way.setTag("highway", "secondary"); + way.setTag("railway", "tram"); + // but allow tram to be on the same way + assertTrue(parser.getAccess(way).isWay()); + } + + @Test + public void testFerry() { + ReaderWay way = new ReaderWay(1); + way.setTag("route", "shuttle_train"); + way.setTag("motorcar", "yes"); + way.setTag("bicycle", "no"); + // Provide the duration value in seconds: + way.setTag("way_distance", 50000.0); + way.setTag("speed_from_duration", 50 / (35.0 / 60)); + way.setTag("duration:seconds", 35L * 60); + // accept + assertTrue(parser.getAccess(way).isFerry()); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + // calculate speed from tags: speed_from_duration * 1.4 (+ rounded using the speed factor) + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(60, speedParser.getAverageSpeedEnc().getDecimal(false, edgeId, edgeIntAccess)); + + //Test for very short and slow 0.5km/h still realistic ferry + way = new ReaderWay(1); + way.setTag("route", "ferry"); + way.setTag("motorcar", "yes"); + // Provide the duration of 12 minutes in seconds: + way.setTag("duration:seconds", 12L * 60); + way.setTag("way_distance", 100.0); + way.setTag("speed_from_duration", 0.1 / (12.0 / 60)); + // accept + assertTrue(parser.getAccess(way).isFerry()); + // We can't store 0.5km/h, but we expect the lowest possible speed (5km/h) + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(5, speedParser.getAverageSpeedEnc().getDecimal(false, edgeId, edgeIntAccess)); + + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + avSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 2.5); + assertEquals(5, avSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + //Test for missing duration + way = new ReaderWay(1); + way.setTag("route", "ferry"); + way.setTag("motorcar", "yes"); + way.setTag("edge_distance", 100.0); + // accept + assertTrue(parser.getAccess(way).isFerry()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + // We use the unknown speed + assertEquals(5, speedParser.getAverageSpeedEnc().getDecimal(false, edgeId, edgeIntAccess)); + + way.clearTags(); + way.setTag("route", "ferry"); + assertTrue(parser.getAccess(way).isFerry()); + way.setTag("motorcar", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("foot", "yes"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("foot", "designated"); + way.setTag("motor_vehicle", "designated"); + assertTrue(parser.getAccess(way).isFerry()); + + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("access", "no"); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("vehicle", "yes"); + assertTrue(parser.getAccess(way).isFerry()); + } + + @Test + public void testBarrierAccess() { + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "lift_gate"); + node.setTag("access", "yes"); + // no barrier! + assertFalse(parser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "lift_gate"); + node.setTag("bicycle", "yes"); + // no barrier! + assertFalse(parser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "lift_gate"); + node.setTag("access", "yes"); + node.setTag("bicycle", "yes"); + // should this be a barrier for motorcars too? + // assertTrue(encoder.handleNodeTags(node) == true); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "lift_gate"); + node.setTag("access", "no"); + node.setTag("motorcar", "yes"); + // no barrier! + assertFalse(parser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "bollard"); + // barrier! + assertTrue(parser.isBarrier(node)); + + // Test if cattle_grid is not blocking + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "cattle_grid"); + assertFalse(parser.isBarrier(node)); + } + + @Test + public void testChainBarrier() { + // by default allow access through the gate for bike & foot! + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "chain"); + assertFalse(parser.isBarrier(node)); + node.setTag("motor_vehicle", "no"); + assertTrue(parser.isBarrier(node)); + node.setTag("motor_vehicle", "yes"); + assertFalse(parser.isBarrier(node)); + } + + @Test + public void testMaxValue() { + DecimalEncodedValueImpl smallFactorSpeedEnc = new DecimalEncodedValueImpl("car_average_speed", 10, 0.5, true); + EncodingManager em = new EncodingManager.Builder() + .add(new SimpleBooleanEncodedValue("car_access", true)) + .add(smallFactorSpeedEnc) + .addTurnCostEncodedValue(TurnCost.create("car", 1)) + .build(); + CarAverageSpeedParser speedParser = new CarAverageSpeedParser(em, new PMap()); + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "motorway_link"); + way.setTag("maxspeed", "60 mph"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + + // double speed = AbstractFlagEncoder.parseSpeed("60 mph"); + // => 96.56 * 0.9 => 86.9 + assertEquals(86.9, smallFactorSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + assertEquals(86.9, smallFactorSpeedEnc.getDecimal(true, edgeId, edgeIntAccess), 1e-1); + + // test that maxPossibleValue is not exceeded + way = new ReaderWay(2); + way.setTag("highway", "motorway_link"); + way.setTag("maxspeed", "70 mph"); + edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(101.5, smallFactorSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + } + + @Test + public void testSetToMaxSpeed() { + ReaderWay way = new ReaderWay(12); + way.setTag("maxspeed", "90"); + assertEquals(90, AbstractAverageSpeedParser.getMaxSpeed(way, false), 1e-2); + + way = new ReaderWay(12); + way.setTag("maxspeed", "90"); + way.setTag("maxspeed:backward", "50"); + assertEquals(90, AbstractAverageSpeedParser.getMaxSpeed(way, false), 1e-2); + assertEquals(50, AbstractAverageSpeedParser.getMaxSpeed(way, true), 1e-2); + } + + @Test + public void testCombination() { + ReaderWay way = new ReaderWay(123); + way.setTag("highway", "cycleway"); + way.setTag("sac_scale", "hiking"); + + BikeAccessParser bikeParser = new BikeAccessParser(em, new PMap()); + bikeParser.init(new DateRangeParser()); + assertEquals(WayAccess.CAN_SKIP, parser.getAccess(way)); + assertNotEquals(WayAccess.CAN_SKIP, bikeParser.getAccess(way)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, way); + bikeParser.handleWayTags(edgeId, edgeIntAccess, way); + assertFalse(accessEnc.getBool(true, edgeId, edgeIntAccess)); + assertFalse(accessEnc.getBool(false, edgeId, edgeIntAccess)); + BooleanEncodedValue bikeAccessEnc = bikeParser.getAccessEnc(); + assertTrue(bikeAccessEnc.getBool(true, edgeId, edgeIntAccess)); + assertTrue(bikeAccessEnc.getBool(false, edgeId, edgeIntAccess)); + } + + @Test + public void testApplyBadSurfaceSpeed() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "secondary"); + way.setTag("surface", "unpaved"); + assertEquals(30, speedParser.applyBadSurfaceSpeed(way, 90), 1e-1); + } + + @Test + public void testIssue_1256() { + ReaderWay way = new ReaderWay(1); + way.setTag("route", "ferry"); + way.setTag("edge_distance", 257.0); + + // default is 5km/h minimum speed for car + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(5, speedParser.getAverageSpeedEnc().getDecimal(false, edgeId, edgeIntAccess), .1); + + // for a smaller speed factor the minimum speed is also smaller + DecimalEncodedValueImpl lowFactorSpeedEnc = new DecimalEncodedValueImpl(VehicleSpeed.key("car"), 10, 1, false); + EncodingManager lowFactorEm = new EncodingManager.Builder() + .add(new SimpleBooleanEncodedValue(VehicleAccess.key("car"), true)) + .add(lowFactorSpeedEnc) + .addTurnCostEncodedValue(TurnCost.create(TurnCost.key("car"), 1)) + .build(); + edgeIntAccess = new ArrayEdgeIntAccess(lowFactorEm.getIntsForFlags()); + new CarAverageSpeedParser(lowFactorEm, new PMap()).handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(1, lowFactorSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + } + + @ParameterizedTest + @ValueSource(strings = {"mofa", "moped", "motorcar", "motor_vehicle", "motorcycle"}) + void footway_etc_not_allowed_despite_vehicle_yes(String vehicle) { + // these highways are blocked, even when we set one of the vehicles to yes + for (String highway : Arrays.asList("footway", "cycleway", "steps", "pedestrian")) { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", highway); + way.setTag(vehicle, "yes"); + assertEquals(WayAccess.CAN_SKIP, parser.getAccess(way)); + } + } +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java new file mode 100644 index 00000000000..83dd1769706 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/FootTagParserTest.java @@ -0,0 +1,560 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderNode; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.*; +import org.junit.jupiter.api.Test; + +import java.text.DateFormat; +import java.util.*; + +import static com.graphhopper.routing.util.parsers.FootAverageSpeedParser.MEAN_SPEED; +import static com.graphhopper.routing.util.parsers.FootAverageSpeedParser.SLOW_SPEED; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Peter Karich + */ +public class FootTagParserTest { + private final BooleanEncodedValue footAccessEnc = VehicleAccess.create("foot"); + private final DecimalEncodedValue footAvgSpeedEnc = VehicleSpeed.create("foot", 4, 1, false); + private final DecimalEncodedValue footPriorityEnc = VehiclePriority.create("foot", 4, PriorityCode.getFactor(1), false); + private final BooleanEncodedValue bikeAccessEnc = VehicleAccess.create("bike"); + private final DecimalEncodedValue bikeAvgSpeedEnc = VehicleSpeed.create("bike", 4, 2, false); + private final BooleanEncodedValue carAccessEnc = VehicleAccess.create("car"); + private final DecimalEncodedValue carAvSpeedEnc = VehicleSpeed.create("car", 5, 5, false); + private final EncodingManager encodingManager = EncodingManager.start() + .add(footAccessEnc).add(footAvgSpeedEnc).add(footPriorityEnc).add(RouteNetwork.create(FootNetwork.KEY)) + .add(bikeAccessEnc).add(bikeAvgSpeedEnc).add(RouteNetwork.create(BikeNetwork.KEY)) + .add(carAccessEnc).add(carAvSpeedEnc) + .build(); + private final FootAccessParser accessParser = new FootAccessParser(encodingManager, new PMap()); + private final FootAverageSpeedParser speedParser = new FootAverageSpeedParser(encodingManager, new PMap()); + private final FootPriorityParser prioParser = new FootPriorityParser(encodingManager, new PMap()); + + public FootTagParserTest() { + accessParser.init(new DateRangeParser()); + } + + @Test + public void testGetSpeed() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + footAccessEnc.setBool(false, edgeId, edgeIntAccess, true); + footAccessEnc.setBool(true, edgeId, edgeIntAccess, true); + footAvgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 10); + assertEquals(10, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + } + + @Test + public void testSteps() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "service"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(MEAN_SPEED, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.setTag("highway", "steps"); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(MEAN_SPEED > footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess)); + } + + @Test + public void testCombined() { + BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + EdgeIteratorState edge = g.edge(0, 1); + edge.set(footAvgSpeedEnc, 10.0).set(footAccessEnc, true, true); + edge.set(carAvSpeedEnc, 100.0).set(carAccessEnc, true, false); + + assertEquals(10, edge.get(footAvgSpeedEnc), 1e-1); + assertTrue(edge.get(footAccessEnc)); + assertTrue(edge.getReverse(footAccessEnc)); + + assertEquals(100, edge.get(carAvSpeedEnc), 1e-1); + assertTrue(edge.get(carAccessEnc)); + assertFalse(edge.getReverse(carAccessEnc)); + + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + footAvgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 10); + footAccessEnc.setBool(false, edgeId, edgeIntAccess, true); + footAccessEnc.setBool(true, edgeId, edgeIntAccess, true); + assertEquals(0, carAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + } + + @Test + public void testGraph() { + BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + g.edge(0, 1).setDistance(10).set(footAvgSpeedEnc, 10.0).set(footAccessEnc, true, true); + g.edge(0, 2).setDistance(10).set(footAvgSpeedEnc, 5.0).set(footAccessEnc, true, true); + g.edge(1, 3).setDistance(10).set(footAvgSpeedEnc, 10.0).set(footAccessEnc, true, true); + EdgeExplorer out = g.createEdgeExplorer(AccessFilter.outEdges(footAccessEnc)); + assertEquals(GHUtility.asSet(1, 2), GHUtility.getNeighbors(out.setBaseNode(0))); + assertEquals(GHUtility.asSet(0, 3), GHUtility.getNeighbors(out.setBaseNode(1))); + assertEquals(GHUtility.asSet(0), GHUtility.getNeighbors(out.setBaseNode(2))); + } + + @Test + public void testAccess() { + ReaderWay way = new ReaderWay(1); + + way.setTag("highway", "motorway"); + way.setTag("sidewalk", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("sidewalk", "left"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("sidewalk", "none"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("sidewalk", "left"); + way.setTag("access", "private"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.clearTags(); + + way.setTag("highway", "pedestrian"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "footway"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "platform"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "motorway"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("highway", "path"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("bicycle", "official"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("foot", "official"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("foot", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("vehicle", "no"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("motorroad", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "cycleway"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("access", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("foot", "yes"); + way.setTag("access", "no"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("route", "ferry"); + assertTrue(accessParser.getAccess(way).isFerry()); + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + // #1562, test if ferry route with foot + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("foot", "yes"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "designated"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "official"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "permissive"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("foot", "designated"); + way.setTag("access", "private"); + assertTrue(accessParser.getAccess(way).canSkip()); + + DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); + + way.clearTags(); + way.setTag("highway", "footway"); + way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("foot", "yes"); // the conditional tag even overrules "yes" + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "footway"); + way.setTag("access", "no"); + way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(accessParser.getAccess(way).isWay()); + } + + @Test + public void testRailPlatformIssue366() { + ReaderWay way = new ReaderWay(1); + way.setTag("railway", "platform"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertEquals(5, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess)); + + way.clearTags(); + way.setTag("highway", "track"); + way.setTag("railway", "platform"); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertEquals(5, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess)); + + way.clearTags(); + // only tram, no highway => no access + way.setTag("railway", "tram"); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertFalse(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertEquals(0, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess)); + } + + @Test + public void testPier() { + ReaderWay way = new ReaderWay(1); + way.setTag("man_made", "pier"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertEquals(5, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess)); + } + + @Test + public void testOneway() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "path"); + way.setTag("foot:forward", "yes"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(footAccessEnc.getBool(true, edgeId, edgeIntAccess)); + + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + way.clearTags(); + way.setTag("highway", "path"); + way.setTag("foot:backward", "yes"); + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + assertFalse(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(footAccessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @Test + public void testFerrySpeed() { + ReaderWay way = new ReaderWay(1); + way.setTag("route", "ferry"); + way.setTag("duration:seconds", 1800L); + way.setTag("edge_distance", 30000.0); + way.setTag("speed_from_duration", 30 / 0.5); + // the speed is truncated to maxspeed (=15) + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(15, speedParser.getAverageSpeedEnc().getDecimal(false, edgeId, edgeIntAccess)); + } + + @Test + public void testMixSpeedAndSafe() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "motorway"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + assertFalse(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(footAccessEnc.getBool(true, edgeId, edgeIntAccess)); + assertEquals(0, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess)); + + way.setTag("sidewalk", "yes"); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(footAccessEnc.getBool(true, edgeId, edgeIntAccess)); + assertEquals(5, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.clearTags(); + way.setTag("highway", "track"); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertTrue(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(footAccessEnc.getBool(true, edgeId, edgeIntAccess)); + assertEquals(5, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + } + + @Test + public void testPriority() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "cycleway"); + assertEquals(PriorityCode.UNCHANGED.getValue(), prioParser.handlePriority(way, null)); + + way.setTag("highway", "primary"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.setTag("highway", "track"); + way.setTag("bicycle", "official"); + assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.setTag("highway", "track"); + way.setTag("bicycle", "designated"); + assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.setTag("highway", "cycleway"); + way.setTag("bicycle", "designated"); + way.setTag("foot", "designated"); + assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "primary"); + way.setTag("sidewalk", "yes"); + assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "cycleway"); + way.setTag("sidewalk", "no"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("bicycle", "official"); + way.setTag("sidewalk", "no"); + assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "trunk"); + way.setTag("sidewalk", "no"); + assertEquals(PriorityCode.VERY_BAD.getValue(), prioParser.handlePriority(way, null)); + way.setTag("sidewalk", "none"); + assertEquals(PriorityCode.VERY_BAD.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "residential"); + way.setTag("sidewalk", "yes"); + assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); + } + + @Test + public void testSlowHiking() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "track"); + way.setTag("sac_scale", "hiking"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(MEAN_SPEED, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + + way.setTag("highway", "track"); + way.setTag("sac_scale", "mountain_hiking"); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + speedParser.handleWayTags(edgeId, edgeIntAccess, way); + assertEquals(SLOW_SPEED, footAvgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), 1e-1); + } + + @Test + public void testReadBarrierNodesFromWay() { + int edgeId = 0; + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "secondary"); + way.setTag("gh:barrier_edge", true); + + Map tags = new HashMap<>(); + tags.put("barrier", "gate"); + tags.put("access", "no"); + way.setTag("node_tags", Collections.singletonList(tags)); + accessParser.handleWayTags(edgeId, edgeIntAccess, way); + + assertFalse(footAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(footAccessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @Test + public void testBarrierAccess() { + // by default allow access through the gate for bike & foot! + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "yes"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "no"); + // barrier! + assertTrue(accessParser.isBarrier(node)); + + node.setTag("bicycle", "yes"); + // no barrier!? + // assertTrue(footEncoder.handleNodeTags(node) == false); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "no"); + node.setTag("foot", "yes"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + + node.setTag("locked", "yes"); + // barrier! + assertTrue(accessParser.isBarrier(node)); + + node.clearTags(); + node.setTag("barrier", "yes"); + node.setTag("access", "no"); + assertTrue(accessParser.isBarrier(node)); + } + + @Test + public void testChainBarrier() { + // by default allow access through the gate for bike & foot! + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "chain"); + assertFalse(accessParser.isBarrier(node)); + node.setTag("foot", "no"); + assertTrue(accessParser.isBarrier(node)); + } + + @Test + public void testFord() { + // by default do not block access due to fords! + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("ford", "no"); + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("ford", "yes"); + assertFalse(accessParser.isBarrier(node)); + + // barrier! + node.setTag("foot", "no"); + assertTrue(accessParser.isBarrier(node)); + + FootAccessParser blockFordsParser = new FootAccessParser(encodingManager, new PMap("block_fords=true")); + node = new ReaderNode(1, -1, -1); + node.setTag("ford", "no"); + assertFalse(blockFordsParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("ford", "yes"); + assertTrue(blockFordsParser.isBarrier(node)); + } + + @Test + public void testBlockByDefault() { + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + // potential barriers are no barrier by default + assertFalse(accessParser.isBarrier(node)); + node.setTag("access", "no"); + assertTrue(accessParser.isBarrier(node)); + + // absolute barriers always block + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "fence"); + assertTrue(accessParser.isBarrier(node)); + node.setTag("barrier", "fence"); + node.setTag("access", "yes"); + assertFalse(accessParser.isBarrier(node)); + + // pass potential barriers per default (if no other access tag exists) + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + assertFalse(accessParser.isBarrier(node)); + node.setTag("access", "yes"); + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "fence"); + assertTrue(accessParser.isBarrier(node)); + + // don't block potential barriers: barrier:cattle_grid should not block here + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "cattle_grid"); + assertFalse(accessParser.isBarrier(node)); + } + + @Test + public void maxSpeed() { + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("foot_speed", 4, 2, true); + // The foot max speed is supposed to be 15km/h, but for speed_bits=4,speed_factor=2 as we use here 15 cannot + // be stored. In fact, when we set the speed of an edge to 15 and call the getter afterwards we get a value of 16 + // because of the internal (scaled) integer representation: + EncodingManager em = EncodingManager.start().add(speedEnc).build(); + BaseGraph graph = new BaseGraph.Builder(em).create(); + EdgeIteratorState edge = graph.edge(0, 1).setDistance(100).set(speedEnc, 15); + assertEquals(16, edge.get(speedEnc)); + + // ... because of this we have to make sure the max speed is set to a value that cannot be exceeded even when + // such conversion occurs. in our case it must be 16 not 15! + // note that this test made more sense when we used encoders that defined a max speed. + assertEquals(16, speedEnc.getNextStorableValue(15)); + } +} diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/MotorcycleTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/MotorcycleTagParserTest.java new file mode 100644 index 00000000000..0d996ae95ed --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/MotorcycleTagParserTest.java @@ -0,0 +1,184 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.routing.util.WayAccess; +import com.graphhopper.util.Helper; +import com.graphhopper.util.PMap; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.text.DateFormat; +import java.util.Arrays; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Peter Karich + */ +public class MotorcycleTagParserTest { + private final BooleanEncodedValue motorcycleAccessEnc = VehicleAccess.create("motorcycle"); + private final DecimalEncodedValue motorcycleSpeedEnc = VehicleSpeed.create("motorcycle", 5, 5, true); + private final DecimalEncodedValue motorcyclePriorityEnc = VehiclePriority.create("motorcycle", 4, PriorityCode.getFactor(1), false); + private final DecimalEncodedValue motorcycleCurvatureEnc = new DecimalEncodedValueImpl("motorcycle_curvature", 4, 0.1, false); + private final BooleanEncodedValue footAccessEnc = VehicleAccess.create("foot"); + private final DecimalEncodedValue footSpeedEnc = VehicleSpeed.create("foot", 4, 1, false); + private final EncodingManager em = EncodingManager.start() + .add(motorcycleAccessEnc).add(motorcycleSpeedEnc).add(motorcyclePriorityEnc).add(motorcycleCurvatureEnc) + .add(footAccessEnc).add(footSpeedEnc) + .build(); + private final MotorcycleAccessParser parser; + private final MotorcycleAverageSpeedParser speedParser; + + public MotorcycleTagParserTest() { + parser = new MotorcycleAccessParser(em, new PMap()); + parser.init(new DateRangeParser()); + speedParser = new MotorcycleAverageSpeedParser(em, new PMap()); + } + + @Test + public void testAccess() { + ReaderWay way = new ReaderWay(1); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("highway", "service"); + assertTrue(parser.getAccess(way).isWay()); + way.setTag("access", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "track"); + assertTrue(parser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "delivery"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "unclassified"); + way.setTag("ford", "yes"); + assertTrue(parser.getAccess(way).isWay()); + way.setTag("motorcycle", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("route", "ferry"); + assertTrue(parser.getAccess(way).isFerry()); + way.setTag("motorcycle", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("foot", "yes"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("access", "yes"); + way.setTag("motor_vehicle", "no"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "yes"); + way.setTag("motor_vehicle", "no"); + assertTrue(parser.getAccess(way).canSkip()); + way.setTag("motor_vehicle", "agricultural;forestry"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "emergency"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("motor_vehicle", "emergency"); + assertTrue(parser.getAccess(way).canSkip()); + + DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(parser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("access", "no"); + way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(parser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("service", "emergency_access"); + assertTrue(parser.getAccess(way).canSkip()); + } + + @Test + public void testHandleWayTags() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "service"); + assertTrue(parser.getAccess(way).isWay()); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + speedParser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertEquals(20, speedParser.avgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + assertEquals(20, speedParser.avgSpeedEnc.getDecimal(true, edgeId, edgeIntAccess), .1); + } + + @Test + public void testSetSpeed0_issue367() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + motorcycleAccessEnc.setBool(false, edgeId, edgeIntAccess, true); + motorcycleAccessEnc.setBool(true, edgeId, edgeIntAccess, true); + speedParser.getAverageSpeedEnc().setDecimal(false, edgeId, edgeIntAccess, 10); + speedParser.getAverageSpeedEnc().setDecimal(true, edgeId, edgeIntAccess, 10); + + assertEquals(10, speedParser.getAverageSpeedEnc().getDecimal(false, edgeId, edgeIntAccess), .1); + assertEquals(10, speedParser.getAverageSpeedEnc().getDecimal(true, edgeId, edgeIntAccess), .1); + + speedParser.setSpeed(false, edgeId, edgeIntAccess, 0); + assertEquals(0, speedParser.avgSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + assertEquals(10, speedParser.avgSpeedEnc.getDecimal(true, edgeId, edgeIntAccess), .1); + + // speed and access are independent + assertTrue(motorcycleAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(motorcycleAccessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @ParameterizedTest + @ValueSource(strings = {"mofa", "moped", "motorcar", "motor_vehicle", "motorcycle"}) + void footway_etc_not_allowed_despite_vehicle_yes(String vehicle) { + // these highways are blocked, even when we set one of the vehicles to yes + for (String highway : Arrays.asList("footway", "cycleway", "steps", "pedestrian")) { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", highway); + way.setTag(vehicle, "yes"); + assertEquals(WayAccess.CAN_SKIP, parser.getAccess(way)); + } + } +} diff --git a/core/src/test/java/com/graphhopper/routing/util/MountainBikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java similarity index 78% rename from core/src/test/java/com/graphhopper/routing/util/MountainBikeTagParserTest.java rename to core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java index b5e532ced61..f5507af61ad 100644 --- a/core/src/test/java/com/graphhopper/routing/util/MountainBikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/MountainBikeTagParserTest.java @@ -15,22 +15,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.graphhopper.routing.util; +package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderNode; import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.routing.util.VehicleEncodedValues; +import com.graphhopper.routing.util.VehicleTagParsers; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; -import static com.graphhopper.routing.util.BikeCommonTagParser.PUSHING_SECTION_SPEED; import static com.graphhopper.routing.util.PriorityCode.*; +import static com.graphhopper.routing.util.parsers.BikeCommonAverageSpeedParser.MIN_SPEED; +import static com.graphhopper.routing.util.parsers.BikeCommonAverageSpeedParser.PUSHING_SECTION_SPEED; import static org.junit.jupiter.api.Assertions.*; public class MountainBikeTagParserTest extends AbstractBikeTagParserTester { @Override - protected BikeCommonTagParser createBikeTagParser() { - return new MountainBikeTagParser(new PMap("block_fords=true")); + protected EncodingManager createEncodingManager() { + return new EncodingManager.Builder().add(VehicleEncodedValues.mountainbike(new PMap())).build(); + } + + @Override + protected VehicleTagParsers createBikeTagParsers(EncodedValueLookup lookup, PMap pMap) { + return VehicleTagParsers.mtb(lookup, pMap); } @Test @@ -44,13 +55,13 @@ public void testSpeedAndPriority() { // Test pushing section speeds way.setTag("highway", "footway"); - assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), 4, way); + assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), PUSHING_SECTION_SPEED, way); way.setTag("highway", "track"); assertPriorityAndSpeed(PREFER.getValue(), 18, way); way.setTag("highway", "steps"); - assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), 4, way); + assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), PUSHING_SECTION_SPEED, way); way.clearTags(); // test speed for allowed pushing section types @@ -83,7 +94,7 @@ public void testSmoothness() { assertEquals(12, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "impassable"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(MIN_SPEED, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "unknown"); assertEquals(12, getSpeedFromFlags(way), 0.01); @@ -102,10 +113,10 @@ public void testSmoothness() { assertEquals(6, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "bad"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(4, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "impassable"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(MIN_SPEED, getSpeedFromFlags(way), 0.01); } @Test @@ -114,17 +125,17 @@ public void testSacScale() { ReaderWay way = new ReaderWay(1); way.setTag("highway", "service"); way.setTag("sac_scale", "hiking"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.setTag("highway", "service"); way.setTag("sac_scale", "mountain_hiking"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.setTag("sac_scale", "alpine_hiking"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); way.setTag("sac_scale", "demanding_alpine_hiking"); - assertTrue(parser.getAccess(way).canSkip()); + assertTrue(accessParser.getAccess(way).canSkip()); } @Test @@ -167,21 +178,21 @@ public void testBarrierAccess() { ReaderNode node = new ReaderNode(1, -1, -1); node.setTag("barrier", "kissing_gate"); // No barrier! - assertFalse(parser.isBarrier(node)); + assertFalse(accessParser.isBarrier(node)); // kissing_gate with bicycle tag = no node = new ReaderNode(1, -1, -1); node.setTag("barrier", "kissing_gate"); node.setTag("bicycle", "no"); // barrier! - assertTrue(parser.isBarrier(node)); + assertTrue(accessParser.isBarrier(node)); // kissing_gate with bicycle tag node = new ReaderNode(1, -1, -1); node.setTag("barrier", "kissing_gate"); node.setTag("bicycle", "yes"); // No barrier! - assertFalse(parser.isBarrier(node)); + assertFalse(accessParser.isBarrier(node)); } } diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMCrossingParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMCrossingParserTest.java new file mode 100644 index 00000000000..3875dea5eb1 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMCrossingParserTest.java @@ -0,0 +1,87 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.*; +import com.graphhopper.util.PMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class OSMCrossingParserTest { + + private OSMCrossingParser parser; + private EnumEncodedValue crossingEV; + + @BeforeEach + public void setup() { + crossingEV = new EnumEncodedValue<>(Crossing.KEY, Crossing.class); + crossingEV.init(new EncodedValue.InitializerConfig()); + parser = new OSMCrossingParser(crossingEV); + } + + @Test + public void testRailway() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, + createReader(new PMap().putObject("railway", "level_crossing").toMap()), null); + assertEquals(Crossing.RAILWAY, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + } + + @Test + public void testSignals() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, + createReader(new PMap().putObject("crossing", "traffic_signals").toMap()), null); + assertEquals(Crossing.TRAFFIC_SIGNALS, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + + parser.handleWayTags(edgeId, edgeIntAccess = new ArrayEdgeIntAccess(1), + createReader(new PMap().putObject("crossing:signals", "yes").toMap()), null); + assertEquals(Crossing.TRAFFIC_SIGNALS, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + + parser.handleWayTags(edgeId, edgeIntAccess = new ArrayEdgeIntAccess(1), + createReader(new PMap().putObject("crossing:signals", "no").toMap()), null); + assertEquals(Crossing.UNMARKED, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + } + + @Test + public void testMarked() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, createReader(new HashMap<>()), null); + assertEquals(Crossing.MISSING, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + + parser.handleWayTags(edgeId, edgeIntAccess = new ArrayEdgeIntAccess(1), + createReader(new PMap().putObject("highway", "crossing").toMap()), null); + assertEquals(Crossing.UNMARKED, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + + parser.handleWayTags(edgeId, edgeIntAccess = new ArrayEdgeIntAccess(1), + createReader(new PMap().putObject("crossing", "marked").toMap()), null); + assertEquals(Crossing.MARKED, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + + parser.handleWayTags(edgeId, edgeIntAccess = new ArrayEdgeIntAccess(1), + createReader(new PMap().putObject("crossing:markings", "yes").toMap()), null); + assertEquals(Crossing.MARKED, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + + parser.handleWayTags(edgeId, edgeIntAccess = new ArrayEdgeIntAccess(1), + createReader(new PMap().putObject("crossing:markings", "no").toMap()), null); + assertEquals(Crossing.UNMARKED, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + + parser.handleWayTags(edgeId, edgeIntAccess = new ArrayEdgeIntAccess(1), + createReader(new PMap().putObject("crossing:signals", "no").putObject("crossing:markings", "yes").toMap()), null); + assertEquals(Crossing.MARKED, crossingEV.getEnum(false, edgeId, edgeIntAccess)); + } + + ReaderWay createReader(Map map) { + ReaderWay way = new ReaderWay(1); + way.setTag("node_tags", Collections.singletonList(map)); + return way; + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParserTest.java index b55aec28e89..1305e455771 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMGetOffBikeParserTest.java @@ -1,10 +1,15 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.ArrayEdgeIntAccess; import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.ev.EncodedValue; +import com.graphhopper.routing.ev.EdgeIntAccess; import com.graphhopper.routing.ev.GetOffBike; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.VehicleEncodedValues; import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -12,10 +17,14 @@ public class OSMGetOffBikeParserTest { private final BooleanEncodedValue offBikeEnc = GetOffBike.create(); - private final OSMGetOffBikeParser parser = new OSMGetOffBikeParser(offBikeEnc); + private final BikeAccessParser accessParser; + private final OSMGetOffBikeParser getOffParser; public OSMGetOffBikeParserTest() { - offBikeEnc.init(new EncodedValue.InitializerConfig()); + EncodingManager em = new EncodingManager.Builder().add(offBikeEnc).add(VehicleEncodedValues.bike(new PMap()).getAccessEnc()).build(); + accessParser = new BikeAccessParser(em, new PMap()); + accessParser.init(new DateRangeParser()); + getOffParser = new OSMGetOffBikeParser(offBikeEnc, accessParser.getAccessEnc()); } @Test @@ -98,10 +107,28 @@ public void testHandleCommonWayTags() { assertTrue(isGetOffBike(way)); } + @Test + public void testOneway() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("oneway", "yes"); + + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + IntsRef rel = new IntsRef(1); + accessParser.handleWayTags(edgeId, edgeIntAccess, way, new IntsRef(1)); + getOffParser.handleWayTags(edgeId, edgeIntAccess, way, new IntsRef(1)); + + assertFalse(offBikeEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(offBikeEnc.getBool(true, edgeId, edgeIntAccess)); + } + private boolean isGetOffBike(ReaderWay way) { - IntsRef edgeFlags = new IntsRef(1); - IntsRef relationFlags = new IntsRef(1); - parser.handleWayTags(edgeFlags, way, relationFlags); - return offBikeEnc.getBool(false, edgeFlags); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + IntsRef rel = new IntsRef(1); + accessParser.handleWayTags(edgeId, edgeIntAccess, way, rel); + getOffParser.handleWayTags(edgeId, edgeIntAccess, way, rel); + return offBikeEnc.getBool(false, edgeId, edgeIntAccess); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatParserTest.java index bf2030e70d4..93ced81a480 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.Hazmat; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -11,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class OSMHazmatParserTest { - private final EnumEncodedValue hazEnc = new EnumEncodedValue<>(Hazmat.KEY, Hazmat.class); + private final EnumEncodedValue hazEnc = Hazmat.create(); private OSMHazmatParser parser; private IntsRef relFlags; @@ -25,32 +23,34 @@ public void setUp() { @Test public void testSimpleTags() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("hazmat", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Hazmat.NO, hazEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Hazmat.NO, hazEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("hazmat", "yes"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Hazmat.YES, hazEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Hazmat.YES, hazEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("hazmat", "designated"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Hazmat.YES, hazEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Hazmat.YES, hazEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("hazmat", "designated"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Hazmat.YES, hazEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Hazmat.YES, hazEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testNoNPE() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Hazmat.YES, hazEnc.getEnum(false, intsRef)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Hazmat.YES, hazEnc.getEnum(false, edgeId, edgeIntAccess)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatTunnelParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatTunnelParserTest.java index 36de3d5f769..8f31f06c7ed 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatTunnelParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatTunnelParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.HazmatTunnel; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,7 +15,7 @@ public class OSMHazmatTunnelParserTest { @BeforeEach public void setUp() { - hazTunnelEnc = new EnumEncodedValue<>(HazmatTunnel.KEY, HazmatTunnel.class); + hazTunnelEnc = HazmatTunnel.create(); hazTunnelEnc.init(new EncodedValue.InitializerConfig()); parser = new OSMHazmatTunnelParser(hazTunnelEnc); relFlags = new IntsRef(2); @@ -25,111 +23,114 @@ public void setUp() { @Test public void testADRTunnelCat() { - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; ReaderWay readerWay = new ReaderWay(1); readerWay.setTag("hazmat:adr_tunnel_cat", "A"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("hazmat:adr_tunnel_cat", "B"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.B, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.B, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("hazmat:adr_tunnel_cat", "C"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.C, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.C, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("hazmat:adr_tunnel_cat", "D"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.D, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.D, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("hazmat:adr_tunnel_cat", "E"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testTunnelCat() { - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; ReaderWay readerWay = new ReaderWay(1); readerWay.setTag("hazmat:tunnel_cat", "A"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("hazmat:tunnel_cat", "B"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.B, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.B, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("hazmat:tunnel_cat", "C"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.C, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.C, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("hazmat:tunnel_cat", "D"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.D, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.D, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("hazmat:tunnel_cat", "E"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testHazmatSubtags() { - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; ReaderWay readerWay = new ReaderWay(1); readerWay.setTag("tunnel", "yes"); readerWay.setTag("hazmat:A", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("tunnel", "yes"); readerWay.setTag("hazmat:B", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.B, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.B, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("tunnel", "yes"); readerWay.setTag("hazmat:C", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.C, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.C, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("tunnel", "yes"); readerWay.setTag("hazmat:D", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.D, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.D, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("tunnel", "yes"); readerWay.setTag("hazmat:E", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testOrder() { - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); ReaderWay readerWay = new ReaderWay(1); readerWay.setTag("tunnel", "yes"); readerWay.setTag("hazmat:A", "no"); @@ -137,41 +138,44 @@ public void testOrder() { readerWay.setTag("hazmat:C", "no"); readerWay.setTag("hazmat:D", "no"); readerWay.setTag("hazmat:E", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, intsRef)); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("tunnel", "yes"); readerWay.setTag("hazmat:A", "no"); readerWay.setTag("hazmat:B", "no"); readerWay.setTag("hazmat:C", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.C, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.C, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay = new ReaderWay(1); readerWay.setTag("tunnel", "yes"); readerWay.setTag("hazmat:B", "no"); readerWay.setTag("hazmat:E", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.E, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testIgnoreNonTunnelSubtags() { - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); ReaderWay readerWay = new ReaderWay(1); readerWay.setTag("hazmat:B", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, intsRef)); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testNoNPE() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, intsRef)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatTunnel.A, hazTunnelEnc.getEnum(false, edgeId, edgeIntAccess)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatWaterParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatWaterParserTest.java index 90d4cbf98cb..18d4fef9c2a 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatWaterParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMHazmatWaterParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.HazmatWater; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,7 +15,7 @@ public class OSMHazmatWaterParserTest { @BeforeEach public void setUp() { - hazWaterEnc = new EnumEncodedValue<>(HazmatWater.KEY, HazmatWater.class); + hazWaterEnc = HazmatWater.create(); hazWaterEnc.init(new EncodedValue.InitializerConfig()); parser = new OSMHazmatWaterParser(hazWaterEnc); relFlags = new IntsRef(2); @@ -26,27 +24,29 @@ public void setUp() { @Test public void testSimpleTags() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("hazmat:water", "no"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatWater.NO, hazWaterEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatWater.NO, hazWaterEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("hazmat:water", "yes"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatWater.YES, hazWaterEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatWater.YES, hazWaterEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("hazmat:water", "permissive"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatWater.PERMISSIVE, hazWaterEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatWater.PERMISSIVE, hazWaterEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testNoNPE() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(HazmatWater.YES, hazWaterEnc.getEnum(false, intsRef)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(HazmatWater.YES, hazWaterEnc.getEnum(false, edgeId, edgeIntAccess)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMLanesParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMLanesParserTest.java index 859001c21af..b0a5dc28790 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMLanesParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMLanesParserTest.java @@ -19,9 +19,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.IntEncodedValue; -import com.graphhopper.routing.ev.Lanes; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -42,18 +40,20 @@ void setup() { @Test void basic() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("lanes", "4"); - parser.handleWayTags(intsRef, readerWay, relFlags); - Assertions.assertEquals(4, lanesEnc.getInt(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + Assertions.assertEquals(4, lanesEnc.getInt(false, edgeId, edgeIntAccess)); } @Test void notTagged() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); - parser.handleWayTags(intsRef, readerWay, relFlags); - Assertions.assertEquals(1, lanesEnc.getInt(false, intsRef)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + Assertions.assertEquals(1, lanesEnc.getInt(false, edgeId, edgeIntAccess)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxAxleLoadParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxAxleLoadParserTest.java index c32fc2ff857..135d669403d 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxAxleLoadParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxAxleLoadParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.MaxAxleLoad; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,42 +24,45 @@ public void setUp() { @Test public void testSimpleTags() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("maxaxleload", "11.5"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(11.5, malEnc.getDecimal(false, intsRef), .01); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(11.5, malEnc.getDecimal(false, edgeId, edgeIntAccess), .01); // if value is beyond the maximum then do not use infinity instead fallback to more restrictive maximum - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("maxaxleload", "80"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(malEnc.getMaxDecimal(), malEnc.getDecimal(false, intsRef), .01); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(63.0, malEnc.getDecimal(false, edgeId, edgeIntAccess), .01); } @Test public void testRounding() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("maxaxleload", "4.8"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(5.0, malEnc.getDecimal(false, intsRef), .01); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(5.0, malEnc.getDecimal(false, edgeId, edgeIntAccess), .01); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("maxaxleload", "3.6"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(3.5, malEnc.getDecimal(false, intsRef), .01); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(3.5, malEnc.getDecimal(false, edgeId, edgeIntAccess), .01); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("maxaxleload", "2.4"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(2.5, malEnc.getDecimal(false, intsRef), .01); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(2.5, malEnc.getDecimal(false, edgeId, edgeIntAccess), .01); } @Test public void testNoLimit() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Double.POSITIVE_INFINITY, malEnc.getDecimal(false, intsRef), .01); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Double.POSITIVE_INFINITY, malEnc.getDecimal(false, edgeId, edgeIntAccess), .01); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxSpeedParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxSpeedParserTest.java index 638241f145d..c04d9fcad43 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxSpeedParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxSpeedParserTest.java @@ -18,9 +18,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.MaxSpeed; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.TransportationMode; import com.graphhopper.routing.util.countryrules.CountryRule; import com.graphhopper.storage.IntsRef; @@ -44,15 +42,16 @@ public double getMaxSpeed(ReaderWay readerWay, TransportationMode transportation return 5; } }); - IntsRef edgeFlags = new IntsRef(1); - parser.handleWayTags(edgeFlags, way, relFlags); - assertEquals(5, maxSpeedEnc.getDecimal(false, edgeFlags), .1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, way, relFlags); + assertEquals(5, maxSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); // without a country_rule we get the default value - edgeFlags = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); way.removeTag("country_rule"); - parser.handleWayTags(edgeFlags, way, relFlags); - assertEquals(MaxSpeed.UNSET_SPEED, maxSpeedEnc.getDecimal(false, edgeFlags), .1); + parser.handleWayTags(edgeId, edgeIntAccess, way, relFlags); + assertEquals(MaxSpeed.UNSET_SPEED, maxSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxWeightParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxWeightParserTest.java index 92d2be6a918..62a0313dadf 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxWeightParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMaxWeightParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.MaxWeight; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,16 +24,17 @@ public void setUp() { @Test public void testSimpleTags() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("highway", "primary"); readerWay.setTag("maxweight", "5"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(5.0, mwEnc.getDecimal(false, intsRef), .01); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(5.0, mwEnc.getDecimal(false, edgeId, edgeIntAccess), .01); // if value is beyond the maximum then do not use infinity instead fallback to more restrictive maximum - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("maxweight", "50"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(mwEnc.getMaxDecimal(), mwEnc.getDecimal(false, intsRef), .01); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(25.4, mwEnc.getDecimal(false, edgeId, edgeIntAccess), .01); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMtbRatingParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMtbRatingParserTest.java index 28d7f2049f8..a09d5ea87db 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMtbRatingParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMMtbRatingParserTest.java @@ -19,9 +19,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.IntEncodedValue; -import com.graphhopper.routing.ev.MtbRating; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; @@ -59,12 +57,13 @@ private void checkRating(int expectedRating, String scaleString) { IntEncodedValue ev = MtbRating.create(); ev.init(new EncodedValue.InitializerConfig()); OSMMtbRatingParser parser = new OSMMtbRatingParser(ev); - IntsRef edgeFlags = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; ReaderWay way = new ReaderWay(0); if (scaleString != null) way.setTag("mtb:scale", scaleString); - parser.handleWayTags(edgeFlags, way, new IntsRef(2)); - assertEquals(expectedRating, ev.getInt(false, edgeFlags), "unexpected rating for mtb:scale=" + scaleString); + parser.handleWayTags(edgeId, edgeIntAccess, way, new IntsRef(2)); + assertEquals(expectedRating, ev.getInt(false, edgeId, edgeIntAccess), "unexpected rating for mtb:scale=" + scaleString); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParserTest.java index ca23d472d15..74aead04caf 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadAccessParserTest.java @@ -19,9 +19,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadAccess; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.TransportationMode; import com.graphhopper.routing.util.countryrules.CountryRule; import com.graphhopper.storage.IntsRef; @@ -33,7 +31,7 @@ class OSMRoadAccessParserTest { @Test void countryRule() { - EnumEncodedValue roadAccessEnc = new EnumEncodedValue<>(RoadAccess.KEY, RoadAccess.class); + EnumEncodedValue roadAccessEnc = RoadAccess.create(); roadAccessEnc.init(new EncodedValue.InitializerConfig()); OSMRoadAccessParser parser = new OSMRoadAccessParser(roadAccessEnc, OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR)); @@ -46,15 +44,25 @@ public RoadAccess getAccess(ReaderWay readerWay, TransportationMode transportati return RoadAccess.DESTINATION; } }); - IntsRef edgeFlags = new IntsRef(1); - parser.handleWayTags(edgeFlags, way, relFlags); - assertEquals(RoadAccess.DESTINATION, roadAccessEnc.getEnum(false, edgeFlags)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, way, relFlags); + assertEquals(RoadAccess.DESTINATION, roadAccessEnc.getEnum(false, edgeId, edgeIntAccess)); // if there is no country rule we get the default value - edgeFlags = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); way.removeTag("country_rule"); - parser.handleWayTags(edgeFlags, way, relFlags); - assertEquals(RoadAccess.YES, roadAccessEnc.getEnum(false, edgeFlags)); + parser.handleWayTags(edgeId, edgeIntAccess, way, relFlags); + assertEquals(RoadAccess.YES, roadAccessEnc.getEnum(false, edgeId, edgeIntAccess)); + + way.setTag("motor_vehicle", "agricultural;forestry"); + parser.handleWayTags(edgeId, edgeIntAccess, way, relFlags); + assertEquals(RoadAccess.AGRICULTURAL, roadAccessEnc.getEnum(false, edgeId, edgeIntAccess)); + + way.setTag("motor_vehicle", "forestry;agricultural"); + parser.handleWayTags(edgeId, edgeIntAccess, way, relFlags); + assertEquals(RoadAccess.AGRICULTURAL, roadAccessEnc.getEnum(false, edgeId, edgeIntAccess)); + } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadClassParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadClassParserTest.java index e106d3c98c0..db5ead2d4fc 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadClassParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadClassParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,7 +15,7 @@ public class OSMRoadClassParserTest { @BeforeEach public void setUp() { - rcEnc = new EnumEncodedValue<>(RoadClass.KEY, RoadClass.class); + rcEnc = RoadClass.create(); rcEnc.init(new EncodedValue.InitializerConfig()); parser = new OSMRoadClassParser(rcEnc); relFlags = new IntsRef(2); @@ -26,42 +24,45 @@ public void setUp() { @Test public void testSimpleTags() { ReaderWay readerWay = new ReaderWay(1); - IntsRef edgeFlags = new IntsRef(1); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("highway", "primary"); - parser.handleWayTags(edgeFlags, readerWay, relFlags); - assertEquals(RoadClass.PRIMARY, rcEnc.getEnum(false, edgeFlags)); + parser.handleWayTags(edgeId, intAccess, readerWay, relFlags); + assertEquals(RoadClass.PRIMARY, rcEnc.getEnum(false, edgeId, intAccess)); - edgeFlags = new IntsRef(1); + intAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("highway", "unknownstuff"); - parser.handleWayTags(edgeFlags, readerWay, relFlags); - assertEquals(RoadClass.OTHER, rcEnc.getEnum(false, edgeFlags)); + parser.handleWayTags(edgeId, intAccess, readerWay, relFlags); + assertEquals(RoadClass.OTHER, rcEnc.getEnum(false, edgeId, intAccess)); - edgeFlags = new IntsRef(1); + intAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("highway", "motorway_link"); - parser.handleWayTags(edgeFlags, readerWay, relFlags); - assertEquals(RoadClass.MOTORWAY, rcEnc.getEnum(false, edgeFlags)); + parser.handleWayTags(edgeId, intAccess, readerWay, relFlags); + assertEquals(RoadClass.MOTORWAY, rcEnc.getEnum(false, edgeId, intAccess)); readerWay = new ReaderWay(1); readerWay.setTag("highway", "cycleway"); - edgeFlags = new IntsRef(1); - parser.handleWayTags(edgeFlags, readerWay, relFlags); - assertEquals(RoadClass.CYCLEWAY, rcEnc.getEnum(false, edgeFlags)); + intAccess = new ArrayEdgeIntAccess(1); + parser.handleWayTags(edgeId, intAccess, readerWay, relFlags); + assertEquals(RoadClass.CYCLEWAY, rcEnc.getEnum(false, edgeId, intAccess)); } @Test public void testIgnore() { ReaderWay readerWay = new ReaderWay(1); - IntsRef edgeFlags = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("route", "ferry"); - parser.handleWayTags(edgeFlags, readerWay, relFlags); - assertEquals(RoadClass.OTHER, rcEnc.getEnum(false, edgeFlags)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(RoadClass.OTHER, rcEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testNoNPE() { ReaderWay readerWay = new ReaderWay(1); - IntsRef edgeFlags = new IntsRef(1); - parser.handleWayTags(edgeFlags, readerWay, relFlags); - assertEquals(RoadClass.OTHER, rcEnc.getEnum(false, edgeFlags)); + ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, intAccess, readerWay, relFlags); + assertEquals(RoadClass.OTHER, rcEnc.getEnum(false, edgeId, intAccess)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadEnvironmentParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadEnvironmentParserTest.java index 3e751df22af..0bc294af446 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadEnvironmentParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMRoadEnvironmentParserTest.java @@ -19,9 +19,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadEnvironment; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.Test; @@ -31,14 +29,15 @@ class OSMRoadEnvironmentParserTest { @Test void ferry() { - EnumEncodedValue roadEnvironmentEnc = new EnumEncodedValue<>(RoadEnvironment.KEY, RoadEnvironment.class); + EnumEncodedValue roadEnvironmentEnc = RoadEnvironment.create(); roadEnvironmentEnc.init(new EncodedValue.InitializerConfig()); OSMRoadEnvironmentParser parser = new OSMRoadEnvironmentParser(roadEnvironmentEnc); - IntsRef edgeFlags = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; ReaderWay way = new ReaderWay(0); way.setTag("route", "shuttle_train"); - parser.handleWayTags(edgeFlags, way, new IntsRef(2)); - RoadEnvironment roadEnvironment = roadEnvironmentEnc.getEnum(false, edgeFlags); + parser.handleWayTags(edgeId, edgeIntAccess, way, new IntsRef(2)); + RoadEnvironment roadEnvironment = roadEnvironmentEnc.getEnum(false, edgeId, edgeIntAccess); assertEquals(RoadEnvironment.FERRY, roadEnvironment); } diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMSmoothnessParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMSmoothnessParserTest.java index 5ce54f37cf7..c0b5e3ead0e 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMSmoothnessParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMSmoothnessParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.Smoothness; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,7 +15,7 @@ public class OSMSmoothnessParserTest { @BeforeEach public void setUp() { - smoothnessEnc = new EnumEncodedValue<>(Smoothness.KEY, Smoothness.class); + smoothnessEnc = Smoothness.create(); smoothnessEnc.init(new EncodedValue.InitializerConfig()); parser = new OSMSmoothnessParser(smoothnessEnc); } @@ -27,14 +25,15 @@ public void testSimpleTags() { IntsRef relFlags = new IntsRef(2); ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("highway", "primary"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Smoothness.MISSING, smoothnessEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Smoothness.MISSING, smoothnessEnc.getEnum(false, edgeId, edgeIntAccess)); readerWay.setTag("smoothness", "bad"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Smoothness.BAD, smoothnessEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Smoothness.BAD, smoothnessEnc.getEnum(false, edgeId, edgeIntAccess)); assertTrue(Smoothness.BAD.ordinal() < Smoothness.VERY_BAD.ordinal()); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMSurfaceParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMSurfaceParserTest.java index fb5a0e4e114..4765dc0bf6c 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMSurfaceParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMSurfaceParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.Surface; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,7 +15,7 @@ public class OSMSurfaceParserTest { @BeforeEach public void setUp() { - surfaceEnc = new EnumEncodedValue<>(Surface.KEY, Surface.class); + surfaceEnc = Surface.create(); surfaceEnc.init(new EncodedValue.InitializerConfig()); parser = new OSMSurfaceParser(surfaceEnc); } @@ -26,18 +24,19 @@ public void setUp() { public void testSimpleTags() { IntsRef relFlags = new IntsRef(2); ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("highway", "primary"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Surface.MISSING, surfaceEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Surface.MISSING, surfaceEnc.getEnum(false, edgeId, edgeIntAccess)); readerWay.setTag("surface", "cobblestone"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Surface.COBBLESTONE, surfaceEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Surface.COBBLESTONE, surfaceEnc.getEnum(false, edgeId, edgeIntAccess)); assertTrue(Surface.COBBLESTONE.ordinal() > Surface.ASPHALT.ordinal()); readerWay.setTag("surface", "earth"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Surface.DIRT, surfaceEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Surface.DIRT, surfaceEnc.getEnum(false, edgeId, edgeIntAccess)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTollParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTollParserTest.java index caaf60c4e43..e5bb19b0189 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTollParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTollParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.Toll; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -16,7 +14,7 @@ public class OSMTollParserTest { @BeforeEach public void setUp() { - tollEnc = new EnumEncodedValue<>(Toll.KEY, Toll.class); + tollEnc = Toll.create(); tollEnc.init(new EncodedValue.InitializerConfig()); parser = new OSMTollParser(tollEnc); } @@ -25,42 +23,43 @@ public void setUp() { public void testSimpleTags() { ReaderWay readerWay = new ReaderWay(1); IntsRef relFlags = new IntsRef(2); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("highway", "primary"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Toll.MISSING, tollEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Toll.MISSING, tollEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("highway", "primary"); readerWay.setTag("toll:hgv", "yes"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Toll.HGV, tollEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Toll.HGV, tollEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("highway", "primary"); readerWay.setTag("toll:N2", "yes"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Toll.HGV, tollEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Toll.HGV, tollEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("highway", "primary"); readerWay.setTag("toll:N3", "yes"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Toll.HGV, tollEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Toll.HGV, tollEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("highway", "primary"); readerWay.setTag("toll", "yes"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Toll.ALL, tollEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Toll.ALL, tollEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("highway", "primary"); readerWay.setTag("toll", "yes"); readerWay.setTag("toll:hgv", "yes"); readerWay.setTag("toll:N2", "yes"); readerWay.setTag("toll:N3", "yes"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(Toll.ALL, tollEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(Toll.ALL, tollEnc.getEnum(false, edgeId, edgeIntAccess)); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTrackTypeParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTrackTypeParserTest.java index 101222380e5..6ea1e973a56 100644 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTrackTypeParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTrackTypeParserTest.java @@ -1,9 +1,7 @@ package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.TrackType; +import com.graphhopper.routing.ev.*; import com.graphhopper.storage.IntsRef; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,7 +15,7 @@ public class OSMTrackTypeParserTest { @BeforeEach public void setUp() { - ttEnc = new EnumEncodedValue<>(TrackType.KEY, TrackType.class); + ttEnc = TrackType.create(); ttEnc.init(new EncodedValue.InitializerConfig()); parser = new OSMTrackTypeParser(ttEnc); relFlags = new IntsRef(2); @@ -26,46 +24,49 @@ public void setUp() { @Test public void testSimpleTags() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("tracktype", "grade1"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(TrackType.GRADE1, ttEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(TrackType.GRADE1, ttEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("tracktype", "grade2"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(TrackType.GRADE2, ttEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(TrackType.GRADE2, ttEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("tracktype", "grade3"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(TrackType.GRADE3, ttEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(TrackType.GRADE3, ttEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("tracktype", "grade4"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(TrackType.GRADE4, ttEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(TrackType.GRADE4, ttEnc.getEnum(false, edgeId, edgeIntAccess)); - intsRef = new IntsRef(1); + edgeIntAccess = new ArrayEdgeIntAccess(1); readerWay.setTag("tracktype", "grade5"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(TrackType.GRADE5, ttEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(TrackType.GRADE5, ttEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testUnkownValue() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; readerWay.setTag("tracktype", "unknownstuff"); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(TrackType.MISSING, ttEnc.getEnum(false, intsRef)); + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(TrackType.MISSING, ttEnc.getEnum(false, edgeId, edgeIntAccess)); } @Test public void testNoNPE() { ReaderWay readerWay = new ReaderWay(1); - IntsRef intsRef = new IntsRef(1); - parser.handleWayTags(intsRef, readerWay, relFlags); - assertEquals(TrackType.MISSING, ttEnc.getEnum(false, intsRef)); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(1); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, readerWay, relFlags); + assertEquals(TrackType.MISSING, ttEnc.getEnum(false, edgeId, edgeIntAccess)); } } diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTurnRelationParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTurnRelationParserTest.java deleted file mode 100644 index f5bac2c8288..00000000000 --- a/core/src/test/java/com/graphhopper/routing/util/parsers/OSMTurnRelationParserTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.graphhopper.routing.util.parsers; - -import com.graphhopper.reader.OSMTurnRelation; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; -import com.graphhopper.routing.util.TransportationMode; -import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.TurnCostStorage; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class OSMTurnRelationParserTest { - - @Test - public void testGetRestrictionAsEntries() { - FlagEncoder encoder = FlagEncoders.createCar(new PMap("turn_costs=true")); - final Map osmNodeToInternal = new HashMap<>(); - final Map internalToOSMEdge = new HashMap<>(); - - osmNodeToInternal.put(3L, 3); - // edge ids are only stored if they occurred before in an OSMRelation - internalToOSMEdge.put(3, 3L); - internalToOSMEdge.put(4, 4L); - - EncodingManager em = EncodingManager.create(encoder); - DecimalEncodedValue tce = encoder.getDecimalEncodedValue(TurnCost.key("car")); - OSMTurnRelationParser parser = new OSMTurnRelationParser(encoder.getAccessEnc(), tce, OSMRoadAccessParser.toOSMRestrictions(TransportationMode.CAR)); - BaseGraph graph = new BaseGraph.Builder(em.getIntsForFlags()).withTurnCosts(true).create(); - initGraph(graph, encoder); - TurnCostParser.ExternalInternalMap map = new TurnCostParser.ExternalInternalMap() { - - @Override - public int getInternalNodeIdOfOsmNode(long nodeOsmId) { - return osmNodeToInternal.getOrDefault(nodeOsmId, -1); - } - - @Override - public long getOsmIdOfInternalEdge(int edgeId) { - Long l = internalToOSMEdge.get(edgeId); - if (l == null) - return -1; - return l; - } - }; - - // TYPE == ONLY - OSMTurnRelation instance = new OSMTurnRelation(4, 3, 3, OSMTurnRelation.Type.ONLY); - parser.addRelationToTCStorage(instance, map, graph); - - TurnCostStorage tcs = graph.getTurnCostStorage(); - assertTrue(Double.isInfinite(tcs.get(tce, 4, 3, 6))); - assertEquals(0, tcs.get(tce, 4, 3, 3), .1); - assertTrue(Double.isInfinite(tcs.get(tce, 4, 3, 2))); - - // TYPE == NOT - instance = new OSMTurnRelation(4, 3, 3, OSMTurnRelation.Type.NOT); - parser.addRelationToTCStorage(instance, map, graph); - assertTrue(Double.isInfinite(tcs.get(tce, 4, 3, 3))); - } - - // 0---1 - // | / - // 2--3--4 - // | | | - // 5--6--7 - private static void initGraph(BaseGraph graph, FlagEncoder encoder) { - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 5).setDistance(0.5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 7).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(1)); - } -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/RacingBikeTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java similarity index 61% rename from core/src/test/java/com/graphhopper/routing/util/RacingBikeTagParserTest.java rename to core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java index 8f69864e287..fc7f8ea2679 100644 --- a/core/src/test/java/com/graphhopper/routing/util/RacingBikeTagParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RacingBikeTagParserTest.java @@ -15,19 +15,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.graphhopper.routing.util; +package com.graphhopper.routing.util.parsers; import com.graphhopper.reader.ReaderRelation; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.GHUtility; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.routing.util.VehicleEncodedValues; +import com.graphhopper.routing.util.VehicleTagParsers; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; -import static com.graphhopper.routing.util.BikeCommonTagParser.PUSHING_SECTION_SPEED; -import static com.graphhopper.routing.util.EncodingManager.Access.WAY; +import java.util.Arrays; +import java.util.List; + import static com.graphhopper.routing.util.PriorityCode.*; +import static com.graphhopper.routing.util.parsers.BikeCommonAverageSpeedParser.MIN_SPEED; +import static com.graphhopper.routing.util.parsers.BikeCommonAverageSpeedParser.PUSHING_SECTION_SPEED; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -36,8 +41,13 @@ */ public class RacingBikeTagParserTest extends AbstractBikeTagParserTester { @Override - protected BikeCommonTagParser createBikeTagParser() { - return new RacingBikeTagParser(new PMap("block_fords=true")); + protected EncodingManager createEncodingManager() { + return new EncodingManager.Builder().add(VehicleEncodedValues.racingbike(new PMap())).build(); + } + + @Override + protected VehicleTagParsers createBikeTagParsers(EncodedValueLookup lookup, PMap pMap) { + return VehicleTagParsers.racingbike(lookup, pMap); } @Test @@ -47,7 +57,7 @@ public void testAvoidTunnel() { ReaderWay osmWay = new ReaderWay(1); osmWay.setTag("highway", "residential"); osmWay.setTag("tunnel", "yes"); - assertPriorityAndSpeed(UNCHANGED.getValue(), 16, osmWay); + assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), 18, osmWay); osmWay.setTag("highway", "secondary"); osmWay.setTag("tunnel", "yes"); @@ -62,10 +72,10 @@ public void testAvoidTunnel() { public void testService() { ReaderWay way = new ReaderWay(1); way.setTag("highway", "service"); - assertPriorityAndSpeed(UNCHANGED.getValue(), 12, way); + assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), 12, way); way.setTag("service", "parking_aisle"); - assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), 6, way); + assertPriorityAndSpeed(SLIGHT_AVOID.getValue(), 4, way); } @Test @@ -74,23 +84,24 @@ public void testSacScale() { ReaderWay way = new ReaderWay(1); way.setTag("highway", "service"); way.setTag("sac_scale", "mountain_hiking"); - assertTrue(parser.getAccess(way).canSkip()); + assertTrue(accessParser.getAccess(way).canSkip()); way.setTag("highway", "path"); way.setTag("sac_scale", "hiking"); - assertTrue(parser.getAccess(way).isWay()); + assertTrue(accessParser.getAccess(way).isWay()); // This looks to be tagging error: way.setTag("highway", "cycleway"); way.setTag("sac_scale", "mountain_hiking"); // we are cautious and disallow this - assertTrue(parser.getAccess(way).canSkip()); + assertTrue(accessParser.getAccess(way).canSkip()); } @Test public void testGetSpeed() { - IntsRef intsRef = GHUtility.setSpeed(10, 0, parser, encodingManager.createEdgeFlags()); - assertEquals(10, avgSpeedEnc.getDecimal(false, intsRef), 1e-1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + avgSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 10); ReaderWay way = new ReaderWay(1); way.setTag("highway", "track"); way.setTag("tracktype", "grade3"); @@ -124,16 +135,16 @@ public void testGetSpeed() { public void testSmoothness() { ReaderWay way = new ReaderWay(1); way.setTag("highway", "residential"); - assertEquals(16, getSpeedFromFlags(way), 0.01); + assertEquals(18, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "excellent"); - assertEquals(20, getSpeedFromFlags(way), 0.01); + assertEquals(22, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "bad"); assertEquals(12, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "impassable"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(MIN_SPEED, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "unknown"); assertEquals(12, getSpeedFromFlags(way), 0.01); @@ -141,28 +152,28 @@ public void testSmoothness() { way.clearTags(); way.setTag("highway", "residential"); way.setTag("surface", "ground"); - assertEquals(2, getSpeedFromFlags(way), 0.01); + assertEquals(MIN_SPEED, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "bad"); - assertEquals(2, getSpeedFromFlags(way), 0.01); + assertEquals(MIN_SPEED, getSpeedFromFlags(way), 0.01); way.clearTags(); way.setTag("highway", "track"); way.setTag("tracktype", "grade5"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(4, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "bad"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(2, getSpeedFromFlags(way), 0.01); way.setTag("smoothness", "impassable"); - assertEquals(PUSHING_SECTION_SPEED, getSpeedFromFlags(way), 0.01); + assertEquals(MIN_SPEED, getSpeedFromFlags(way), 0.01); } @Test public void testHandleWayTagsInfluencedByRelation() { ReaderWay osmWay = new ReaderWay(1); osmWay.setTag("highway", "track"); - assertEquals(PUSHING_SECTION_SPEED / 2, getSpeedFromFlags(osmWay), 1e-1); + assertEquals(MIN_SPEED, getSpeedFromFlags(osmWay), 1e-1); // relation code is PREFER ReaderRelation osmRel = new ReaderRelation(1); @@ -201,67 +212,73 @@ public void testHandleWayTagsInfluencedByRelation() { @Test public void testPriority_avoidanceOfHighMaxSpeed() { // here we test the priority that would be calculated if the way was accessible (even when it is not) - // therefore we need a modified encoder that always yields access=WAY - BikeCommonTagParser encoder = new RacingBikeTagParser(new PMap("block_fords=true")) { - @Override - public EncodingManager.Access getAccess(ReaderWay way) { - return WAY; - } - }; - TagParserManager encodingManager = TagParserManager.create(encoder); + // therefore we need a modified parser that always yields access=WAY + BooleanEncodedValue accessEnc = VehicleAccess.create("racingbike"); + DecimalEncodedValue speedEnc = VehicleSpeed.create("racingbike", 4, 2, false); + DecimalEncodedValue priorityEnc = VehiclePriority.create("racingbike", 4, PriorityCode.getValue(1), false); + EncodingManager encodingManager = EncodingManager.start() + .add(accessEnc).add(speedEnc).add(priorityEnc) + .add(RouteNetwork.create(BikeNetwork.KEY)) + .add(Smoothness.create()) + .build(); + List parsers = Arrays.asList( + new RacingBikeAverageSpeedParser(encodingManager, new PMap()), + new RacingBikePriorityParser(encodingManager, new PMap()) + ); ReaderWay osmWay = new ReaderWay(1); osmWay.setTag("highway", "tertiary"); osmWay.setTag("maxspeed", "50"); - assertPriorityAndSpeed(encodingManager, PREFER.getValue(), 20, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, PREFER.getValue(), 20, osmWay); osmWay.setTag("maxspeed", "60"); - assertPriorityAndSpeed(encodingManager, PREFER.getValue(), 20, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, PREFER.getValue(), 20, osmWay); osmWay.setTag("maxspeed", "80"); - assertPriorityAndSpeed(encodingManager, PREFER.getValue(), 20, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, PREFER.getValue(), 20, osmWay); osmWay.setTag("maxspeed", "90"); - assertPriorityAndSpeed(encodingManager, UNCHANGED.getValue(), 20, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, UNCHANGED.getValue(), 20, osmWay); osmWay.setTag("maxspeed", "120"); - assertPriorityAndSpeed(encodingManager, UNCHANGED.getValue(), 20, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, UNCHANGED.getValue(), 20, osmWay); osmWay.setTag("highway", "motorway"); - assertPriorityAndSpeed(encodingManager, AVOID.getValue(), 18, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, AVOID.getValue(), 18, osmWay); osmWay.setTag("tunnel", "yes"); - assertPriorityAndSpeed(encodingManager, AVOID_MORE.getValue(), 18, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, BAD.getValue(), 18, osmWay); osmWay.clearTags(); osmWay.setTag("highway", "motorway"); osmWay.setTag("tunnel", "yes"); osmWay.setTag("maxspeed", "80"); - assertPriorityAndSpeed(encodingManager, AVOID_MORE.getValue(), 18, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, BAD.getValue(), 18, osmWay); osmWay.clearTags(); osmWay.setTag("highway", "motorway"); osmWay.setTag("tunnel", "yes"); osmWay.setTag("maxspeed", "120"); - assertPriorityAndSpeed(encodingManager, AVOID_MORE.getValue(), 18, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, BAD.getValue(), 18, osmWay); osmWay.clearTags(); osmWay.setTag("highway", "notdefined"); osmWay.setTag("tunnel", "yes"); osmWay.setTag("maxspeed", "120"); - assertPriorityAndSpeed(encodingManager, AVOID_MORE.getValue(), 4, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, BAD.getValue(), PUSHING_SECTION_SPEED, osmWay); osmWay.clearTags(); osmWay.setTag("highway", "notdefined"); osmWay.setTag("maxspeed", "50"); - assertPriorityAndSpeed(encodingManager, UNCHANGED.getValue(), 4, osmWay); + assertPriorityAndSpeed(encodingManager, priorityEnc, speedEnc, parsers, UNCHANGED.getValue(), PUSHING_SECTION_SPEED, osmWay); } - private void assertPriorityAndSpeed(TagParserManager encodingManager, int expectedPrio, double expectedSpeed, ReaderWay way) { - IntsRef edgeFlags = encodingManager.handleWayTags(way, encodingManager.createRelationFlags()); - FlagEncoder encoder = encodingManager.fetchEdgeEncoders().iterator().next(); - DecimalEncodedValue enc = encodingManager.getDecimalEncodedValue(EncodingManager.getKey(encoder.toString(), "priority")); - assertEquals(expectedSpeed, encoder.getAverageSpeedEnc().getDecimal(false, edgeFlags), 0.1); - assertEquals(PriorityCode.getValue(expectedPrio), enc.getDecimal(false, edgeFlags), 0.01); + private void assertPriorityAndSpeed(EncodingManager encodingManager, DecimalEncodedValue priorityEnc, DecimalEncodedValue speedEnc, + List parsers, int expectedPrio, double expectedSpeed, ReaderWay way) { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + for (TagParser p : parsers) p.handleWayTags(edgeId, edgeIntAccess, way, null); + assertEquals(PriorityCode.getValue(expectedPrio), priorityEnc.getDecimal(false, edgeId, edgeIntAccess), 0.01); + assertEquals(expectedSpeed, speedEnc.getDecimal(false, edgeId, edgeIntAccess), 0.1); } @Test diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java new file mode 100644 index 00000000000..10c17c6b59d --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RestrictionSetterTest.java @@ -0,0 +1,294 @@ +package com.graphhopper.routing.util.parsers; + +import com.carrotsearch.hppc.IntArrayList; +import com.graphhopper.reader.osm.GraphRestriction; +import com.graphhopper.reader.osm.Pair; +import com.graphhopper.reader.osm.RestrictionType; +import com.graphhopper.routing.Dijkstra; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.EncodedValue; +import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.util.TraversalMode; +import com.graphhopper.routing.weighting.DefaultTurnCostProvider; +import com.graphhopper.routing.weighting.TurnCostProvider; +import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.EdgeIteratorState; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class RestrictionSetterTest { + private static final IntArrayList NO_PATH = IntArrayList.from(); + private BaseGraph graph; + private RestrictionSetter r; + + @BeforeEach + void setup() { + graph = new BaseGraph.Builder(1).withTurnCosts(true).create(); + r = new RestrictionSetter(graph); + } + + @Test + void viaNode_no() { + // 0-1-2 + // | | + // 3-4 + int a = edge(0, 1); + int b = edge(1, 2); + edge(1, 3); + edge(2, 4); + edge(3, 4); + GraphRestriction graphRestriction = GraphRestriction.node(a, 1, b); + DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.NO)), turnCostEnc); + assertEquals(nodes(0, 1, 3, 4, 2), calcPath(0, 2, turnCostEnc)); + } + + @Test + void viaNode_only() { + // 0-1-2 + // | | + // 3-4 + int a = edge(0, 1); + int b = edge(1, 2); + edge(1, 3); + edge(2, 4); + edge(3, 4); + GraphRestriction graphRestriction = GraphRestriction.node(a, 1, b); + DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + r.setRestrictions(Arrays.asList(new Pair<>(graphRestriction, RestrictionType.ONLY)), turnCostEnc); + assertEquals(nodes(0, 1, 2, 4, 3), calcPath(0, 3, turnCostEnc)); + } + + @Test + void viaWay_no() { + // 4 + // a b|c + // 0-1-2-3 + // | | + // 5 6 + // | | + // 8-9 + int a = edge(0, 1); + int b = edge(1, 2); + int c = edge(2, 3); + edge(2, 4); + edge(1, 5); + edge(5, 8); + edge(2, 6); + edge(6, 9); + edge(8, 9); + GraphRestriction graphRestriction = GraphRestriction.way(a, b, c, nodes(1, 2)); + DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + r.setRestrictions(Arrays.asList( + new Pair<>(graphRestriction, RestrictionType.NO) + ), turnCostEnc); + // turning from a to b and then to c is not allowed + assertEquals(nodes(0, 1, 5, 8, 9, 6, 2, 3), calcPath(0, 3, turnCostEnc)); + // turning from a to b, or b to c is still allowed + assertEquals(nodes(0, 1, 2, 4), calcPath(0, 4, turnCostEnc)); + assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnCostEnc)); + } + + @Test + void viaWay_no_withOverlap() { + // a b c d + // 0---1---2---3---4 + // |s |t |u + // 5 6 7 + int a = edge(0, 1); + int b = edge(1, 2); + int c = edge(2, 3); + int d = edge(3, 4); + int s = edge(1, 5); + int t = edge(2, 6); + int u = edge(3, 7); + + DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + r.setRestrictions(Arrays.asList( + new Pair<>(GraphRestriction.way(a, b, c, nodes(1, 2)), RestrictionType.NO), + new Pair<>(GraphRestriction.way(b, c, d, nodes(2, 3)), RestrictionType.NO) + ), turnCostEnc); + + assertEquals(NO_PATH, calcPath(0, 3, turnCostEnc)); // a-b-c + assertEquals(nodes(0, 1, 2, 6), calcPath(0, 6, turnCostEnc)); // a-b-t + assertEquals(nodes(5, 1, 2, 3), calcPath(5, 3, turnCostEnc)); // s-b-c + assertEquals(nodes(5, 1, 2, 6), calcPath(5, 6, turnCostEnc)); // s-b-t + + assertEquals(NO_PATH, calcPath(1, 4, turnCostEnc)); // b-c-d + assertEquals(nodes(1, 2, 3, 7), calcPath(1, 7, turnCostEnc)); // b-c-u + assertEquals(nodes(6, 2, 3, 4), calcPath(6, 4, turnCostEnc)); // t-c-d + assertEquals(nodes(6, 2, 3, 7), calcPath(6, 7, turnCostEnc)); // t-c-u + } + + @Test + void viaWay_no_withOverlap_more_complex() { + // 0 1 + // | a | + // 2--3---4--5 + // b| |d + // 6--7---8--9 + // | c | + // 10---11 + int s = edge(0, 3); + edge(1, 4); + edge(2, 3); + int a = edge(3, 4); + int t = edge(4, 5); + int b = edge(3, 7); + int d = edge(4, 8); + edge(6, 7); + int c = edge(7, 8); + edge(8, 9); + edge(7, 10); + edge(8, 11); + edge(10, 11); + DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + r.setRestrictions(Arrays.asList( + new Pair<>(GraphRestriction.node(t, 4, d), RestrictionType.NO), + new Pair<>(GraphRestriction.node(s, 3, a), RestrictionType.NO), + new Pair<>(GraphRestriction.way(a, b, c, nodes(3, 7)), RestrictionType.NO), + new Pair<>(GraphRestriction.way(b, c, d, nodes(7, 8)), RestrictionType.NO), + new Pair<>(GraphRestriction.way(c, d, a, nodes(8, 4)), RestrictionType.NO), + new Pair<>(GraphRestriction.way(d, a, b, nodes(4, 3)), RestrictionType.NO) + ), turnCostEnc); + + assertEquals(nodes(0, 3, 7, 8, 9), calcPath(0, 9, turnCostEnc)); + assertEquals(nodes(5, 4, 3, 7, 10, 11, 8, 9), calcPath(5, 9, turnCostEnc)); + assertEquals(nodes(5, 4, 3, 2), calcPath(5, 2, turnCostEnc)); + assertEquals(nodes(0, 3, 7, 10), calcPath(0, 10, turnCostEnc)); + assertEquals(nodes(6, 7, 8, 9), calcPath(6, 9, turnCostEnc)); + } + + @Test + void viaWay_only() { + // 0 + // a |b c + // 1----2----3 + // |d + // 4----5----6 + // e |f g + // 7 + int a = edge(1, 2); + int b = edge(0, 2); + int c = edge(2, 3); + int d = edge(2, 5); + int e = edge(4, 5); + int f = edge(5, 7); + int g = edge(5, 6); + DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + r.setRestrictions(Arrays.asList( + new Pair<>(GraphRestriction.way(a, d, f, nodes(2, 5)), RestrictionType.ONLY), + // we add a few more restrictions, because that happens a lot in real data + new Pair<>(GraphRestriction.way(c, d, g, nodes(2, 5)), RestrictionType.NO), + new Pair<>(GraphRestriction.node(e, 5, f), RestrictionType.NO) + ), turnCostEnc); + // following the restriction is allowed of course + assertEquals(nodes(1, 2, 5, 7), calcPath(1, 7, turnCostEnc)); + // taking another turn at the beginning is not allowed + assertEquals(nodes(), calcPath(1, 3, turnCostEnc)); + // taking another turn after the first turn is not allowed either + assertEquals(nodes(), calcPath(1, 4, turnCostEnc)); + // coming from somewhere we can go anywhere + assertEquals(nodes(0, 2, 5, 6), calcPath(0, 6, turnCostEnc)); + assertEquals(nodes(0, 2, 5, 7), calcPath(0, 7, turnCostEnc)); + } + + @Test + void viaWay_only_twoRestrictionsSharingSameVia() { + // a c d + // 0---1---2---3 + // |b |e + // 5--/ \--4 + int a = edge(0, 1); + int b = edge(5, 1); + int c = edge(1, 2); + int d = edge(2, 3); + int e = edge(2, 4); + DecimalEncodedValue turnCostEnc = createTurnCostEnc("car"); + assertThrows(IllegalStateException.class, () -> r.setRestrictions(Arrays.asList( + // These are two 'only' via-way restrictions that share the same via way. A real-world example can + // be found in Rüdesheim am Rhein where vehicles either have to go straight or enter the ferry depending + // on the from-way, even though they use the same via way before. + // We have to make sure such cases are ignored already when we parse the OSM data. + new Pair<>(GraphRestriction.way(a, c, d, nodes(1, 2)), RestrictionType.ONLY), + new Pair<>(GraphRestriction.way(b, c, e, nodes(1, 2)), RestrictionType.ONLY) + ), turnCostEnc) + ); + } + + private static DecimalEncodedValue createTurnCostEnc(String name) { + DecimalEncodedValue turnCostEnc = TurnCost.create(name, 1); + turnCostEnc.init(new EncodedValue.InitializerConfig()); + return turnCostEnc; + } + + private IntArrayList calcPath(int from, int to, DecimalEncodedValue turnCostEnc) { + return new IntArrayList(new Dijkstra(graph, new MyWeighting(graph, turnCostEnc), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes()); + } + + private IntArrayList nodes(int... nodes) { + return IntArrayList.from(nodes); + } + + private IntArrayList edges(int... edges) { + return IntArrayList.from(edges); + } + + private int edge(int from, int to) { + return graph.edge(from, to).setDistance(10).getEdge(); + } + + private static class MyWeighting implements Weighting { + private final TurnCostProvider turnCostProvider; + + public MyWeighting(BaseGraph graph, DecimalEncodedValue turnCostEnc) { + turnCostProvider = new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()); + } + + @Override + public double getMinWeight(double distance) { + return 0; + } + + @Override + public boolean edgeHasNoAccess(EdgeIteratorState edgeState, boolean reverse) { + return false; + } + + @Override + public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { + return edgeState.getDistance(); + } + + @Override + public long calcEdgeMillis(EdgeIteratorState edgeState, boolean reverse) { + return 0; + } + + @Override + public double calcTurnWeight(int inEdge, int viaNode, int outEdge) { + return turnCostProvider.calcTurnWeight(inEdge, viaNode, outEdge); + } + + @Override + public long calcTurnMillis(int inEdge, int viaNode, int outEdge) { + return 0; + } + + @Override + public boolean hasTurnCosts() { + return true; + } + + @Override + public String getName() { + return "test"; + } + } +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/RoadsTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/RoadsTagParserTest.java new file mode 100644 index 00000000000..bb68170c981 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/RoadsTagParserTest.java @@ -0,0 +1,32 @@ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.routing.ev.ArrayEdgeIntAccess; +import com.graphhopper.routing.ev.EdgeIntAccess; +import com.graphhopper.routing.ev.VehicleSpeed; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.VehicleEncodedValues; +import com.graphhopper.util.PMap; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class RoadsTagParserTest { + + private final EncodingManager encodingManager = new EncodingManager.Builder().add(VehicleEncodedValues.roads(new PMap())).build(); + private final RoadsAverageSpeedParser parser; + + public RoadsTagParserTest() { + parser = new RoadsAverageSpeedParser(encodingManager, new PMap()); + } + + @Test + public void testSpeed() { + ReaderWay way = new ReaderWay(1); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + parser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(encodingManager.getDecimalEncodedValue(VehicleSpeed.key("roads")).getDecimal(false, edgeId, edgeIntAccess) > 200); + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/TagParsingTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/TagParsingTest.java new file mode 100644 index 00000000000..2e44a7dfd3c --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/TagParsingTest.java @@ -0,0 +1,182 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderRelation; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.OSMParsers; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.storage.IntsRef; +import com.graphhopper.util.PMap; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class TagParsingTest { + @Test + public void testCombineRelations() { + ReaderWay osmWay = new ReaderWay(1); + osmWay.setTag("highway", "track"); + ReaderRelation osmRel = new ReaderRelation(1); + + BooleanEncodedValue bike1AccessEnc = VehicleAccess.create("bike1"); + DecimalEncodedValue bike1SpeedEnc = VehicleSpeed.create("bike1", 4, 2, false); + DecimalEncodedValue bike1PriorityEnc = VehiclePriority.create("bike1", 4, PriorityCode.getFactor(1), false); + BooleanEncodedValue bike2AccessEnc = VehicleAccess.create("bike2"); + DecimalEncodedValue bike2SpeedEnc = VehicleSpeed.create("bike2", 4, 2, false); + DecimalEncodedValue bike2PriorityEnc = VehiclePriority.create("bike2", 4, PriorityCode.getFactor(1), false); + EnumEncodedValue bikeNetworkEnc = RouteNetwork.create(BikeNetwork.KEY); + EncodingManager em = EncodingManager.start() + .add(bike1AccessEnc).add(bike1SpeedEnc).add(bike1PriorityEnc) + .add(bike2AccessEnc).add(bike2SpeedEnc).add(bike2PriorityEnc) + .add(bikeNetworkEnc) + .add(Smoothness.create()) + .build(); + BikePriorityParser bike1Parser = new BikePriorityParser(em, new PMap("name=bike1")); + BikePriorityParser bike2Parser = new BikePriorityParser(em, new PMap("name=bike2")) { + @Override + public void handleWayTags(int edgeId, EdgeIntAccess intAccess, ReaderWay way, IntsRef relTags) { + // accept less relations + if (bikeRouteEnc.getEnum(false, edgeId, intAccess) != RouteNetwork.MISSING) + priorityEnc.setDecimal(false, edgeId, intAccess, PriorityCode.getFactor(2)); + } + }; + OSMParsers osmParsers = new OSMParsers() + .addRelationTagParser(relConfig -> new OSMBikeNetworkTagParser(bikeNetworkEnc, relConfig)) + .addWayTagParser(new OSMRoadClassParser(em.getEnumEncodedValue(RoadClass.KEY, RoadClass.class))) + .addWayTagParser(bike1Parser) + .addWayTagParser(bike2Parser); + + // relation code is PREFER + osmRel.setTag("route", "bicycle"); + osmRel.setTag("network", "lcn"); + IntsRef relFlags = osmParsers.createRelationFlags(); + relFlags = osmParsers.handleRelationTags(osmRel, relFlags); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + osmParsers.handleWayTags(edgeId, edgeIntAccess, osmWay, relFlags); + assertEquals(RouteNetwork.LOCAL, bikeNetworkEnc.getEnum(false, edgeId, edgeIntAccess)); + assertTrue(bike1PriorityEnc.getDecimal(false, edgeId, edgeIntAccess) > bike2PriorityEnc.getDecimal(false, edgeId, edgeIntAccess)); + } + + @Test + public void testMixBikeTypesAndRelationCombination() { + ReaderWay osmWay = new ReaderWay(1); + osmWay.setTag("highway", "track"); + osmWay.setTag("tracktype", "grade1"); + + ReaderRelation osmRel = new ReaderRelation(1); + + BooleanEncodedValue bikeAccessEnc = VehicleAccess.create("bike"); + DecimalEncodedValue bikeSpeedEnc = VehicleSpeed.create("bike", 4, 2, false); + DecimalEncodedValue bikePriorityEnc = VehiclePriority.create("bike", 4, PriorityCode.getFactor(1), false); + BooleanEncodedValue mtbAccessEnc = VehicleAccess.create("mtb"); + DecimalEncodedValue mtbSpeedEnc = VehicleSpeed.create("mtb", 4, 2, false); + DecimalEncodedValue mtbPriorityEnc = VehiclePriority.create("mtb", 4, PriorityCode.getFactor(1), false); + EnumEncodedValue bikeNetworkEnc = RouteNetwork.create(BikeNetwork.KEY); + EncodingManager em = EncodingManager.start() + .add(bikeAccessEnc).add(bikeSpeedEnc).add(bikePriorityEnc) + .add(mtbAccessEnc).add(mtbSpeedEnc).add(mtbPriorityEnc) + .add(bikeNetworkEnc) + .add(Smoothness.create()) + .build(); + BikePriorityParser bikeTagParser = new BikePriorityParser(em, new PMap()); + MountainBikePriorityParser mtbTagParser = new MountainBikePriorityParser(em, new PMap()); + OSMParsers osmParsers = new OSMParsers() + .addRelationTagParser(relConfig -> new OSMBikeNetworkTagParser(bikeNetworkEnc, relConfig)) + .addWayTagParser(new OSMRoadClassParser(em.getEnumEncodedValue(RoadClass.KEY, RoadClass.class))) + .addWayTagParser(bikeTagParser) + .addWayTagParser(mtbTagParser); + + // relation code for network rcn is NICE for bike and PREFER for mountainbike + osmRel.setTag("route", "bicycle"); + osmRel.setTag("network", "rcn"); + IntsRef relFlags = osmParsers.createRelationFlags(); + relFlags = osmParsers.handleRelationTags(osmRel, relFlags); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(em.getIntsForFlags()); + int edgeId = 0; + osmParsers.handleWayTags(edgeId, edgeIntAccess, osmWay, relFlags); + // bike: uninfluenced speed for grade but via network => NICE + // mtb: uninfluenced speed only PREFER + assertTrue(bikePriorityEnc.getDecimal(false, edgeId, edgeIntAccess) > mtbPriorityEnc.getDecimal(false, edgeId, edgeIntAccess)); + } + + @Test + public void testSharedEncodedValues() { + BooleanEncodedValue carAccessEnc = VehicleAccess.create("car"); + BooleanEncodedValue footAccessEnc = VehicleAccess.create("foot"); + BooleanEncodedValue bikeAccessEnc = VehicleAccess.create("bike"); + BooleanEncodedValue motorcycleAccessEnc = VehicleAccess.create("motorcycle"); + BooleanEncodedValue mtbAccessEnc = VehicleAccess.create("mtb"); + List accessEncs = Arrays.asList(carAccessEnc, footAccessEnc, bikeAccessEnc, motorcycleAccessEnc, mtbAccessEnc); + EncodingManager manager = EncodingManager.start() + .add(carAccessEnc).add(VehicleSpeed.create("car", 5, 5, true)) + .add(footAccessEnc).add(VehicleSpeed.create("foot", 4, 1, true)).add(VehiclePriority.create("foot", 4, PriorityCode.getFactor(1), false)) + .add(bikeAccessEnc).add(VehicleSpeed.create("bike", 4, 2, false)).add(VehiclePriority.create("bike", 4, PriorityCode.getFactor(1), false)) + .add(motorcycleAccessEnc).add(VehicleSpeed.create("motorcycle", 5, 5, true)).add(VehiclePriority.create("motorcycle", 4, PriorityCode.getFactor(1), false)).add(new DecimalEncodedValueImpl("motorcycle_curvature", 5, 5, true)) + .add(mtbAccessEnc).add(VehicleSpeed.create("mtb", 4, 2, false)).add(VehiclePriority.create("mtb", 4, PriorityCode.getFactor(1), false)) + .add(RouteNetwork.create(FootNetwork.KEY)) + .add(RouteNetwork.create(BikeNetwork.KEY)) + .add(Smoothness.create()) + .build(); + + BooleanEncodedValue roundaboutEnc = manager.getBooleanEncodedValue(Roundabout.KEY); + List tagParsers = Arrays.asList( + new OSMRoundaboutParser(roundaboutEnc), + new CarAccessParser(manager, new PMap()), + new FootAccessParser(manager, new PMap()), + new BikeAccessParser(manager, new PMap()), + new MotorcycleAccessParser(manager, new PMap()), + new MountainBikeAccessParser(manager, new PMap()) + ); + for (TagParser tagParser : tagParsers) + if (tagParser instanceof AbstractAccessParser) + ((AbstractAccessParser) tagParser).init(new DateRangeParser()); + + final ArrayEdgeIntAccess intAccess = new ArrayEdgeIntAccess(manager.getIntsForFlags()); + int edgeId = 0; + IntsRef relFlags = manager.createRelationFlags(); + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "primary"); + way.setTag("junction", "roundabout"); + tagParsers.forEach(p -> p.handleWayTags(edgeId, intAccess, way, relFlags)); + + assertTrue(roundaboutEnc.getBool(false, edgeId, intAccess)); + for (BooleanEncodedValue accessEnc : accessEncs) + assertTrue(accessEnc.getBool(false, edgeId, intAccess)); + + final IntsRef edgeFlags2 = manager.createEdgeFlags(); + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("junction", "circular"); + tagParsers.forEach(p -> p.handleWayTags(edgeId, intAccess, way, relFlags)); + + assertTrue(roundaboutEnc.getBool(false, edgeId, intAccess)); + for (BooleanEncodedValue accessEnc : accessEncs) + assertTrue(accessEnc.getBool(false, edgeId, intAccess)); + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java b/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java new file mode 100644 index 00000000000..60a3d77d88f --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/util/parsers/WheelchairTagParserTest.java @@ -0,0 +1,591 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.routing.util.parsers; + +import com.graphhopper.reader.ReaderNode; +import com.graphhopper.reader.ReaderWay; +import com.graphhopper.reader.osm.conditional.DateRangeParser; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.PriorityCode; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.storage.NodeAccess; +import com.graphhopper.util.*; +import org.junit.jupiter.api.Test; + +import java.text.DateFormat; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author don-philipe + */ +public class WheelchairTagParserTest { + private final BooleanEncodedValue wheelchairAccessEnc; + private final DecimalEncodedValue wheelchairAvSpeedEnc; + private final DecimalEncodedValue wheelchairPriorityEnc; + private final BooleanEncodedValue carAccessEnc; + private final DecimalEncodedValue carAvSpeedEnc; + private final EncodingManager encodingManager; + private final WheelchairAccessParser accessParser; + private final WheelchairAverageSpeedParser speedParser; + private final WheelchairPriorityParser prioParser; + + public WheelchairTagParserTest() { + wheelchairAccessEnc = VehicleAccess.create("wheelchair"); + wheelchairAvSpeedEnc = VehicleSpeed.create("wheelchair", 4, 1, true); + wheelchairPriorityEnc = VehiclePriority.create("wheelchair", 4, PriorityCode.getFactor(1), false); + carAccessEnc = VehicleAccess.create("car"); + carAvSpeedEnc = VehicleSpeed.create("car", 5, 5, false); + encodingManager = EncodingManager.start() + .add(wheelchairAccessEnc).add(wheelchairAvSpeedEnc).add(wheelchairPriorityEnc).add(RouteNetwork.create(FootNetwork.KEY)) + .add(carAccessEnc).add(carAvSpeedEnc) + .build(); + accessParser = new WheelchairAccessParser(encodingManager, new PMap()); + accessParser.init(new DateRangeParser()); + speedParser = new WheelchairAverageSpeedParser(encodingManager, new PMap()) { + @Override + public void applyWayTags(ReaderWay way, int edgeId, EdgeIntAccess edgeIntAccess) { + if (way.hasTag("point_list") && way.hasTag("edge_distance")) + super.applyWayTags(way, edgeId, edgeIntAccess); + } + }; + prioParser = new WheelchairPriorityParser(encodingManager, new PMap()); + } + + @Test + public void testGetSpeed() { + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + wheelchairAccessEnc.setBool(false, edgeId, edgeIntAccess, true); + wheelchairAccessEnc.setBool(true, edgeId, edgeIntAccess, true); + wheelchairAvSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 10); + assertEquals(10, wheelchairAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + } + + @Test + public void testCombined() { + BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + EdgeIteratorState edge = g.edge(0, 1); + edge.set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); + edge.set(carAvSpeedEnc, 100.0).set(carAccessEnc, true, false); + + assertEquals(10, edge.get(wheelchairAvSpeedEnc), .1); + assertTrue(edge.get(wheelchairAccessEnc)); + assertTrue(edge.getReverse(wheelchairAccessEnc)); + + assertEquals(100, edge.get(carAvSpeedEnc), .1); + assertTrue(edge.get(carAccessEnc)); + assertFalse(edge.getReverse(carAccessEnc)); + + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + wheelchairAvSpeedEnc.setDecimal(false, edgeId, edgeIntAccess, 10); + wheelchairAccessEnc.setBool(false, edgeId, edgeIntAccess, true); + wheelchairAccessEnc.setBool(true, edgeId, edgeIntAccess, true); + assertEquals(0, carAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + } + + @Test + public void testGraph() { + BaseGraph g = new BaseGraph.Builder(encodingManager).create(); + g.edge(0, 1).setDistance(10).set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); + g.edge(0, 2).setDistance(10).set(wheelchairAvSpeedEnc, 5.0).set(wheelchairAccessEnc, true, true); + g.edge(1, 3).setDistance(10).set(wheelchairAvSpeedEnc, 10.0).set(wheelchairAccessEnc, true, true); + EdgeExplorer out = g.createEdgeExplorer(AccessFilter.outEdges(accessParser.getAccessEnc())); + assertEquals(GHUtility.asSet(1, 2), GHUtility.getNeighbors(out.setBaseNode(0))); + assertEquals(GHUtility.asSet(0, 3), GHUtility.getNeighbors(out.setBaseNode(1))); + assertEquals(GHUtility.asSet(0), GHUtility.getNeighbors(out.setBaseNode(2))); + } + + @Test + public void testAccess() { + ReaderWay way = new ReaderWay(1); + + way.setTag("highway", "motorway"); + way.setTag("sidewalk", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("sidewalk", "left"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("sidewalk", "none"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("sidewalk", "left"); + way.setTag("access", "private"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.clearTags(); + + way.setTag("highway", "pedestrian"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "footway"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "platform"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("highway", "motorway"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("bicycle", "official"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("foot", "official"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("access", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("foot", "yes"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("vehicle", "no"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "tertiary"); + way.setTag("motorroad", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "cycleway"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("access", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "service"); + way.setTag("foot", "yes"); + way.setTag("access", "no"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "track"); + way.setTag("ford", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("route", "ferry"); + assertTrue(accessParser.getAccess(way).isFerry()); + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + // #1562, test if ferry route with foot + way.clearTags(); + way.setTag("route", "ferry"); + way.setTag("foot", "yes"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "designated"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "official"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "permissive"); + assertTrue(accessParser.getAccess(way).isFerry()); + + way.setTag("foot", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("foot", "designated"); + way.setTag("access", "private"); + assertTrue(accessParser.getAccess(way).canSkip()); + + DateFormat simpleDateFormat = Helper.createFormatter("yyyy MMM dd"); + + way.clearTags(); + way.setTag("highway", "footway"); + way.setTag("access:conditional", "no @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "footway"); + way.setTag("access", "no"); + way.setTag("access:conditional", "yes @ (" + simpleDateFormat.format(new Date().getTime()) + ")"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "steps"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + // allow paths as they are used as generic path + way.setTag("highway", "path"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "track"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("sac_scale", "hiking"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "footway"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("incline", "up"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("incline", "3%"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("incline", "9.1%"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("incline", "1°"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("incline", "5°"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("incline", "-4%"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("incline", "-9%"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("incline", "-3°"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("incline", "-6.5°"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.clearTags(); + way.setTag("highway", "footway"); + way.setTag("wheelchair", "no"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("wheelchair", "limited"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "footway"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("kerb", "lowered"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("kerb", "raised"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("kerb", "2cm"); + assertTrue(accessParser.getAccess(way).isWay()); + way.setTag("kerb", "4cm"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("kerb", "20mm"); + assertTrue(accessParser.getAccess(way).isWay()); + + // highway tag required + way.clearTags(); + way.setTag("wheelchair", "yes"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("highway", "footway"); + assertTrue(accessParser.getAccess(way).isWay()); + } + + @Test + public void testPier() { + ReaderWay way = new ReaderWay(1); + way.setTag("man_made", "pier"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); + speedParser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(wheelchairAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(wheelchairAccessEnc.getBool(true, edgeId, edgeIntAccess)); + assertEquals(5, wheelchairAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess)); + } + + @Test + public void testMixSpeedAndSafe() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "motorway"); + EdgeIntAccess edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + int edgeId = 0; + accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertFalse(wheelchairAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(wheelchairAccessEnc.getBool(true, edgeId, edgeIntAccess)); + + way.setTag("sidewalk", "yes"); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); + speedParser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertTrue(wheelchairAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertTrue(wheelchairAccessEnc.getBool(true, edgeId, edgeIntAccess)); + assertEquals(5, wheelchairAvSpeedEnc.getDecimal(false, edgeId, edgeIntAccess), .1); + + way.clearTags(); + way.setTag("highway", "track"); + edgeIntAccess = new ArrayEdgeIntAccess(encodingManager.getIntsForFlags()); + accessParser.handleWayTags(edgeId, edgeIntAccess, way, null); + assertFalse(wheelchairAccessEnc.getBool(false, edgeId, edgeIntAccess)); + assertFalse(wheelchairAccessEnc.getBool(true, edgeId, edgeIntAccess)); + } + + @Test + public void testPriority() { + ReaderWay way = new ReaderWay(1); + way.setTag("highway", "cycleway"); + assertEquals(PriorityCode.UNCHANGED.getValue(), prioParser.handlePriority(way, null)); + way.setTag("highway", "primary"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + way.setTag("highway", "secondary"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.setTag("highway", "track"); + way.setTag("bicycle", "official"); + assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.setTag("highway", "track"); + way.setTag("bicycle", "designated"); + assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.setTag("highway", "cycleway"); + way.setTag("bicycle", "designated"); + way.setTag("foot", "designated"); + assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "primary"); + way.setTag("sidewalk", "yes"); + assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "cycleway"); + way.setTag("sidewalk", "no"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "road"); + way.setTag("bicycle", "official"); + way.setTag("sidewalk", "no"); + assertEquals(PriorityCode.SLIGHT_AVOID.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "trunk"); + way.setTag("sidewalk", "no"); + assertEquals(PriorityCode.VERY_BAD.getValue(), prioParser.handlePriority(way, null)); + way.setTag("sidewalk", "none"); + assertEquals(PriorityCode.VERY_BAD.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "residential"); + way.setTag("sidewalk", "yes"); + assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "footway"); + assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); + way.setTag("wheelchair", "designated"); + assertEquals(PriorityCode.VERY_NICE.getValue(), prioParser.handlePriority(way, null)); + + way.clearTags(); + way.setTag("highway", "footway"); + assertEquals(PriorityCode.PREFER.getValue(), prioParser.handlePriority(way, null)); + way.setTag("wheelchair", "limited"); + assertEquals(PriorityCode.AVOID.getValue(), prioParser.handlePriority(way, null)); + } + + @Test + public void testBarrierAccess() { + // by default allow access through the gate for bike & foot! + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "yes"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "no"); + // barrier! + assertTrue(accessParser.isBarrier(node)); + + node.setTag("bicycle", "yes"); + // no barrier!? + // assertTrue(wheelchairEncoder.handleNodeTags(node) == false); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "no"); + node.setTag("foot", "yes"); + // no barrier! + assertFalse(accessParser.isBarrier(node)); + + node.setTag("locked", "yes"); + // barrier! + assertTrue(accessParser.isBarrier(node)); + } + + @Test + public void testBlockByDefault() { + ReaderNode node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + // passByDefaultBarriers are no barrier by default + assertFalse(accessParser.isBarrier(node)); + node.setTag("access", "no"); + assertTrue(accessParser.isBarrier(node)); + + // these barriers block + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "fence"); + assertTrue(accessParser.isBarrier(node)); + node.setTag("barrier", "wall"); + assertTrue(accessParser.isBarrier(node)); + node.setTag("barrier", "handrail"); + assertTrue(accessParser.isBarrier(node)); + node.setTag("barrier", "turnstile"); + assertTrue(accessParser.isBarrier(node)); + // Explictly allowed access is allowed + node.setTag("barrier", "fence"); + node.setTag("access", "yes"); + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "gate"); + node.setTag("access", "yes"); + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "kerb"); + assertFalse(accessParser.isBarrier(node)); + node.setTag("wheelchair", "yes"); + assertFalse(accessParser.isBarrier(node)); + + node = new ReaderNode(1, -1, -1); + node.setTag("barrier", "fence"); + assertTrue(accessParser.isBarrier(node)); + } + + @Test + public void testSurfaces() { + ReaderWay way = new ReaderWay(1); + + way.setTag("highway", "footway"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("surface", "cobblestone"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("surface", "sand"); + assertTrue(accessParser.getAccess(way).canSkip()); + way.setTag("surface", "gravel"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("surface", "asphalt"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.clearTags(); + way.setTag("highway", "service"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("surface", "sand"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("sidewalk", "left"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("sidewalk:left:surface", "cobblestone"); + assertTrue(accessParser.getAccess(way).canSkip()); + } + + @Test + public void testSmoothness() { + ReaderWay way = new ReaderWay(1); + + way.setTag("highway", "residential"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("smoothness", "bad"); + assertTrue(accessParser.getAccess(way).canSkip()); + + way.setTag("sidewalk", "both"); + assertTrue(accessParser.getAccess(way).isWay()); + + way.setTag("sidewalk:both:smoothness", "horrible"); + assertTrue(accessParser.getAccess(way).canSkip()); + } + + @Test + public void testApplyWayTags() { + BaseGraph graph = new BaseGraph.Builder(encodingManager).set3D(true).create(); + NodeAccess na = graph.getNodeAccess(); + // incline of 5% over all + na.setNode(0, 51.1, 12.0010, 50); + na.setNode(1, 51.1, 12.0015, 55); + EdgeIteratorState edge01 = graph.edge(0, 1).setWayGeometry(Helper.createPointList3D(51.1, 12.0011, 49, 51.1, 12.0015, 55)); + edge01.setDistance(100); + GHUtility.setSpeed(5, 5, wheelchairAccessEnc, wheelchairAvSpeedEnc, edge01); + + // incline of 10% & shorter edge + na.setNode(2, 51.2, 12.1010, 50); + na.setNode(3, 51.2, 12.1015, 60); + EdgeIteratorState edge23 = graph.edge(2, 3).setWayGeometry(Helper.createPointList3D(51.2, 12.1011, 49, 51.2, 12.1015, 55)); + edge23.setDistance(30); + GHUtility.setSpeed(5, 5, wheelchairAccessEnc, wheelchairAvSpeedEnc, edge23); + + // incline of 10% & longer edge + na.setNode(4, 51.2, 12.101, 50); + na.setNode(5, 51.2, 12.102, 60); + EdgeIteratorState edge45 = graph.edge(2, 3).setWayGeometry(Helper.createPointList3D(51.2, 12.1011, 49, 51.2, 12.1015, 55)); + edge45.setDistance(100); + GHUtility.setSpeed(5, 5, wheelchairAccessEnc, wheelchairAvSpeedEnc, edge45); + + + ReaderWay way1 = new ReaderWay(1); + way1.setTag("point_list", edge01.fetchWayGeometry(FetchMode.ALL)); + way1.setTag("edge_distance", edge01.getDistance()); + EdgeIntAccess edgeIntAccess = graph.createEdgeIntAccess(); + speedParser.applyWayTags(way1, edge01.getEdge(), edgeIntAccess); + + assertTrue(edge01.get(wheelchairAccessEnc)); + assertTrue(edge01.getReverse(wheelchairAccessEnc)); + assertEquals(2, edge01.get(wheelchairAvSpeedEnc), 0); + assertEquals(2, edge01.get(speedParser.getAverageSpeedEnc()), 0); + assertEquals(5, edge01.getReverse(speedParser.getAverageSpeedEnc()), 0); + + ReaderWay way2 = new ReaderWay(2); + way2.setTag("point_list", edge23.fetchWayGeometry(FetchMode.ALL)); + way2.setTag("edge_distance", edge23.getDistance()); + speedParser.applyWayTags(way2, edge23.getEdge(), edgeIntAccess); + + assertTrue(edge23.get(wheelchairAccessEnc)); + assertTrue(edge23.getReverse(wheelchairAccessEnc)); + + assertEquals(2, edge23.get(speedParser.getAverageSpeedEnc()), 0); + assertEquals(2, edge23.getReverse(speedParser.getAverageSpeedEnc()), 0); + + // only exclude longer edges with too large incline: + ReaderWay way3 = new ReaderWay(3); + way3.setTag("point_list", edge45.fetchWayGeometry(FetchMode.ALL)); + way3.setTag("edge_distance", edge45.getDistance()); + speedParser.handleWayTags(edge45.getEdge(), edgeIntAccess, way3, null); + speedParser.applyWayTags(way3, edge45.getEdge(), edgeIntAccess); + + assertEquals(0, edge45.get(wheelchairAvSpeedEnc), 0.1); + assertEquals(0, edge45.getReverse(wheelchairAvSpeedEnc), 0.1); + } +} diff --git a/core/src/test/java/com/graphhopper/routing/weighting/BlockAreaWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/BlockAreaWeightingTest.java deleted file mode 100644 index 0c1330efadb..00000000000 --- a/core/src/test/java/com/graphhopper/routing/weighting/BlockAreaWeightingTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.graphhopper.routing.weighting; - -import com.graphhopper.coll.GHIntHashSet; -import com.graphhopper.routing.querygraph.QueryGraph; -import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; -import com.graphhopper.storage.BaseGraph; -import com.graphhopper.storage.GraphEdgeIdFinder; -import com.graphhopper.storage.index.LocationIndex; -import com.graphhopper.storage.index.LocationIndexTree; -import com.graphhopper.storage.index.Snap; -import com.graphhopper.util.EdgeIterator; -import com.graphhopper.util.EdgeIteratorState; -import com.graphhopper.util.GHUtility; -import com.graphhopper.util.shapes.Circle; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static com.graphhopper.util.GHUtility.updateDistancesFor; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class BlockAreaWeightingTest { - - private FlagEncoder encoder = FlagEncoders.createCar(); - private EncodingManager em; - private BaseGraph graph; - - @BeforeEach - public void setUp() { - encoder = FlagEncoders.createCar(); - em = EncodingManager.create(encoder); - graph = new BaseGraph.Builder(em).create(); - // 0-1 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - updateDistancesFor(graph, 0, 0.00, 0.00); - updateDistancesFor(graph, 1, 0.01, 0.01); - } - - @Test - public void testBlockedById() { - GraphEdgeIdFinder.BlockArea bArea = new GraphEdgeIdFinder.BlockArea(graph); - EdgeIteratorState edge = graph.getEdgeIteratorState(0, 1); - BlockAreaWeighting instance = new BlockAreaWeighting(new FastestWeighting(encoder), bArea); - assertEquals(94.35, instance.calcEdgeWeight(edge, false), .01); - - GHIntHashSet set = new GHIntHashSet(); - set.add(0); - bArea.add(null, set); - instance = new BlockAreaWeighting(new FastestWeighting(encoder), bArea); - assertEquals(Double.POSITIVE_INFINITY, instance.calcEdgeWeight(edge, false), .01); - } - - @Test - public void testBlockedByShape() { - EdgeIteratorState edge = graph.getEdgeIteratorState(0, 1); - GraphEdgeIdFinder.BlockArea bArea = new GraphEdgeIdFinder.BlockArea(graph); - BlockAreaWeighting instance = new BlockAreaWeighting(new FastestWeighting(encoder), bArea); - assertEquals(94.35, instance.calcEdgeWeight(edge, false), 0.01); - - bArea.add(new Circle(0.01, 0.01, 100)); - assertEquals(Double.POSITIVE_INFINITY, instance.calcEdgeWeight(edge, false), .01); - - bArea = new GraphEdgeIdFinder.BlockArea(graph); - instance = new BlockAreaWeighting(new FastestWeighting(encoder), bArea); - // Do not match 1,1 of edge - bArea.add(new Circle(0.1, 0.1, 100)); - assertEquals(94.35, instance.calcEdgeWeight(edge, false), .01); - } - - @Test - public void testBlockVirtualEdges_QueryGraph() { - GraphEdgeIdFinder.BlockArea bArea = new GraphEdgeIdFinder.BlockArea(graph); - // add base graph edge to fill caches and trigger edgeId cache search (without virtual edges) - GHIntHashSet set = new GHIntHashSet(); - set.add(0); - bArea.add(new Circle(0.0025, 0.0025, 1), set); - - LocationIndex index = new LocationIndexTree(graph, graph.getDirectory()).prepareIndex(); - Snap snap = index.findClosest(0.005, 0.005, EdgeFilter.ALL_EDGES); - QueryGraph queryGraph = QueryGraph.create(graph, snap); - - BlockAreaWeighting instance = new BlockAreaWeighting(new FastestWeighting(encoder), bArea); - EdgeIterator iter = queryGraph.createEdgeExplorer().setBaseNode(snap.getClosestNode()); - int blockedEdges = 0, totalEdges = 0; - while (iter.next()) { - if (Double.isInfinite(instance.calcEdgeWeight(iter, false))) - blockedEdges++; - totalEdges++; - } - assertEquals(1, blockedEdges); - assertEquals(2, totalEdges); - } -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java index b853abe44c8..7cf2d31894e 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/FastestWeightingTest.java @@ -18,22 +18,17 @@ package com.graphhopper.routing.weighting; import com.graphhopper.reader.ReaderWay; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadAccess; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.VirtualEdgeIteratorState; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; -import com.graphhopper.storage.IntsRef; import com.graphhopper.util.*; import com.graphhopper.util.Parameters.Routing; import org.junit.jupiter.api.Test; -import static com.graphhopper.util.GHUtility.createMockedEdgeIteratorState; +import static com.graphhopper.routing.weighting.FastestWeighting.DESTINATION_FACTOR; +import static com.graphhopper.routing.weighting.FastestWeighting.PRIVATE_FACTOR; import static com.graphhopper.util.GHUtility.getEdge; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -41,22 +36,27 @@ * @author Peter Karich */ public class FastestWeightingTest { - private final FlagEncoder encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", 10)); - private final EncodingManager encodingManager = EncodingManager.create(encoder); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final DecimalEncodedValue turnCostEnc = TurnCost.create("car", 10); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); @Test public void testMinWeightHasSameUnitAs_getWeight() { - Weighting instance = new FastestWeighting(encoder); - IntsRef flags = GHUtility.setSpeed(encoder.getMaxSpeed(), 0, encoder, encodingManager.createEdgeFlags()); - assertEquals(instance.getMinWeight(10), instance.calcEdgeWeight(createMockedEdgeIteratorState(10, flags), false), 1e-8); + EdgeIteratorState edge = graph.edge(0, 1).setDistance(10); + GHUtility.setSpeed(140, 0, accessEnc, speedEnc, edge); + Weighting instance = new FastestWeighting(accessEnc, speedEnc); + assertEquals(instance.getMinWeight(10), instance.calcEdgeWeight(edge, false), 1e-8); } @Test public void testWeightWrongHeading() { - Weighting instance = new FastestWeighting(encoder, new PMap().putObject(Parameters.Routing.HEADING_PENALTY, 100)); - - VirtualEdgeIteratorState virtEdge = new VirtualEdgeIteratorState(0, GHUtility.createEdgeKey(1, false), 1, 2, 10, - GHUtility.setSpeed(10, 0, encoder, encodingManager.createEdgeFlags()), "test", Helper.createPointList(51, 0, 51, 1), false); + Weighting instance = new FastestWeighting(accessEnc, speedEnc, null, new PMap().putObject(Parameters.Routing.HEADING_PENALTY, 100), TurnCostProvider.NO_TURN_COST_PROVIDER); + EdgeIteratorState edge = graph.edge(1, 2).setDistance(10).setWayGeometry(Helper.createPointList(51, 0, 51, 1)); + GHUtility.setSpeed(10, 0, accessEnc, speedEnc, edge); + VirtualEdgeIteratorState virtEdge = new VirtualEdgeIteratorState(edge.getEdgeKey(), 99, 5, 6, edge.getDistance(), edge.getFlags(), + edge.getKeyValues(), edge.fetchWayGeometry(FetchMode.PILLAR_ONLY), false); double time = instance.calcEdgeWeight(virtEdge, false); virtEdge.setUnfavored(true); @@ -71,45 +71,41 @@ public void testWeightWrongHeading() { // test default penalty virtEdge.setUnfavored(true); - instance = new FastestWeighting(encoder); + instance = new FastestWeighting(accessEnc, speedEnc); assertEquals(time + Routing.DEFAULT_HEADING_PENALTY, instance.calcEdgeWeight(virtEdge, false), 1e-8); } @Test public void testSpeed0() { - Weighting instance = new FastestWeighting(encoder); - IntsRef edgeFlags = encodingManager.createEdgeFlags(); - encoder.getAverageSpeedEnc().setDecimal(false, edgeFlags, 0); - assertEquals(1.0 / 0, instance.calcEdgeWeight(createMockedEdgeIteratorState(10, edgeFlags), false), 1e-8); + EdgeIteratorState edge = graph.edge(0, 1).setDistance(10); + Weighting instance = new FastestWeighting(accessEnc, speedEnc); + edge.set(speedEnc, 0); + assertEquals(1.0 / 0, instance.calcEdgeWeight(edge, false), 1e-8); // 0 / 0 returns NaN but calcWeight should not return NaN! - assertEquals(1.0 / 0, instance.calcEdgeWeight(createMockedEdgeIteratorState(0, edgeFlags), false), 1e-8); + edge.setDistance(0); + assertEquals(1.0 / 0, instance.calcEdgeWeight(edge, false), 1e-8); } @Test public void testTime() { - FlagEncoder tmpEnc = FlagEncoders.createBike2(); - EncodingManager em = EncodingManager.create(tmpEnc); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, true); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph g = new BaseGraph.Builder(em).create(); - Weighting w = new FastestWeighting(tmpEnc); - - IntsRef edgeFlags = GHUtility.setSpeed(15, 15, tmpEnc, em.createEdgeFlags()); - tmpEnc.getAverageSpeedEnc().setDecimal(true, edgeFlags, 10.0); - - EdgeIteratorState edge = GHUtility.createMockedEdgeIteratorState(100000, edgeFlags); - + Weighting w = new FastestWeighting(accessEnc, speedEnc); + EdgeIteratorState edge = g.edge(0, 1).setDistance(100_000); + GHUtility.setSpeed(15, 10, accessEnc, speedEnc, edge); assertEquals(375 * 60 * 1000, w.calcEdgeMillis(edge, false)); assertEquals(600 * 60 * 1000, w.calcEdgeMillis(edge, true)); - - g.close(); } @Test public void calcWeightAndTime_withTurnCosts() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); - Weighting weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage())); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(100)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(100)); + BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); // turn costs are given in seconds setTurnCost(graph, 0, 1, 2, 5); assertEquals(6 + 5, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); @@ -118,19 +114,19 @@ public void calcWeightAndTime_withTurnCosts() { @Test public void calcWeightAndTime_uTurnCosts() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); - Weighting weighting = new FastestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage(), 40)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(100)); + BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); + Weighting weighting = new FastestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage(), 40)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); assertEquals(6 + 40, GHUtility.calcWeightWithTurnWeight(weighting, edge, false, 0), 1.e-6); assertEquals((6 + 40) * 1000, GHUtility.calcMillisWithTurnMillis(weighting, edge, false, 0), 1.e-6); } @Test public void calcWeightAndTime_withTurnCosts_shortest() { - BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); - Weighting weighting = new ShortestWeighting(encoder, new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage())); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(100)); - EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(100)); + BaseGraph graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); + Weighting weighting = new ShortestWeighting(accessEnc, speedEnc, new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage())); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1).setDistance(100)); + EdgeIteratorState edge = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2).setDistance(100)); // turn costs are given in seconds setTurnCost(graph, 0, 1, 2, 5); // todo: for the shortest weighting turn costs cannot be interpreted as seconds? at least when they are added @@ -141,27 +137,29 @@ public void calcWeightAndTime_withTurnCosts_shortest() { @Test public void testDestinationTag() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - FlagEncoder bikeEncoder = FlagEncoders.createBike(); - EncodingManager em = EncodingManager.create(carEncoder, bikeEncoder); + BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); + DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); + EncodingManager em = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).add(bikeAccessEnc).add(bikeSpeedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); EdgeIteratorState edge = graph.edge(0, 1).setDistance(1000); - for (FlagEncoder encoder : em.fetchEdgeEncoders()) { - edge.set(encoder.getAccessEnc(), true); - edge.setReverse(encoder.getAccessEnc(), true); - } - edge.set(carEncoder.getAverageSpeedEnc(), 60); - edge.set(bikeEncoder.getAverageSpeedEnc(), 18); + edge.set(carAccessEnc, true, true); + edge.set(bikeAccessEnc, true, true); + edge.set(carSpeedEnc, 60); + edge.set(bikeSpeedEnc, 18); EnumEncodedValue roadAccessEnc = em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); - FastestWeighting weighting = new FastestWeighting(carEncoder); - FastestWeighting bikeWeighting = new FastestWeighting(bikeEncoder); + FastestWeighting weighting = new FastestWeighting(carAccessEnc, carSpeedEnc, roadAccessEnc, + new PMap().putObject(DESTINATION_FACTOR, 10), TurnCostProvider.NO_TURN_COST_PROVIDER); + FastestWeighting bikeWeighting = new FastestWeighting(bikeAccessEnc, bikeSpeedEnc, roadAccessEnc, + new PMap().putObject(DESTINATION_FACTOR, 1), TurnCostProvider.NO_TURN_COST_PROVIDER); edge.set(roadAccessEnc, RoadAccess.YES); assertEquals(60, weighting.calcEdgeWeight(edge, false), 1.e-6); assertEquals(200, bikeWeighting.calcEdgeWeight(edge, false), 1.e-6); - // the destination tag does not change the weight for bikes! + // the destination tag does not change the weight for the bike weighting edge.set(roadAccessEnc, RoadAccess.DESTINATION); assertEquals(600, weighting.calcEdgeWeight(edge, false), 0.1); assertEquals(200, bikeWeighting.calcEdgeWeight(edge, false), 0.1); @@ -169,21 +167,23 @@ public void testDestinationTag() { @Test public void testPrivateTag() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - FlagEncoder bikeEncoder = FlagEncoders.createBike(); - EncodingManager em = EncodingManager.create(carEncoder, bikeEncoder); + BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); + DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); + EncodingManager em = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).add(bikeAccessEnc).add(bikeSpeedEnc).build(); BaseGraph graph = new BaseGraph.Builder(em).create(); EdgeIteratorState edge = graph.edge(0, 1).setDistance(1000); - for (FlagEncoder encoder : em.fetchEdgeEncoders()) { - edge.set(encoder.getAccessEnc(), true); - edge.setReverse(encoder.getAccessEnc(), true); - } - edge.set(carEncoder.getAverageSpeedEnc(), 60); - edge.set(bikeEncoder.getAverageSpeedEnc(), 18); + edge.set(carAccessEnc, true, true); + edge.set(bikeAccessEnc, true, true); + edge.set(carSpeedEnc, 60); + edge.set(bikeSpeedEnc, 18); EnumEncodedValue roadAccessEnc = em.getEnumEncodedValue(RoadAccess.KEY, RoadAccess.class); - FastestWeighting weighting = new FastestWeighting(carEncoder); - FastestWeighting bikeWeighting = new FastestWeighting(bikeEncoder); + FastestWeighting weighting = new FastestWeighting(carAccessEnc, carSpeedEnc, roadAccessEnc, + new PMap().putObject(PRIVATE_FACTOR, 10), TurnCostProvider.NO_TURN_COST_PROVIDER); + FastestWeighting bikeWeighting = new FastestWeighting(bikeAccessEnc, bikeSpeedEnc, roadAccessEnc, + new PMap().putObject(PRIVATE_FACTOR, 1.2), TurnCostProvider.NO_TURN_COST_PROVIDER); ReaderWay way = new ReaderWay(1); way.setTag("highway", "secondary"); @@ -199,7 +199,7 @@ public void testPrivateTag() { } private void setTurnCost(Graph graph, int from, int via, int to, double turnCost) { - graph.getTurnCostStorage().set(((EncodedValueLookup) encodingManager).getDecimalEncodedValue(TurnCost.key(encoder.toString())), getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost); + graph.getTurnCostStorage().set(turnCostEnc, getEdge(graph, from, via).getEdge(), via, getEdge(graph, via, to).getEdge(), turnCost); } } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/ShortFastestWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/ShortFastestWeightingTest.java index ca31be0906f..a4a4cc6cda8 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/ShortFastestWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/ShortFastestWeightingTest.java @@ -17,42 +17,44 @@ */ package com.graphhopper.routing.weighting; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.storage.BaseGraph; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; -import static com.graphhopper.util.GHUtility.createMockedEdgeIteratorState; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @author Peter Karich */ public class ShortFastestWeightingTest { - EncodingManager encodingManager = EncodingManager.create("car"); - private final FlagEncoder encoder = encodingManager.getEncoder("car"); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); + private final BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); @Test public void testShort() { - EdgeIteratorState edge = createMockedEdgeIteratorState(10, GHUtility.setSpeed(50, 0, encoder, encodingManager.createEdgeFlags())); - Weighting instance = new ShortFastestWeighting(encoder, 0.03); - assertEquals(1.02, instance.calcEdgeWeight(edge, false), 1e-8); + EdgeIteratorState edge = graph.edge(0, 1).setDistance(10); + GHUtility.setSpeed(50, 0, accessEnc, speedEnc, edge); + Weighting instance = new ShortFastestWeighting(accessEnc, speedEnc, null, new PMap("short_fastest.distance_factor=0.03"), TurnCostProvider.NO_TURN_COST_PROVIDER); + assertEquals(1.02, instance.calcEdgeWeight(edge, false), 1e-6); // more influence from distance - instance = new ShortFastestWeighting(encoder, 0.1); - assertEquals(1.72, instance.calcEdgeWeight(edge, false), 1e-8); + instance = new ShortFastestWeighting(accessEnc, speedEnc, null, new PMap("short_fastest.distance_factor=0.1"), TurnCostProvider.NO_TURN_COST_PROVIDER); + assertEquals(1.72, instance.calcEdgeWeight(edge, false), 1e-6); } @Test public void testTooSmall() { - try { - new ShortFastestWeighting(encoder, new PMap("short_fastest.distance_factor=0|short_fastest.time_factor=0"), - TurnCostProvider.NO_TURN_COST_PROVIDER); - fail(); - } catch (Exception ex) { - } + assertThrows(Exception.class, () -> new ShortFastestWeighting(accessEnc, speedEnc, null, new PMap("short_fastest.distance_factor=0|short_fastest.time_factor=0"), + TurnCostProvider.NO_TURN_COST_PROVIDER)); } } diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitorTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitorTest.java new file mode 100644 index 00000000000..4d2b23ef30c --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/ConditionalExpressionVisitorTest.java @@ -0,0 +1,135 @@ +package com.graphhopper.routing.weighting.custom; + +import com.graphhopper.routing.ev.ArrayEdgeIntAccess; +import com.graphhopper.routing.ev.StringEncodedValue; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.util.Helper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static com.graphhopper.routing.weighting.custom.ConditionalExpressionVisitor.parse; +import static org.junit.jupiter.api.Assertions.*; + +public class ConditionalExpressionVisitorTest { + + @BeforeEach + public void before() { + StringEncodedValue sev = new StringEncodedValue("country", 10); + new EncodingManager.Builder().add(sev).build(); + sev.setString(false, 0, new ArrayEdgeIntAccess(1), "DEU"); + } + + @Test + public void protectUsFromStuff() { + NameValidator allNamesInvalid = s -> false; + for (String toParse : Arrays.asList( + "", + "new Object()", + "java.lang.Object", + "Test.class", + "new Object(){}.toString().length", + "{ 5}", + "{ 5, 7 }", + "Object.class", + "System.out.println(\"\")", + "something.newInstance()", + "e.getClass ( )", + "edge.getDistance()*7/*test", + "edge.getDistance()//*test", + "edge . getClass()", + "(edge = edge) == edge", + ") edge (", + "in(area_blup(), edge)", + "s -> truevalue")) { + ParseResult res = parse(toParse, allNamesInvalid); + assertFalse(res.ok, "should not be simple condition: " + toParse); + assertTrue(res.guessedVariables == null || res.guessedVariables.isEmpty()); + } + + assertFalse(parse("edge; getClass()", allNamesInvalid).ok); + } + + @Test + public void testConvertExpression() { + NameValidator validVariable = s -> Helper.toUpperCase(s).equals(s) || s.equals("road_class") || s.equals("toll"); + + ParseResult result = parse("toll == NO", validVariable); + assertTrue(result.ok); + assertEquals("[toll]", result.guessedVariables.toString()); + + assertEquals("road_class == RoadClass.PRIMARY", + parse("road_class == PRIMARY", validVariable).converted.toString()); + assertEquals("toll == Toll.NO", parse("toll == NO", validVariable).converted.toString()); + assertEquals("toll == Toll.NO || road_class == RoadClass.NO", parse("toll == NO || road_class == NO", validVariable).converted.toString()); + + // convert in_area variable to function call: + assertEquals(CustomWeightingHelper.class.getSimpleName() + ".in(this.in_custom_1, edge)", + parse("in_custom_1", validVariable).converted.toString()); + + // no need to inject: + assertNull(parse("toll == Toll.NO", validVariable).converted); + } + + @Test + public void isValidAndSimpleCondition() { + NameValidator validVariable = s -> Helper.toUpperCase(s).equals(s) || s.equals("road_class") || s.equals("toll") || s.equals("my_speed") || s.equals("backward_my_speed"); + + ParseResult result = parse("in_something", validVariable); + assertTrue(result.ok); + assertEquals("[in_something]", result.guessedVariables.toString()); + + result = parse("edge == edge", validVariable); + assertFalse(result.ok); + + result = parse("Math.sqrt(my_speed)", validVariable); + assertTrue(result.ok); + assertEquals("[my_speed]", result.guessedVariables.toString()); + + result = parse("Math.sqrt(2)", validVariable); + assertTrue(result.ok); + assertTrue(result.guessedVariables.isEmpty()); + + result = parse("edge.blup()", validVariable); + assertFalse(result.ok); + assertTrue(result.guessedVariables.isEmpty()); + + result = parse("edge.getDistance()", validVariable); + assertTrue(result.ok); + assertEquals("[edge]", result.guessedVariables.toString()); + assertFalse(parse("road_class == PRIMARY", s -> false).ok); + result = parse("road_class == PRIMARY", validVariable); + assertTrue(result.ok); + assertEquals("[road_class]", result.guessedVariables.toString()); + + result = parse("toll == Toll.NO", validVariable); + assertFalse(result.ok); + assertEquals("[toll]", result.guessedVariables.toString()); + + assertTrue(parse("road_class.ordinal()*2 == PRIMARY.ordinal()*2", validVariable).ok); + assertTrue(parse("Math.sqrt(road_class.ordinal()) > 1", validVariable).ok); + + result = parse("(toll == NO || road_class == PRIMARY) && toll == NO", validVariable); + assertTrue(result.ok); + assertEquals("[toll, road_class]", result.guessedVariables.toString()); + + result = parse("backward_my_speed", validVariable); + assertTrue(result.ok); + assertEquals("[backward_my_speed]", result.guessedVariables.toString()); + } + + @Test + public void testNegativeConstant() { + ParseResult result = parse("average_slope < -0.5", "average_slope"::equals); + assertTrue(result.ok); + assertEquals("[average_slope]", result.guessedVariables.toString()); + result = parse("-average_slope > -0.5", "average_slope"::equals); + assertTrue(result.ok); + assertEquals("[average_slope]", result.guessedVariables.toString()); + + result = parse("Math.sqrt(-2)", (var) -> false); + assertTrue(result.ok); + assertTrue(result.guessedVariables.isEmpty()); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java index 5e942283bcb..1ee9433db5d 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomModelParserTest.java @@ -20,51 +20,54 @@ import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.storage.BaseGraph; import com.graphhopper.util.CustomModel; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.JsonFeature; +import com.graphhopper.util.JsonFeatureCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; +import java.util.Arrays; import java.util.HashMap; -import java.util.Map; +import java.util.HashSet; import static com.graphhopper.json.Statement.*; import static com.graphhopper.json.Statement.Op.LIMIT; import static com.graphhopper.json.Statement.Op.MULTIPLY; import static com.graphhopper.routing.ev.RoadClass.*; +import static com.graphhopper.routing.weighting.custom.CustomModelParser.parseExpressions; import static org.junit.jupiter.api.Assertions.*; class CustomModelParserTest { - - FlagEncoder encoder; BaseGraph graph; EncodingManager encodingManager; EnumEncodedValue roadClassEnc; + BooleanEncodedValue accessEnc; DecimalEncodedValue avgSpeedEnc; StringEncodedValue countryEnc; + double maxSpeed; @BeforeEach void setup() { - encoder = FlagEncoders.createCar(); + accessEnc = VehicleAccess.create("car"); + avgSpeedEnc = VehicleSpeed.create("car", 5, 5, false); countryEnc = new StringEncodedValue("country", 10); - encodingManager = new EncodingManager.Builder().add(encoder).add(countryEnc).add(new EnumEncodedValue<>(Surface.KEY, Surface.class)).build(); + encodingManager = new EncodingManager.Builder().add(accessEnc).add(avgSpeedEnc) + .add(countryEnc).add(MaxSpeed.create()).add(Surface.create()).build(); graph = new BaseGraph.Builder(encodingManager).create(); - avgSpeedEnc = encoder.getAverageSpeedEnc(); roadClassEnc = encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); + maxSpeed = 140; } @Test void setPriorityForRoadClass() { CustomModel customModel = new CustomModel(); - customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, 0.5)); + customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.5")); CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null).getEdgeToPriorityMapping(); + avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); EdgeIteratorState edge1 = graph.edge(0, 1).setDistance(100).set(roadClassEnc, RoadClass.PRIMARY); @@ -77,20 +80,20 @@ void setPriorityForRoadClass() { @Test void testPriority() { EdgeIteratorState primary = graph.edge(0, 1).setDistance(10). - set(roadClassEnc, PRIMARY).set(avgSpeedEnc, 80).set(encoder.getAccessEnc(), true, true); + set(roadClassEnc, PRIMARY).set(avgSpeedEnc, 80).set(accessEnc, true, true); EdgeIteratorState secondary = graph.edge(1, 2).setDistance(10). - set(roadClassEnc, SECONDARY).set(avgSpeedEnc, 70).set(encoder.getAccessEnc(), true, true); + set(roadClassEnc, SECONDARY).set(avgSpeedEnc, 70).set(accessEnc, true, true); EdgeIteratorState tertiary = graph.edge(1, 2).setDistance(10). - set(roadClassEnc, TERTIARY).set(avgSpeedEnc, 70).set(encoder.getAccessEnc(), true, true); + set(roadClassEnc, TERTIARY).set(avgSpeedEnc, 70).set(accessEnc, true, true); CustomModel customModel = new CustomModel(); - customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, 0.5)); - customModel.addToPriority(ElseIf("road_class == SECONDARY", MULTIPLY, 0.7)); - customModel.addToPriority(Else(MULTIPLY, 0.9)); - customModel.addToPriority(If("road_environment != FERRY", MULTIPLY, 0.8)); + customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.5")); + customModel.addToPriority(ElseIf("road_class == SECONDARY", MULTIPLY, "0.7")); + customModel.addToPriority(Else(MULTIPLY, "0.9")); + customModel.addToPriority(If("road_environment != FERRY", MULTIPLY, "0.8")); CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null).getEdgeToPriorityMapping(); + avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); assertEquals(0.5 * 0.8, priorityMapping.get(primary, false), 0.01); assertEquals(0.7 * 0.8, priorityMapping.get(secondary, false), 0.01); @@ -98,25 +101,25 @@ void testPriority() { // force integer value customModel = new CustomModel(); - customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, 1)); - customModel.addToPriority(If("road_class == SECONDARY", MULTIPLY, 0.9)); + customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "1")); + customModel.addToPriority(If("road_class == SECONDARY", MULTIPLY, "0.9")); priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null).getEdgeToPriorityMapping(); + avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); assertEquals(1, priorityMapping.get(primary, false), 0.01); assertEquals(0.9, priorityMapping.get(secondary, false), 0.01); } @Test public void testBrackets() { - EdgeIteratorState primary = graph.edge(0, 1).setDistance(10).set(encoder.getAccessEnc(), true, true). + EdgeIteratorState primary = graph.edge(0, 1).setDistance(10).set(accessEnc, true, true). set(roadClassEnc, PRIMARY).set(avgSpeedEnc, 80); - EdgeIteratorState secondary = graph.edge(0, 1).setDistance(10).set(encoder.getAccessEnc(), true, true). + EdgeIteratorState secondary = graph.edge(0, 1).setDistance(10).set(accessEnc, true, true). set(roadClassEnc, SECONDARY).set(avgSpeedEnc, 40); CustomModel customModel = new CustomModel(); - customModel.addToPriority(If("(road_class == PRIMARY || car$access == true) && car$average_speed > 50", MULTIPLY, 0.9)); + customModel.addToPriority(If("(road_class == PRIMARY || car_access == true) && car_average_speed > 50", MULTIPLY, "0.9")); CustomWeighting.Parameters parameters = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null); + avgSpeedEnc, maxSpeed, null); assertEquals(0.9, parameters.getEdgeToPriorityMapping().get(primary, false), 0.01); assertEquals(1, parameters.getEdgeToPriorityMapping().get(secondary, false), 0.01); } @@ -124,64 +127,47 @@ public void testBrackets() { @Test public void testSpeedFactorAndPriorityAndMaxSpeed() { EdgeIteratorState primary = graph.edge(0, 1).setDistance(10). - set(roadClassEnc, PRIMARY).set(avgSpeedEnc, 80).set(encoder.getAccessEnc(), true, true); + set(roadClassEnc, PRIMARY).set(avgSpeedEnc, 80).set(accessEnc, true, true); EdgeIteratorState secondary = graph.edge(1, 2).setDistance(10). - set(roadClassEnc, SECONDARY).set(avgSpeedEnc, 70).set(encoder.getAccessEnc(), true, true); + set(roadClassEnc, SECONDARY).set(avgSpeedEnc, 70).set(accessEnc, true, true); CustomModel customModel = new CustomModel(); - customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, 0.9)); - customModel.addToSpeed(If("road_class == PRIMARY", MULTIPLY, 0.8)); + customModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.9")); + customModel.addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.8")); CustomWeighting.Parameters parameters = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null); + avgSpeedEnc, maxSpeed, null); assertEquals(0.9, parameters.getEdgeToPriorityMapping().get(primary, false), 0.01); assertEquals(64, parameters.getEdgeToSpeedMapping().get(primary, false), 0.01); assertEquals(1, parameters.getEdgeToPriorityMapping().get(secondary, false), 0.01); assertEquals(70, parameters.getEdgeToSpeedMapping().get(secondary, false), 0.01); - customModel.addToSpeed(If("road_class != PRIMARY", LIMIT, 50)); + customModel.addToSpeed(If("road_class != PRIMARY", LIMIT, "50")); CustomWeighting.EdgeToDoubleMapping speedMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null).getEdgeToSpeedMapping(); + avgSpeedEnc, maxSpeed, null).getEdgeToSpeedMapping(); assertEquals(64, speedMapping.get(primary, false), 0.01); assertEquals(50, speedMapping.get(secondary, false), 0.01); } - @Test - public void testString() { - EdgeIteratorState deu = graph.edge(0, 1).setDistance(10). - set(countryEnc, "DEU").set(avgSpeedEnc, 80).set(encoder.getAccessEnc(), true, true); - EdgeIteratorState blup = graph.edge(1, 2).setDistance(10). - set(countryEnc, "blup").set(avgSpeedEnc, 70).set(encoder.getAccessEnc(), true, true); - - CustomModel customModel = new CustomModel(); - customModel.addToPriority(If("country == \"DEU\"", MULTIPLY, 0.9)); - customModel.addToPriority(ElseIf("country == \"blup\"", MULTIPLY, 0.7)); - customModel.addToPriority(Else(MULTIPLY, 0.5)); - CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null).getEdgeToPriorityMapping(); - assertEquals(0.9, priorityMapping.get(deu, false), 0.01); - assertEquals(0.7, priorityMapping.get(blup, false), 0.01); - } - @Test void testIllegalOrder() { CustomModel customModel = new CustomModel(); - customModel.addToPriority(Else(MULTIPLY, 0.9)); - customModel.addToPriority(If("road_environment != FERRY", MULTIPLY, 0.8)); + customModel.addToPriority(Else(MULTIPLY, "0.9")); + customModel.addToPriority(If("road_environment != FERRY", MULTIPLY, "0.8")); assertThrows(IllegalArgumentException.class, () -> CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null)); + avgSpeedEnc, maxSpeed, null)); CustomModel customModel2 = new CustomModel(); - customModel2.addToPriority(ElseIf("road_environment != FERRY", MULTIPLY, 0.9)); - customModel2.addToPriority(If("road_class != PRIMARY", MULTIPLY, 0.8)); + customModel2.addToPriority(ElseIf("road_environment != FERRY", MULTIPLY, "0.9")); + customModel2.addToPriority(If("road_class != PRIMARY", MULTIPLY, "0.8")); assertThrows(IllegalArgumentException.class, () -> CustomModelParser.createWeightingParameters(customModel2, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null)); + avgSpeedEnc, maxSpeed, null)); } @Test public void multipleAreas() { CustomModel customModel = new CustomModel(); - Map areas = new HashMap<>(); + JsonFeatureCollection areas = new JsonFeatureCollection(); Coordinate[] area_1_coordinates = new Coordinate[]{ new Coordinate(48.019324184801185, 11.28021240234375), new Coordinate(48.019324184801185, 11.53564453125), @@ -196,37 +182,122 @@ public void multipleAreas() { new Coordinate(48.281365151571755, 11.53289794921875), new Coordinate(48.15509285476017, 11.53289794921875), }; - areas.put("area_1", new JsonFeature("area_1", + areas.getFeatures().add(new JsonFeature("area_1", "Feature", null, new GeometryFactory().createPolygon(area_1_coordinates), new HashMap<>())); - areas.put("area_2", new JsonFeature("area_2", + areas.getFeatures().add(new JsonFeature("area_2", "Feature", null, new GeometryFactory().createPolygon(area_2_coordinates), new HashMap<>())); customModel.setAreas(areas); - customModel.addToSpeed(If("in_area_1", LIMIT, 100)); - customModel.addToSpeed(If("!in_area_2", LIMIT, 25)); - customModel.addToSpeed(Else(LIMIT, 15)); + customModel.addToSpeed(If("in_area_1", LIMIT, "100")); + customModel.addToSpeed(If("!in_area_2", LIMIT, "25")); + customModel.addToSpeed(Else(LIMIT, "15")); // No exception is thrown during createWeightingParameters assertAll(() -> CustomModelParser.createWeightingParameters(customModel, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null)); + avgSpeedEnc, maxSpeed, null)); CustomModel customModel2 = new CustomModel(); customModel2.setAreas(areas); - customModel2.addToSpeed(If("in_area_1", LIMIT, 100)); - customModel2.addToSpeed(If("in_area_2", LIMIT, 25)); - customModel2.addToSpeed(If("in_area_3", LIMIT, 150)); - customModel2.addToSpeed(Else(LIMIT, 15)); + customModel2.addToSpeed(If("in_area_1", LIMIT, "100")); + customModel2.addToSpeed(If("in_area_2", LIMIT, "25")); + customModel2.addToSpeed(If("in_area_3", LIMIT, "150")); + customModel2.addToSpeed(Else(LIMIT, "15")); assertThrows(IllegalArgumentException.class, () -> CustomModelParser.createWeightingParameters(customModel2, encodingManager, - avgSpeedEnc, encoder.getMaxSpeed(), null)); + avgSpeedEnc, maxSpeed, null)); } + + @Test + public void parseValue() { + DecimalEncodedValue maxSpeedEnc = encodingManager.getDecimalEncodedValue(MaxSpeed.KEY); + EdgeIteratorState maxLower = graph.edge(0, 1).setDistance(10). + set(maxSpeedEnc, 60).set(avgSpeedEnc, 70).set(accessEnc, true, true); + EdgeIteratorState maxSame = graph.edge(1, 2).setDistance(10). + set(maxSpeedEnc, 70).set(avgSpeedEnc, 70).set(accessEnc, true, true); + + CustomModel customModel = new CustomModel(); + customModel.addToSpeed(If("true", LIMIT, "max_speed * 1.1")); + CustomWeighting.EdgeToDoubleMapping speedMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, + avgSpeedEnc, maxSpeed, null).getEdgeToSpeedMapping(); + assertEquals(70.0, speedMapping.get(maxSame, false), 0.01); + assertEquals(66.0, speedMapping.get(maxLower, false), 0.01); + } + + @Test + public void parseValueWithError() { + CustomModel customModel1 = new CustomModel(); + customModel1.addToSpeed(If("true", LIMIT, "unknown")); + + IllegalArgumentException ret = assertThrows(IllegalArgumentException.class, + () -> CustomModelParser.createWeightingParameters(customModel1, encodingManager, + avgSpeedEnc, maxSpeed, null)); + assertTrue(ret.getMessage().startsWith("Cannot compile expression: 'unknown' not available"), ret.getMessage()); + + CustomModel customModel2 = new CustomModel(); + customModel2.addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.5")); + customModel2.addToSpeed(Else(MULTIPLY, "-0.5")); + ret = assertThrows(IllegalArgumentException.class, + () -> CustomModelParser.createWeightingParameters(customModel2, encodingManager, + avgSpeedEnc, maxSpeed, null)); + assertTrue(ret.getMessage().startsWith("Cannot compile expression: speed has to be >=0 but can be negative (-0.5)"), ret.getMessage()); + + CustomModel customModel3 = new CustomModel(); + customModel3.addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.5")); + customModel3.addToSpeed(Else(MULTIPLY, "road_class")); + ret = assertThrows(IllegalArgumentException.class, + () -> CustomModelParser.createWeightingParameters(customModel3, encodingManager, + avgSpeedEnc, maxSpeed, null)); + assertTrue(ret.getMessage().contains("Binary numeric promotion not possible on types \"double\" and \"java.lang.Enum\""), ret.getMessage()); + } + + @Test + public void parseConditionWithError() { + NameValidator validVariable = s -> encodingManager.hasEncodedValue(s); + + // existing encoded value but not added + IllegalArgumentException ret = assertThrows(IllegalArgumentException.class, + () -> parseExpressions(new StringBuilder(), + validVariable, "[HERE]", new HashSet<>(), + Arrays.asList(If("max_weight > 10", MULTIPLY, "0")))); + assertTrue(ret.getMessage().startsWith("[HERE] invalid condition \"max_weight > 10\": 'max_weight' not available"), ret.getMessage()); + + // invalid variable or constant (NameValidator returns false) + ret = assertThrows(IllegalArgumentException.class, + () -> parseExpressions(new StringBuilder(), + validVariable, "[HERE]", new HashSet<>(), + Arrays.asList(If("country == GERMANY", MULTIPLY, "0")))); + assertTrue(ret.getMessage().startsWith("[HERE] invalid condition \"country == GERMANY\": 'GERMANY' not available"), ret.getMessage()); + + // not whitelisted method + ret = assertThrows(IllegalArgumentException.class, + () -> parseExpressions(new StringBuilder(), + validVariable, "[HERE]", new HashSet<>(), + Arrays.asList(If("edge.fetchWayGeometry().size() > 2", MULTIPLY, "0")))); + assertTrue(ret.getMessage().startsWith("[HERE] invalid condition \"edge.fetchWayGeometry().size() > 2\": size is an illegal method"), ret.getMessage()); + } + + @Test + void testBackwardFunction() { + CustomModel customModel = new CustomModel(); + customModel.addToPriority(If("backward_car_access != car_access", MULTIPLY, "0.5")); + CustomWeighting.EdgeToDoubleMapping priorityMapping = CustomModelParser.createWeightingParameters(customModel, encodingManager, + avgSpeedEnc, maxSpeed, null).getEdgeToPriorityMapping(); + + BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); + EdgeIteratorState edge1 = graph.edge(0, 1).setDistance(100).set(accessEnc, true, false); + EdgeIteratorState edge2 = graph.edge(1, 2).setDistance(100).set(accessEnc, true, true); + + assertEquals(0.5, priorityMapping.get(edge1, false), 1.e-6); + assertEquals(1.0, priorityMapping.get(edge2, false), 1.e-6); + } + } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelperTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelperTest.java new file mode 100644 index 00000000000..c032b43bb13 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingHelperTest.java @@ -0,0 +1,49 @@ +package com.graphhopper.routing.weighting.custom; + +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.FetchMode; +import com.graphhopper.util.Helper; +import com.graphhopper.util.shapes.Polygon; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class CustomWeightingHelperTest { + + @Test + public void testInRectangle() { + Polygon square = new Polygon(new double[]{0, 0, 20, 20}, new double[]{0, 20, 20, 0}); + assertTrue(square.isRectangle()); + + BaseGraph g = new BaseGraph.Builder(1).create(); + + // (1,1) (2,2) (3,3) + // Polygon fully contains the edge and its BBox + g.getNodeAccess().setNode(0, 1, 1); + g.getNodeAccess().setNode(1, 3, 3); + EdgeIteratorState edge = g.edge(0, 1).setWayGeometry(Helper.createPointList(2, 2)); + assertTrue(CustomWeightingHelper.in(square, edge)); + + // (0,0) (20,0) (20,20) + // Polygon contains the edge; BBoxes overlap + g.getNodeAccess().setNode(2, 0, 0); + g.getNodeAccess().setNode(3, 20, 20); + edge = g.edge(2, 3).setWayGeometry(Helper.createPointList(20, 0)); + assertTrue(CustomWeightingHelper.in(square, edge)); + + // (0,30) (10,40) (20,50) + // Edge is outside the polygon; BBoxes are not intersecting + g.getNodeAccess().setNode(4, 0, 30); + g.getNodeAccess().setNode(5, 20, 50); + edge = g.edge(4, 5).setWayGeometry(Helper.createPointList(10, 40)); + assertFalse(CustomWeightingHelper.in(square, edge)); + + // (0,30) (30,30) (30,0) + // Edge is outside the polygon; BBoxes are intersecting + g.getNodeAccess().setNode(6, 0, 30); + g.getNodeAccess().setNode(7, 30, 0); + edge = g.edge(6, 7).setWayGeometry(Helper.createPointList(30, 30)); + assertFalse(CustomWeightingHelper.in(square, edge)); + } +} diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java index 95726f9dc02..5d3a1cbeab6 100644 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/CustomWeightingTest.java @@ -5,15 +5,18 @@ import com.graphhopper.json.Statement; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.BaseGraph; -import com.graphhopper.util.*; +import com.graphhopper.util.CustomModel; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.GHUtility; +import com.graphhopper.util.JsonFeature; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.Random; + import static com.graphhopper.json.Statement.*; import static com.graphhopper.json.Statement.Op.LIMIT; import static com.graphhopper.json.Statement.Op.MULTIPLY; @@ -28,18 +31,16 @@ class CustomWeightingTest { DecimalEncodedValue maxSpeedEnc; EnumEncodedValue roadClassEnc; EncodingManager encodingManager; - FlagEncoder carFE; @BeforeEach public void setup() { - carFE = FlagEncoders.createCar(new PMap().putObject("speed_two_directions", true)); - encodingManager = new EncodingManager.Builder().add(carFE) - .add(new EnumEncodedValue<>(Toll.KEY, Toll.class)) - .add(new EnumEncodedValue<>(Hazmat.KEY, Hazmat.class)) - .add(new EnumEncodedValue<>(BikeNetwork.KEY, RouteNetwork.class)) + accessEnc = VehicleAccess.create("car"); + avSpeedEnc = VehicleSpeed.create("car", 5, 5, true); + encodingManager = new EncodingManager.Builder().add(accessEnc).add(avSpeedEnc) + .add(Toll.create()) + .add(Hazmat.create()) + .add(RouteNetwork.create(BikeNetwork.KEY)) .build(); - avSpeedEnc = carFE.getAverageSpeedEnc(); - accessEnc = carFE.getAccessEnc(); maxSpeedEnc = encodingManager.getDecimalEncodedValue(MaxSpeed.KEY); roadClassEnc = encodingManager.getEnumEncodedValue(KEY, RoadClass.class); graph = new BaseGraph.Builder(encodingManager).create(); @@ -49,33 +50,33 @@ public void setup() { public void speedOnly() { // 50km/h -> 72s per km, 100km/h -> 36s per km EdgeIteratorState edge; - GHUtility.setSpeed(50, 100, carFE, edge = graph.edge(0, 1).setDistance(1000)); - assertEquals(72, createWeighting(new CustomModel().setDistanceInfluence(0)).calcEdgeWeight(edge, false), 1.e-6); - assertEquals(36, createWeighting(new CustomModel().setDistanceInfluence(0)).calcEdgeWeight(edge, true), 1.e-6); + GHUtility.setSpeed(50, 100, accessEnc, avSpeedEnc, edge = graph.edge(0, 1).setDistance(1000)); + assertEquals(72, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeWeight(edge, false), 1.e-6); + assertEquals(36, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeWeight(edge, true), 1.e-6); } @Test public void withPriority() { // 25km/h -> 144s per km, 50km/h -> 72s per km, 100km/h -> 36s per km - EdgeIteratorState slow = GHUtility.setSpeed(25, true, true, carFE, graph.edge(0, 1).setDistance(1000)). + EdgeIteratorState slow = GHUtility.setSpeed(25, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(1000)). set(roadClassEnc, SECONDARY); - EdgeIteratorState medium = GHUtility.setSpeed(50, true, true, carFE, graph.edge(0, 1).setDistance(1000)). + EdgeIteratorState medium = GHUtility.setSpeed(50, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(1000)). set(roadClassEnc, SECONDARY); - EdgeIteratorState fast = GHUtility.setSpeed(100, true, true, carFE, graph.edge(0, 1).setDistance(1000)). + EdgeIteratorState fast = GHUtility.setSpeed(100, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(1000)). set(roadClassEnc, SECONDARY); // without priority costs fastest weighting is the same as custom weighting - assertEquals(144, new FastestWeighting(carFE, NO_TURN_COST_PROVIDER).calcEdgeWeight(slow, false), .1); - assertEquals(72, new FastestWeighting(carFE, NO_TURN_COST_PROVIDER).calcEdgeWeight(medium, false), .1); - assertEquals(36, new FastestWeighting(carFE, NO_TURN_COST_PROVIDER).calcEdgeWeight(fast, false), .1); + assertEquals(144, new FastestWeighting(accessEnc, avSpeedEnc, NO_TURN_COST_PROVIDER).calcEdgeWeight(slow, false), .1); + assertEquals(72, new FastestWeighting(accessEnc, avSpeedEnc, NO_TURN_COST_PROVIDER).calcEdgeWeight(medium, false), .1); + assertEquals(36, new FastestWeighting(accessEnc, avSpeedEnc, NO_TURN_COST_PROVIDER).calcEdgeWeight(fast, false), .1); - CustomModel model = new CustomModel().setDistanceInfluence(0); + CustomModel model = new CustomModel().setDistanceInfluence(0d); assertEquals(144, createWeighting(model).calcEdgeWeight(slow, false), .1); assertEquals(72, createWeighting(model).calcEdgeWeight(medium, false), .1); assertEquals(36, createWeighting(model).calcEdgeWeight(fast, false), .1); // if we reduce the priority we get higher edge weights - model.addToPriority(If("road_class == SECONDARY", MULTIPLY, 0.5)); + model.addToPriority(If("road_class == SECONDARY", MULTIPLY, "0.5")); // the absolute priority costs depend on the speed, so setting priority=0.5 means a lower absolute weight // weight increase for fast edges and a higher absolute increase for slower edges assertEquals(2 * 144, createWeighting(model).calcEdgeWeight(slow, false), .1); @@ -85,31 +86,30 @@ public void withPriority() { @Test public void withDistanceInfluence() { - BooleanEncodedValue accessEnc = carFE.getAccessEnc(); EdgeIteratorState edge = graph.edge(0, 1).setDistance(10_000).set(avSpeedEnc, 50).set(accessEnc, true, true); - assertEquals(720, createWeighting(new CustomModel().setDistanceInfluence(0)).calcEdgeWeight(edge, false), .1); - assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(0)).calcEdgeMillis(edge, false), .1); + assertEquals(720, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeWeight(edge, false), .1); + assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeMillis(edge, false), .1); // distance_influence=30 means that for every kilometer we get additional costs of 30s, so +300s here - assertEquals(1020, createWeighting(new CustomModel().setDistanceInfluence(30)).calcEdgeWeight(edge, false), .1); + assertEquals(1020, createWeighting(new CustomModel().setDistanceInfluence(30d)).calcEdgeWeight(edge, false), .1); // ... but the travelling time stays the same - assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(30)).calcEdgeMillis(edge, false), .1); + assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(30d)).calcEdgeMillis(edge, false), .1); // we can also imagine a shorter but slower road that takes the same time edge = graph.edge(0, 1).setDistance(5_000).set(avSpeedEnc, 25).set(accessEnc, true, true); - assertEquals(720, createWeighting(new CustomModel().setDistanceInfluence(0)).calcEdgeWeight(edge, false), .1); - assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(0)).calcEdgeMillis(edge, false), .1); + assertEquals(720, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeWeight(edge, false), .1); + assertEquals(720_000, createWeighting(new CustomModel().setDistanceInfluence(0d)).calcEdgeMillis(edge, false), .1); // and if we include the distance influence the weight will be bigger but still smaller than what we got for // the longer and faster edge - assertEquals(870, createWeighting(new CustomModel().setDistanceInfluence(30)).calcEdgeWeight(edge, false), .1); + assertEquals(870, createWeighting(new CustomModel().setDistanceInfluence(30d)).calcEdgeWeight(edge, false), .1); } @Test public void testSpeedFactorBooleanEV() { - EdgeIteratorState edge = GHUtility.setSpeed(15, true, true, carFE, graph.edge(0, 1).setDistance(10)); - CustomModel vehicleModel = new CustomModel(); + EdgeIteratorState edge = GHUtility.setSpeed(15, true, true, accessEnc, avSpeedEnc, graph.edge(0, 1).setDistance(10)); + CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d); assertEquals(3.1, createWeighting(vehicleModel).calcEdgeWeight(edge, false), 0.01); // here we increase weight for edges that are road class links - vehicleModel.addToPriority(If(RoadClassLink.KEY, MULTIPLY, 0.5)); + vehicleModel.addToPriority(If(RoadClassLink.KEY, MULTIPLY, "0.5")); Weighting weighting = createWeighting(vehicleModel); BooleanEncodedValue rcLinkEnc = encodingManager.getBooleanEncodedValue(RoadClassLink.KEY); assertEquals(3.1, weighting.calcEdgeWeight(edge.set(rcLinkEnc, false), false), 0.01); @@ -118,21 +118,21 @@ public void testSpeedFactorBooleanEV() { @Test public void testBoolean() { - carFE = FlagEncoders.createCar(); + BooleanEncodedValue accessEnc = VehicleAccess.create("car"); + DecimalEncodedValue avSpeedEnc = VehicleSpeed.create("car", 5, 5, false); BooleanEncodedValue specialEnc = new SimpleBooleanEncodedValue("special", true); - encodingManager = new EncodingManager.Builder().add(carFE).add(specialEnc).build(); - avSpeedEnc = carFE.getAverageSpeedEnc(); + encodingManager = new EncodingManager.Builder().add(accessEnc).add(avSpeedEnc).add(specialEnc).build(); graph = new BaseGraph.Builder(encodingManager).create(); - BooleanEncodedValue accessEnc = carFE.getAccessEnc(); EdgeIteratorState edge = graph.edge(0, 1).set(accessEnc, true).setReverse(accessEnc, true). set(avSpeedEnc, 15).set(specialEnc, false).setReverse(specialEnc, true).setDistance(10); - CustomModel vehicleModel = new CustomModel(); - assertEquals(3.1, createWeighting(vehicleModel).calcEdgeWeight(edge, false), 0.01); - vehicleModel.addToPriority(If("special == true", MULTIPLY, 0.8)); - vehicleModel.addToPriority(If("special == false", MULTIPLY, 0.4)); - Weighting weighting = createWeighting(vehicleModel); + CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d); + Weighting weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, NO_TURN_COST_PROVIDER, vehicleModel); + assertEquals(3.1, weighting.calcEdgeWeight(edge, false), 0.01); + vehicleModel.addToPriority(If("special == true", MULTIPLY, "0.8")); + vehicleModel.addToPriority(If("special == false", MULTIPLY, "0.4")); + weighting = CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, NO_TURN_COST_PROVIDER, vehicleModel); assertEquals(6.7, weighting.calcEdgeWeight(edge, false), 0.01); assertEquals(3.7, weighting.calcEdgeWeight(edge, true), 0.01); } @@ -144,16 +144,16 @@ public void testSpeedFactorAndPriority() { EdgeIteratorState secondary = graph.edge(1, 2).setDistance(10). set(roadClassEnc, SECONDARY).set(avSpeedEnc, 70).set(accessEnc, true, true); - CustomModel vehicleModel = new CustomModel(); - vehicleModel.addToPriority(If("road_class != PRIMARY", MULTIPLY, 0.5)); - vehicleModel.addToSpeed(If("road_class != PRIMARY", MULTIPLY, 0.9)); + CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + addToPriority(If("road_class != PRIMARY", MULTIPLY, "0.5")). + addToSpeed(If("road_class != PRIMARY", MULTIPLY, "0.9")); assertEquals(1.15, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); assertEquals(1.84, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); - vehicleModel = new CustomModel(); - vehicleModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, 1.0)); - vehicleModel.addToPriority(Else(MULTIPLY, 0.5)); - vehicleModel.addToSpeed(If("road_class != PRIMARY", MULTIPLY, 0.9)); + vehicleModel = new CustomModel().setDistanceInfluence(70d). + addToPriority(If("road_class == PRIMARY", MULTIPLY, "1.0")). + addToPriority(Else(MULTIPLY, "0.5")). + addToSpeed(If("road_class != PRIMARY", MULTIPLY, "0.9")); assertEquals(1.15, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); assertEquals(1.84, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); } @@ -166,13 +166,14 @@ public void testIssueSameKey() { set(avSpeedEnc, 80).set(accessEnc, true, true); CustomModel vehicleModel = new CustomModel(); - vehicleModel.addToSpeed(If("toll == HGV || toll == ALL", MULTIPLY, 0.8)); - vehicleModel.addToSpeed(If("hazmat != NO", MULTIPLY, 0.8)); + vehicleModel.setDistanceInfluence(70d). + addToSpeed(If("toll == HGV || toll == ALL", MULTIPLY, "0.8")). + addToSpeed(If("hazmat != NO", MULTIPLY, "0.8")); assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(withToll, false), 0.01); assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(noToll, false), 0.01); - vehicleModel = new CustomModel(); - vehicleModel.addToSpeed(If("bike_network != OTHER", MULTIPLY, 0.8)); + vehicleModel = new CustomModel().setDistanceInfluence(70d). + addToSpeed(If("bike_network != OTHER", MULTIPLY, "0.8")); assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(withToll, false), 0.01); assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(noToll, false), 0.01); } @@ -184,13 +185,13 @@ public void testFirstMatch() { EdgeIteratorState secondary = graph.edge(1, 2).setDistance(10). set(roadClassEnc, SECONDARY).set(avSpeedEnc, 70).set(accessEnc, true, true); - CustomModel vehicleModel = new CustomModel(); - vehicleModel.addToSpeed(If("road_class == PRIMARY", MULTIPLY, 0.8)); + CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + addToSpeed(If("road_class == PRIMARY", MULTIPLY, "0.8")); assertEquals(1.26, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); assertEquals(1.21, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); - vehicleModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, 0.9)); - vehicleModel.addToPriority(ElseIf("road_class == SECONDARY", MULTIPLY, 0.8)); + vehicleModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.9")); + vehicleModel.addToPriority(ElseIf("road_class == SECONDARY", MULTIPLY, "0.8")); assertEquals(1.33, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); assertEquals(1.34, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); @@ -201,21 +202,21 @@ public void testCarAccess() { EdgeIteratorState edge40 = graph.edge(0, 1).setDistance(10).set(avSpeedEnc, 40).set(accessEnc, true, true); EdgeIteratorState edge50 = graph.edge(1, 2).setDistance(10).set(avSpeedEnc, 50).set(accessEnc, true, true); - CustomModel vehicleModel = new CustomModel(); - vehicleModel.addToPriority(If("car$average_speed > 40", MULTIPLY, 0.5)); + CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + addToPriority(If("car_average_speed > 40", MULTIPLY, "0.5")); assertEquals(1.60, createWeighting(vehicleModel).calcEdgeWeight(edge40, false), 0.01); assertEquals(2.14, createWeighting(vehicleModel).calcEdgeWeight(edge50, false), 0.01); } @Test - public void testRoadClass() throws Exception { + public void testRoadClass() { EdgeIteratorState primary = graph.edge(0, 1).setDistance(10). set(roadClassEnc, PRIMARY).set(avSpeedEnc, 80).set(accessEnc, true, true); EdgeIteratorState secondary = graph.edge(1, 2).setDistance(10). set(roadClassEnc, SECONDARY).set(avSpeedEnc, 80).set(accessEnc, true, true); - CustomModel vehicleModel = new CustomModel(); - vehicleModel.addToPriority(If("road_class == PRIMARY", MULTIPLY, 0.5)); + CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + addToPriority(If("road_class == PRIMARY", MULTIPLY, "0.5")); assertEquals(1.6, createWeighting(vehicleModel).calcEdgeWeight(primary, false), 0.01); assertEquals(1.15, createWeighting(vehicleModel).calcEdgeWeight(secondary, false), 0.01); } @@ -230,13 +231,14 @@ public void testArea() throws Exception { graph.getNodeAccess().setNode(1, 50.0125, 11.585); graph.getNodeAccess().setNode(2, 40.0, 8.0); graph.getNodeAccess().setNode(3, 40.1, 8.1); - CustomModel vehicleModel = new CustomModel(); - vehicleModel.addToPriority(If("in_custom1", MULTIPLY, 0.5)); + CustomModel vehicleModel = new CustomModel().setDistanceInfluence(70d). + addToPriority(If("in_custom1", MULTIPLY, "0.5")); ObjectMapper om = new ObjectMapper().registerModule(new JtsModule()); JsonFeature json = om.readValue("{ \"geometry\":{ \"type\": \"Polygon\", \"coordinates\": " + "[[[11.5818,50.0126], [11.5818,50.0119], [11.5861,50.0119], [11.5861,50.0126], [11.5818,50.0126]]] }}", JsonFeature.class); - vehicleModel.getAreas().put("custom1", json); + json.setId("custom1"); + vehicleModel.getAreas().getFeatures().add(json); // edge1 is located within the area custom1, edge2 is not assertEquals(1.6, createWeighting(vehicleModel).calcEdgeWeight(edge1, false), 0.01); @@ -245,46 +247,53 @@ public void testArea() throws Exception { @Test public void testMaxSpeed() { - assertEquals(140, carFE.getMaxSpeed(), 0.1); + assertEquals(155, avSpeedEnc.getMaxOrMaxStorableDecimal(), 0.1); assertEquals(1000.0 / 72 * 3.6, createWeighting(new CustomModel(). - addToSpeed(If("true", LIMIT, 72)).setDistanceInfluence(0)).getMinWeight(1000)); + addToSpeed(If("true", LIMIT, "72")).setDistanceInfluence(0d)).getMinWeight(1000)); - // ignore too big limit to let custom model compatibility not break when max speed of encoder later decreases - assertEquals(1000.0 / 140 * 3.6, createWeighting(new CustomModel(). - addToSpeed(If("true", LIMIT, 150)).setDistanceInfluence(0)).getMinWeight(1000)); + // ignore too big limit to let custom model compatibility not break when max speed of encoded value later decreases + assertEquals(1000.0 / 155 * 3.6, createWeighting(new CustomModel(). + addToSpeed(If("true", LIMIT, "180")).setDistanceInfluence(0d)).getMinWeight(1000)); - // a speed bigger than the allowed stored speed is fine, see discussion in #2335 + // reduce speed only a bit assertEquals(1000.0 / 150 * 3.6, createWeighting(new CustomModel(). - addToSpeed(If("road_class == SERVICE", MULTIPLY, 1.5)). - addToSpeed(If("true", LIMIT, 150)).setDistanceInfluence(0)).getMinWeight(1000)); + addToSpeed(If("road_class == SERVICE", MULTIPLY, "1.5")). + addToSpeed(If("true", LIMIT, "150")).setDistanceInfluence(0d)).getMinWeight(1000)); } @Test public void testMaxPriority() { - assertEquals(1000.0 / 140 / 0.5 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("true", MULTIPLY, 0.5)).setDistanceInfluence(0)).getMinWeight(1000)); + assertEquals(155, avSpeedEnc.getMaxOrMaxStorableDecimal(), 0.1); + double maxSpeed = 155; + assertEquals(1000.0 / maxSpeed / 0.5 * 3.6, createWeighting(new CustomModel(). + addToPriority(If("true", MULTIPLY, "0.5")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); // ignore too big limit - assertEquals(1000.0 / 140 / 1.0 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("true", LIMIT, 2.0)).setDistanceInfluence(0)).getMinWeight(1000)); + assertEquals(1000.0 / maxSpeed / 1.0 * 3.6, createWeighting(new CustomModel(). + addToPriority(If("true", LIMIT, "2.0")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); // priority bigger 1 is fine (if CustomModel not in query) - assertEquals(1000.0 / 140 / 2.0 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("true", MULTIPLY, 3.0)). - addToPriority(If("true", LIMIT, 2.0)).setDistanceInfluence(0)).getMinWeight(1000)); - assertEquals(1000.0 / 140 / 1.5 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("true", MULTIPLY, 1.5)).setDistanceInfluence(0)).getMinWeight(1000)); - // pick maximum priority from value even if this is for a very special case - assertEquals(1000.0 / 140 / 3.0 * 3.6, createWeighting(new CustomModel(). - addToPriority(If("road_class == SERVICE", MULTIPLY, 3.0)).setDistanceInfluence(0)).getMinWeight(1000)); + assertEquals(1000.0 / maxSpeed / 2.0 * 3.6, createWeighting(new CustomModel(). + addToPriority(If("true", MULTIPLY, "3.0")). + addToPriority(If("true", LIMIT, "2.0")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); + assertEquals(1000.0 / maxSpeed / 1.5 * 3.6, createWeighting(new CustomModel(). + addToPriority(If("true", MULTIPLY, "1.5")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); + + // pick maximum priority from value even if this is for a special case + assertEquals(1000.0 / maxSpeed / 3.0 * 3.6, createWeighting(new CustomModel(). + addToPriority(If("road_class == SERVICE", MULTIPLY, "3.0")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); + + // do NOT pick maximum priority when it is for a special case + assertEquals(1000.0 / maxSpeed / 1.0 * 3.6, createWeighting(new CustomModel(). + addToPriority(If("road_class == SERVICE", MULTIPLY, "0.5")).setDistanceInfluence(0d)).getMinWeight(1000), 1.e-6); } @Test public void tooManyStatements() { CustomModel customModel = new CustomModel(); for (int i = 0; i < 1050; i++) { - customModel.addToPriority(If("road_class == MOTORWAY || road_class == SECONDARY || road_class == PRIMARY", MULTIPLY, 0.1)); + customModel.addToPriority(If("road_class == MOTORWAY || road_class == SECONDARY || road_class == PRIMARY", MULTIPLY, "0.1")); } IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> createWeighting(customModel)); assertTrue(ex.getMessage().startsWith("Custom Model too big"), ex.getMessage()); @@ -295,14 +304,43 @@ public void maxSpeedViolated_bug_2307() { EdgeIteratorState motorway = graph.edge(0, 1).setDistance(10). set(roadClassEnc, MOTORWAY).set(avSpeedEnc, 80).set(accessEnc, true, true); CustomModel customModel = new CustomModel() - .addToSpeed(Statement.If("road_class == MOTORWAY", Statement.Op.MULTIPLY, 0.7)) - .addToSpeed(Statement.Else(LIMIT, 30)); + .setDistanceInfluence(70d) + .addToSpeed(Statement.If("road_class == MOTORWAY", Statement.Op.MULTIPLY, "0.7")) + .addToSpeed(Statement.Else(LIMIT, "30")); Weighting weighting = createWeighting(customModel); assertEquals(1.3429, weighting.calcEdgeWeight(motorway, false), 1e-4); assertEquals(10 / (80 * 0.7 / 3.6) * 1000, weighting.calcEdgeMillis(motorway, false), 1); } + @Test + public void bugWithNaNForBarrierEdges() { + EdgeIteratorState motorway = graph.edge(0, 1).setDistance(0). + set(roadClassEnc, MOTORWAY).set(avSpeedEnc, 80).set(accessEnc, true, true); + CustomModel customModel = new CustomModel() + .addToPriority(Statement.If("road_class == MOTORWAY", Statement.Op.MULTIPLY, "0")); + Weighting weighting = createWeighting(customModel); + assertFalse(Double.isNaN(weighting.calcEdgeWeight(motorway, false))); + assertTrue(Double.isInfinite(weighting.calcEdgeWeight(motorway, false))); + } + + @Test + void sameTimeAsFastestWeighting() { + // we make sure the returned times are the same, so we can check for regressions more easily when we migrate from fastest to custom + FastestWeighting fastestWeighting = new FastestWeighting(accessEnc, avSpeedEnc); + Weighting customWeighting = createWeighting(new CustomModel().setDistanceInfluence(0d)); + Random rnd = new Random(); + for (int i = 0; i < 100; i++) { + double speed = 5 + rnd.nextDouble() * 100; + double distance = rnd.nextDouble() * 1000; + EdgeIteratorState edge = graph.edge(0, 1).setDistance(distance); + GHUtility.setSpeed(speed, speed, accessEnc, avSpeedEnc, edge); + long fastestMillis = fastestWeighting.calcEdgeMillis(edge, false); + long customMillis = customWeighting.calcEdgeMillis(edge, false); + assertEquals(fastestMillis, customMillis); + } + } + private Weighting createWeighting(CustomModel vehicleModel) { - return CustomModelParser.createWeighting(carFE, encodingManager, NO_TURN_COST_PROVIDER, vehicleModel); + return CustomModelParser.createWeighting(accessEnc, avSpeedEnc, null, encodingManager, NO_TURN_COST_PROVIDER, vehicleModel); } } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/ExpressionVisitorTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/ExpressionVisitorTest.java deleted file mode 100644 index 0c143cdee03..00000000000 --- a/core/src/test/java/com/graphhopper/routing/weighting/custom/ExpressionVisitorTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.graphhopper.routing.weighting.custom; - -import com.graphhopper.json.Statement; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.StringEncodedValue; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.storage.IntsRef; -import com.graphhopper.util.Helper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.HashSet; - -import static com.graphhopper.json.Statement.If; -import static com.graphhopper.routing.weighting.custom.CustomModelParser.isValidVariableName; -import static com.graphhopper.routing.weighting.custom.ExpressionVisitor.parseExpression; -import static org.junit.jupiter.api.Assertions.*; - -public class ExpressionVisitorTest { - - private EncodedValueLookup lookup; - - @BeforeEach - public void before() { - StringEncodedValue sev = new StringEncodedValue("country", 10); - lookup = new EncodingManager.Builder().add(sev).build(); - sev.setString(false, new IntsRef(1), "DEU"); - } - - @Test - public void protectUsFromStuff() { - ExpressionVisitor.NameValidator allNamesInvalid = s -> false; - for (String toParse : Arrays.asList( - "", - "new Object()", - "java.lang.Object", - "Test.class", - "new Object(){}.toString().length", - "{ 5}", - "{ 5, 7 }", - "Object.class", - "System.out.println(\"\")", - "something.newInstance()", - "e.getClass ( )", - "edge.getDistance()*7/*test", - "edge.getDistance()//*test", - "edge . getClass()", - "(edge = edge) == edge", - ") edge (", - "in(area_blup(), edge)", - "s -> truevalue")) { - ExpressionVisitor.ParseResult res = parseExpression(toParse, allNamesInvalid, lookup); - assertFalse(res.ok, "should not be simple condition: " + toParse); - assertTrue(res.guessedVariables == null || res.guessedVariables.isEmpty()); - } - - assertFalse(parseExpression("edge; getClass()", allNamesInvalid, lookup).ok); - } - - @Test - public void testConvertExpression() { - ExpressionVisitor.NameValidator validVariable = s -> isValidVariableName(s) - || Helper.toUpperCase(s).equals(s) || s.equals("road_class") || s.equals("toll"); - - ExpressionVisitor.ParseResult result = parseExpression("toll == NO", validVariable, lookup); - assertTrue(result.ok); - assertEquals("[toll]", result.guessedVariables.toString()); - - assertEquals("road_class == RoadClass.PRIMARY", - parseExpression("road_class == PRIMARY", validVariable, lookup).converted.toString()); - assertEquals("toll == Toll.NO", parseExpression("toll == NO", validVariable, lookup).converted.toString()); - assertEquals("toll == Toll.NO || road_class == RoadClass.NO", parseExpression("toll == NO || road_class == NO", validVariable, lookup).converted.toString()); - - // convert in_area variable to function call: - assertEquals(CustomWeightingHelper.class.getSimpleName() + ".in(this.in_custom_1, edge)", - parseExpression("in_custom_1", validVariable, lookup).converted.toString()); - - // no need to inject: - assertNull(parseExpression("toll == Toll.NO", validVariable, lookup).converted); - } - - @Test - public void testStringExpression() { - ExpressionVisitor.NameValidator validVariable = s -> isValidVariableName(s) || s.equals("country"); - - ExpressionVisitor.ParseResult result = parseExpression("country == \"DEU\"", validVariable, lookup); - assertTrue(result.ok); - assertEquals("[country]", result.guessedVariables.toString()); - assertEquals("country == 1", result.converted.toString()); - - // unknown String should result in a negative integer. If we would throw an Exception here the same script that - // works on a global map will not work on a smaller map where the "blup" String is missing - result = parseExpression("country == \"blup\"", validVariable, lookup); - assertTrue(result.ok); - assertEquals("[country]", result.guessedVariables.toString()); - assertEquals("country == -1", result.converted.toString()); - } - - @Test - public void isValidAndSimpleCondition() { - ExpressionVisitor.NameValidator validVariable = s -> isValidVariableName(s) - || Helper.toUpperCase(s).equals(s) || s.equals("road_class") || s.equals("toll"); - ExpressionVisitor.ParseResult result = parseExpression("edge == edge", validVariable, lookup); - assertTrue(result.ok); - assertEquals("[edge]", result.guessedVariables.toString()); - - result = parseExpression("Math.sqrt(2)", validVariable, lookup); - assertTrue(result.ok); - assertTrue(result.guessedVariables.isEmpty()); - - result = parseExpression("edge.blup()", validVariable, lookup); - assertFalse(result.ok); - assertTrue(result.guessedVariables.isEmpty()); - - result = parseExpression("edge.getDistance()", validVariable, lookup); - assertTrue(result.ok); - assertEquals("[edge]", result.guessedVariables.toString()); - assertFalse(parseExpression("road_class == PRIMARY", s -> false, lookup).ok); - result = parseExpression("road_class == PRIMARY", validVariable, lookup); - assertTrue(result.ok); - assertEquals("[road_class]", result.guessedVariables.toString()); - - result = parseExpression("toll == Toll.NO", validVariable, lookup); - assertFalse(result.ok); - assertEquals("[toll]", result.guessedVariables.toString()); - - assertTrue(parseExpression("road_class.ordinal()*2 == PRIMARY.ordinal()*2", validVariable, lookup).ok); - assertTrue(parseExpression("Math.sqrt(road_class.ordinal()) > 1", validVariable, lookup).ok); - - result = parseExpression("(toll == NO || road_class == PRIMARY) && toll == NO", validVariable, lookup); - assertTrue(result.ok); - assertEquals("[toll, road_class]", result.guessedVariables.toString()); - } - - @Test - public void errorMessage() { - ExpressionVisitor.NameValidator validVariable = s -> lookup.hasEncodedValue(s); - - // existing encoded value but not added - IllegalArgumentException ret = assertThrows(IllegalArgumentException.class, - () -> ExpressionVisitor.parseExpressions(new StringBuilder(), - validVariable, "[HERE]", new HashSet<>(), - Arrays.asList(If("max_weight > 10", Statement.Op.MULTIPLY, 0)), - lookup, "")); - assertTrue(ret.getMessage().startsWith("[HERE] invalid expression \"max_weight > 10\": 'max_weight' not available"), ret.getMessage()); - - // invalid variable or constant (NameValidator returns false) - ret = assertThrows(IllegalArgumentException.class, - () -> ExpressionVisitor.parseExpressions(new StringBuilder(), - validVariable, "[HERE]", new HashSet<>(), - Arrays.asList(If("country == GERMANY", Statement.Op.MULTIPLY, 0)), - lookup, "")); - assertTrue(ret.getMessage().startsWith("[HERE] invalid expression \"country == GERMANY\": 'GERMANY' not available"), ret.getMessage()); - - // not whitelisted method - ret = assertThrows(IllegalArgumentException.class, - () -> ExpressionVisitor.parseExpressions(new StringBuilder(), - validVariable, "[HERE]", new HashSet<>(), - Arrays.asList(If("edge.fetchWayGeometry().size() > 2", Statement.Op.MULTIPLY, 0)), - lookup, "")); - assertTrue(ret.getMessage().startsWith("[HERE] invalid expression \"edge.fetchWayGeometry().size() > 2\": size is illegal method"), ret.getMessage()); - } -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/FindMinMaxTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/FindMinMaxTest.java new file mode 100644 index 00000000000..1d0452710e7 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/FindMinMaxTest.java @@ -0,0 +1,114 @@ +package com.graphhopper.routing.weighting.custom; + +import com.graphhopper.json.MinMax; +import com.graphhopper.json.Statement; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.util.CustomModel; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import static com.graphhopper.json.Statement.*; +import static com.graphhopper.json.Statement.Op.LIMIT; +import static com.graphhopper.json.Statement.Op.MULTIPLY; +import static com.graphhopper.routing.weighting.custom.FindMinMax.findMinMax; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class FindMinMaxTest { + + private EncodedValueLookup lookup; + + @BeforeEach + void setup() { + lookup = new EncodingManager.Builder().build(); + } + + @Test + public void testCheck() { + CustomModel queryModel = new CustomModel(); + queryModel.addToPriority(If("max_width < 3", MULTIPLY, "10")); + assertEquals(1, CustomModel.merge(new CustomModel(), queryModel).getPriority().size()); + // priority bigger than 1 is not ok for CustomModel of query + assertThrows(IllegalArgumentException.class, () -> FindMinMax.checkLMConstraints(new CustomModel(), queryModel, lookup)); + } + + @Test + public void testFindMax() { + List statements = new ArrayList<>(); + statements.add(If("true", LIMIT, "100")); + assertEquals(100, findMinMax(new HashSet<>(), new MinMax(0, 120), statements, lookup).max); + + statements.add(Else(LIMIT, "20")); + assertEquals(100, findMinMax(new HashSet<>(), new MinMax(0, 120), statements, lookup).max); + + statements = new ArrayList<>(); + statements.add(If("road_environment == BRIDGE", LIMIT, "85")); + statements.add(Else(LIMIT, "100")); + assertEquals(100, findMinMax(new HashSet<>(), new MinMax(0, 120), statements, lookup).max); + + // find bigger speed than stored max_speed (30) in server-side custom_models + statements = new ArrayList<>(); + statements.add(If("true", MULTIPLY, "2")); + statements.add(If("true", LIMIT, "35")); + assertEquals(35, findMinMax(new HashSet<>(), new MinMax(0, 30), statements, lookup).max); + } + + @Test + public void findMax_limitAndMultiply() { + List statements = Arrays.asList( + If("road_class == TERTIARY", LIMIT, "90"), + ElseIf("road_class == SECONDARY", MULTIPLY, "1.0"), + ElseIf("road_class == PRIMARY", LIMIT, "30"), + Else(LIMIT, "3") + ); + assertEquals(140, findMinMax(new HashSet<>(), new MinMax(0, 140), statements, lookup).max); + } + + @Test + public void testFindMaxPriority() { + List statements = new ArrayList<>(); + statements.add(If("true", MULTIPLY, "2")); + assertEquals(2, findMinMax(new HashSet<>(), new MinMax(0, 1), statements, lookup).max); + + statements = new ArrayList<>(); + statements.add(If("true", MULTIPLY, "0.5")); + assertEquals(0.5, findMinMax(new HashSet<>(), new MinMax(0, 1), statements, lookup).max); + + statements = new ArrayList<>(); + statements.add(If("road_class == MOTORWAY", MULTIPLY, "0.5")); + statements.add(Else(MULTIPLY, "-0.5")); + MinMax minMax = findMinMax(new HashSet<>(), new MinMax(1, 1), statements, lookup); + assertEquals(-0.5, minMax.min); + assertEquals(0.5, minMax.max); + } + + @Test + public void findMax_multipleBlocks() { + List statements = Arrays.asList( + If("road_class == TERTIARY", MULTIPLY, "0.2"), + ElseIf("road_class == SECONDARY", LIMIT, "25"), + If("road_environment == TUNNEL", LIMIT, "60"), + ElseIf("road_environment == BRIDGE", LIMIT, "50"), + Else(MULTIPLY, "0.8") + ); + assertEquals(120, findMinMax(new HashSet<>(), new MinMax(0, 150), statements, lookup).max); + assertEquals(80, findMinMax(new HashSet<>(), new MinMax(0, 100), statements, lookup).max); + assertEquals(60, findMinMax(new HashSet<>(), new MinMax(0, 60), statements, lookup).max); + + statements = Arrays.asList( + If("road_class == TERTIARY", MULTIPLY, "0.2"), + ElseIf("road_class == SECONDARY", LIMIT, "25"), + Else(LIMIT, "40"), + If("road_environment == TUNNEL", MULTIPLY, "0.8"), + ElseIf("road_environment == BRIDGE", LIMIT, "30") + ); + assertEquals(40, findMinMax(new HashSet<>(), new MinMax(0, 150), statements, lookup).max); + assertEquals(40, findMinMax(new HashSet<>(), new MinMax(0, 40), statements, lookup).max); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitorTest.java b/core/src/test/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitorTest.java new file mode 100644 index 00000000000..03b03ed6204 --- /dev/null +++ b/core/src/test/java/com/graphhopper/routing/weighting/custom/ValueExpressionVisitorTest.java @@ -0,0 +1,110 @@ +package com.graphhopper.routing.weighting.custom; + +import com.graphhopper.json.MinMax; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.IntEncodedValueImpl; +import com.graphhopper.routing.util.EncodingManager; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import static com.graphhopper.routing.weighting.custom.ValueExpressionVisitor.findMinMax; +import static com.graphhopper.routing.weighting.custom.ValueExpressionVisitor.parse; +import static org.junit.jupiter.api.Assertions.*; + +class ValueExpressionVisitorTest { + + @Test + public void protectUsFromStuff() { + NameValidator allNamesInvalid = s -> false; + for (String toParse : Arrays.asList("", "new Object()", "java.lang.Object", "Test.class", + "new Object(){}.toString().length", "{ 5}", "{ 5, 7 }", "Object.class", "System.out.println(\"\")", + "something.newInstance()", "e.getClass ( )", "edge.getDistance()*7/*test", "edge.getDistance()//*test", + "edge . getClass()", "(edge = edge) == edge", ") edge (", "in(area_blup(), edge)", "s -> truevalue")) { + ParseResult res = parse(toParse, allNamesInvalid); + assertFalse(res.ok, "should not be simple condition: " + toParse); + assertTrue(res.guessedVariables == null || res.guessedVariables.isEmpty()); + } + + assertFalse(parse("edge; getClass()", allNamesInvalid).ok); + } + + @Test + public void isValidAndSimpleCondition() { + ParseResult result = parse("edge == edge", (arg) -> false); + assertFalse(result.ok); + + result = parse("Math.sqrt(2)", (arg) -> false); + assertTrue(result.ok, result.invalidMessage); + assertTrue(result.guessedVariables.isEmpty()); + + result = parse("Math.sqrt(my_speed)", (arg) -> arg.equals("my_speed")); + assertTrue(result.ok, result.invalidMessage); + assertEquals("[my_speed]", result.guessedVariables.toString()); + + result = parse("edge.getDistance()", (arg) -> false); + assertFalse(result.ok); + + result = parse("road_class == PRIMARY", (arg) -> false); + assertFalse(result.ok); + + result = parse("toll == Toll.NO", (arg) -> false); + assertFalse(result.ok); + + result = parse("priority * 2", (s) -> s.equals("priority")); + assertTrue(result.ok, result.invalidMessage); + assertEquals("[priority]", result.guessedVariables.toString()); + + // LATER but requires accepting also EnumEncodedValue for value expression + // result = parse("road_class.ordinal()*2", validVariable); + // assertTrue(result.ok, result.invalidMessage); + // assertTrue(parse("Math.sqrt(road_class.ordinal())", validVariable).ok); + } + + + @Test + public void testErrors() { + Set objs = new HashSet<>(); + DecimalEncodedValue prio1 = new DecimalEncodedValueImpl("my_priority", 5, 1, false); + IntEncodedValueImpl prio2 = new IntEncodedValueImpl("my_priority2", 5, -5, false, false); + EncodedValueLookup lookup = new EncodingManager.Builder().add(prio1).add(prio2).build(); + + String msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "unknown*3", lookup)).getMessage(); + assertTrue(msg.contains("'unknown' not available"), msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "my_priority - my_priority2 * 3", lookup)).getMessage(); + assertTrue(msg.contains("a single EncodedValue"), msg); + // unary minus is also a minus operator + msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "-my_priority + my_priority2 * 3", lookup)).getMessage(); + assertTrue(msg.contains("a single EncodedValue"), msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "1/my_priority", lookup)).getMessage(); + assertTrue(msg.contains("invalid operation '/'"), msg); + + msg = assertThrows(IllegalArgumentException.class, () -> findMinMax(objs, "my_priority*my_priority2 * 3", lookup)).getMessage(); + assertTrue(msg.contains("Currently only a single EncodedValue is allowed on the right-hand side"), msg); + } + + @Test + public void runMaxMin() { + DecimalEncodedValue prio1 = new DecimalEncodedValueImpl("my_priority", 5, 1, false); + IntEncodedValueImpl prio2 = new IntEncodedValueImpl("my_priority2", 5, -5, false, false); + EncodedValueLookup lookup = new EncodingManager.Builder().add(prio1).add(prio2).build(); + + assertInterval(2, 2, "2", lookup); + + assertInterval(0, 62, "2*my_priority", lookup); + + assertInterval(-52, 10, "-2*my_priority2", lookup); + } + + void assertInterval(double min, double max, String expression, EncodedValueLookup lookup) { + MinMax minmax = findMinMax(new HashSet<>(), expression, lookup); + assertEquals(min, minmax.min, 0.1, expression); + assertEquals(max, minmax.max, 0.1, expression); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/search/KVStorageTest.java b/core/src/test/java/com/graphhopper/search/KVStorageTest.java new file mode 100644 index 00000000000..938ffce14c6 --- /dev/null +++ b/core/src/test/java/com/graphhopper/search/KVStorageTest.java @@ -0,0 +1,404 @@ +package com.graphhopper.search; + +import com.carrotsearch.hppc.LongArrayList; +import com.graphhopper.search.KVStorage.KeyValue; +import com.graphhopper.storage.RAMDirectory; +import com.graphhopper.util.Helper; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.*; + +import static com.graphhopper.search.KVStorage.KeyValue.createKV; +import static com.graphhopper.search.KVStorage.MAX_UNIQUE_KEYS; +import static com.graphhopper.search.KVStorage.cutString; +import static com.graphhopper.util.Helper.UTF_CS; +import static org.junit.jupiter.api.Assertions.*; + +public class KVStorageTest { + + private final static String location = "./target/edge-kv-storage"; + + private KVStorage create() { + return new KVStorage(new RAMDirectory(), true).create(1000); + } + + List createList(Object... keyValues) { + if (keyValues.length % 2 != 0) + throw new IllegalArgumentException("Cannot create list from " + Arrays.toString(keyValues)); + List map = new ArrayList<>(); + for (int i = 0; i < keyValues.length; i += 2) { + map.add(new KeyValue((String) keyValues[i], keyValues[i + 1])); + } + return map; + } + + @Test + public void putSame() { + KVStorage index = create(); + long aPointer = index.add(createList("a", "same name", "b", "same name")); + + assertNull(index.get(aPointer, "", false)); + assertEquals("same name", index.get(aPointer, "a", false)); + assertEquals("same name", index.get(aPointer, "b", false)); + assertNull(index.get(aPointer, "c", false)); + + index = create(); + aPointer = index.add(createList("a", "a name", "b", "same name")); + assertEquals("a name", index.get(aPointer, "a", false)); + } + + @Test + public void putAB() { + KVStorage index = create(); + long aPointer = index.add(createList("a", "a name", "b", "b name")); + + assertNull(index.get(aPointer, "", false)); + assertEquals("a name", index.get(aPointer, "a", false)); + assertEquals("b name", index.get(aPointer, "b", false)); + } + + @Test + public void getForwardBackward() { + KVStorage index = create(); + List list = new ArrayList<>(); + list.add(new KeyValue("keyA", "FORWARD", true, false)); + list.add(new KeyValue("keyB", "BACKWARD", false, true)); + list.add(new KeyValue("keyC", "BOTH", true, true)); + long aPointer = index.add(list); + + assertNull(index.get(aPointer, "", false)); + List deserializedList = index.getAll(aPointer); + assertEquals(list, deserializedList); + + assertEquals("FORWARD", index.get(aPointer, "keyA", false)); + assertNull(index.get(aPointer, "keyA", true)); + + assertNull(index.get(aPointer, "keyB", false)); + assertEquals("BACKWARD", index.get(aPointer, "keyB", true)); + + assertEquals("BOTH", index.get(aPointer, "keyC", false)); + assertEquals("BOTH", index.get(aPointer, "keyC", true)); + } + + @Test + public void putEmpty() { + KVStorage index = create(); + assertEquals(1, index.add(createList("", ""))); + // cannot store null (in its first version we accepted null once it was clear which type the value has, but this is inconsequential) + assertThrows(IllegalArgumentException.class, () -> assertEquals(5, index.add(createList("", null)))); + assertThrows(IllegalArgumentException.class, () -> index.add(createList("blup", null))); + assertThrows(IllegalArgumentException.class, () -> index.add(createList(null, null))); + + assertNull(index.get(0, "", false)); + + assertEquals(5, index.add(createList("else", "else"))); + } + + @Test + public void putMany() { + KVStorage index = create(); + long aPointer = 0, tmpPointer = 0; + + for (int i = 0; i < 10000; i++) { + aPointer = index.add(createList("a", "a name " + i, "b", "b name " + i, "c", "c name " + i)); + if (i == 567) + tmpPointer = aPointer; + } + + assertEquals("b name 9999", index.get(aPointer, "b", false)); + assertEquals("c name 9999", index.get(aPointer, "c", false)); + + assertEquals("a name 567", index.get(tmpPointer, "a", false)); + assertEquals("b name 567", index.get(tmpPointer, "b", false)); + assertEquals("c name 567", index.get(tmpPointer, "c", false)); + } + + @Test + public void putManyKeys() { + KVStorage index = create(); + // one key is already stored => empty key + for (int i = 1; i < MAX_UNIQUE_KEYS; i++) { + index.add(createList("a" + i, "a name")); + } + try { + index.add(createList("new", "a name")); + fail(); + } catch (IllegalArgumentException ex) { + } + } + + @Test + public void testNoErrorOnLargeStringValue() { + KVStorage index = create(); + String str = ""; + for (int i = 0; i < 127; i++) { + str += "ß"; + } + assertEquals(254, str.getBytes(Helper.UTF_CS).length); + long result = index.add(createList("", str)); + assertEquals(127, ((String) index.get(result, "", false)).length()); + } + + @Test + public void testTooLongStringValueError() { + KVStorage index = create(); + assertThrows(IllegalArgumentException.class, () -> index.add(createList("", "Бухарестская улица (http://ru.wikipedia.org/wiki" + + "/%D0%91%D1%83%D1%85%D0%B0%D1%80%D0%B5%D1%81%D1%82%D1%81%D0%BA%D0%B0%D1%8F_%D1%83%D0%BB%D0%B8%D1%86%D0%B0_(%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3))"))); + + String str = "sdfsdfds"; + for (int i = 0; i < 256 * 3; i++) { + str += "Б"; + } + final String finalStr = str; + assertThrows(IllegalArgumentException.class, () -> index.add(createList("", finalStr))); + } + + @Test + public void testNoErrorOnLargestByteArray() { + KVStorage index = create(); + byte[] bytes = new byte[255]; + byte[] copy = new byte[255]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) (i % 255); + copy[i] = bytes[i]; + } + long result = index.add(createKV("myval", bytes)); + bytes = (byte[]) index.get(result, "myval", false); + assertArrayEquals(copy, bytes); + + final byte[] biggerByteArray = Arrays.copyOf(bytes, 256); + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> index.add(createKV("myval2", biggerByteArray))); + assertTrue(e.getMessage().contains("bytes.length cannot be > 255")); + } + + @Test + public void testIntLongDoubleFloat() { + KVStorage index = create(); + long intres = index.add(createKV("intres", 4)); + long doubleres = index.add(createKV("doubleres", 4d)); + long floatres = index.add(createKV("floatres", 4f)); + long longres = index.add(createKV("longres", 4L)); + long after4Inserts = index.add(createKV("somenext", 0)); + + // initial point is 1, then twice plus 1 + (2+4) and twice plus 1 + (2+8) + assertEquals(1 + 36, after4Inserts); + + assertEquals(4f, index.get(floatres, "floatres", false)); + assertEquals(4L, index.get(longres, "longres", false)); + assertEquals(4d, index.get(doubleres, "doubleres", false)); + assertEquals(4, index.get(intres, "intres", false)); + } + + @Test + public void testIntLongDoubleFloat2() { + KVStorage index = create(); + List list = new ArrayList<>(); + list.add(new KeyValue("int", 4)); + list.add(new KeyValue("long", 4L)); + list.add(new KeyValue("double", 4d)); + list.add(new KeyValue("float", 4f)); + long allInOne = index.add(list); + + long afterMapInsert = index.add(createKV("somenext", 0)); + + // 1 + 1 + (2+4) + (2+8) + (2+8) + (2+4) + assertEquals(1 + 1 + 32, afterMapInsert); + + List resMap = index.getAll(allInOne); + assertEquals(4, resMap.get(0).value); + assertEquals(4L, resMap.get(1).value); + assertEquals(4d, resMap.get(2).value); + assertEquals(4f, resMap.get(3).value); + } + + @Test + public void testFlush() { + Helper.removeDir(new File(location)); + + KVStorage index = new KVStorage(new RAMDirectory(location, true).create(), true); + long pointer = index.add(createList("", "test")); + index.flush(); + index.close(); + + index = new KVStorage(new RAMDirectory(location, true), true); + assertTrue(index.loadExisting()); + assertEquals("test", index.get(pointer, "", false)); + // make sure bytePointer is correctly set after loadExisting + long newPointer = index.add(createList("", "testing")); + assertEquals(pointer + 1 + 3 + "test".getBytes().length, newPointer, newPointer + ">" + pointer); + index.close(); + + Helper.removeDir(new File(location)); + } + + @Test + public void testLoadKeys() { + Helper.removeDir(new File(location)); + + KVStorage index = new KVStorage(new RAMDirectory(location, true).create(), true).create(1000); + long pointerA = index.add(createList("c", "test value")); + assertEquals(2, index.getKeys().size()); + long pointerB = index.add(createList("a", "value", "b", "another value")); + // empty string is always the first key + assertEquals("[, c, a, b]", index.getKeys().toString()); + index.flush(); + index.close(); + + index = new KVStorage(new RAMDirectory(location, true), true); + assertTrue(index.loadExisting()); + assertEquals("[, c, a, b]", index.getKeys().toString()); + assertEquals("test value", index.get(pointerA, "c", false)); + assertNull(index.get(pointerA, "b", false)); + + assertNull(index.get(pointerB, "", false)); + assertEquals("value", index.get(pointerB, "a", false)); + assertEquals("another value", index.get(pointerB, "b", false)); + assertEquals("[a=value (true|true), b=another value (true|true)]", index.getAll(pointerB).toString()); + index.close(); + + Helper.removeDir(new File(location)); + } + + @Test + public void testEmptyKey() { + KVStorage index = create(); + long pointerA = index.add(createList("", "test value")); + long pointerB = index.add(createList("a", "value", "b", "another value")); + + assertEquals("test value", index.get(pointerA, "", false)); + assertNull(index.get(pointerA, "a", false)); + + assertEquals("value", index.get(pointerB, "a", false)); + assertNull(index.get(pointerB, "", false)); + } + + @Test + public void testSameByteArray() { + KVStorage index = create(); + + long pointerA = index.add(createList("mykey", new byte[]{1, 2, 3, 4})); + long pointerB = index.add(createList("mykey", new byte[]{1, 2, 3, 4})); + assertEquals(pointerA, pointerB); + + byte[] sameRef = new byte[]{1, 2, 3, 4}; + pointerA = index.add(createList("mykey", sameRef)); + pointerB = index.add(createList("mykey", sameRef)); + assertEquals(pointerA, pointerB); + } + + @Test + public void testUnknownValueClass() { + KVStorage index = create(); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> index.add(createList("mykey", new Object()))); + assertTrue(ex.getMessage().contains("The Class of a value was Object, currently supported"), ex.getMessage()); + } + + @RepeatedTest(20) + public void testRandom() { + final long seed = new Random().nextLong(); + try { + KVStorage index = new KVStorage(new RAMDirectory(location, true).create(), true).create(1000); + Random random = new Random(seed); + List keys = createRandomStringList(random, "_key", 100); + List values = createRandomList(random, 500); + + int size = 10000; + LongArrayList pointers = new LongArrayList(size); + for (int i = 0; i < size; i++) { + List list = createRandomList(random, keys, values); + long pointer = index.add(list); + try { + assertEquals(list.size(), index.getAll(pointer).size(), "" + i); + } catch (Exception ex) { + throw new RuntimeException(i + " " + list + ", " + pointer, ex); + } + pointers.add(pointer); + } + + for (int i = 0; i < size; i++) { + List list = index.getAll(pointers.get(i)); + assertTrue(list.size() > 0, i + " " + list); + for (KeyValue entry : list) { + Object value = index.get(pointers.get(i), entry.key, false); + assertEquals(entry.value, value, i + " " + list); + } + } + index.flush(); + index.close(); + + index = new KVStorage(new RAMDirectory(location, true).create(), true); + assertTrue(index.loadExisting()); + for (int i = 0; i < size; i++) { + List list = index.getAll(pointers.get(i)); + assertTrue(list.size() > 0, i + " " + list); + for (KeyValue entry : list) { + Object value = index.get(pointers.get(i), entry.key, false); + assertEquals(entry.value, value, i + " " + list); + } + } + index.close(); + } catch (Throwable t) { + throw new RuntimeException("KVStorageTest.testRandom seed:" + seed, t); + } + } + + private List createRandomList(Random random, int size) { + List list = new ArrayList<>(); + for (int i = 0; i < size; i++) { + list.add(random.nextInt(size * 5)); + } + return list; + } + + private List createRandomStringList(Random random, String postfix, int size) { + List list = new ArrayList<>(); + for (int i = 0; i < size; i++) { + list.add(random.nextInt(size * 5) + postfix); + } + return list; + } + + private List createRandomList(Random random, List keys, List values) { + int count = random.nextInt(10) + 2; + Set avoidDuplicates = new HashSet<>(); // otherwise index.get returns potentially wrong value + List list = new ArrayList<>(); + for (int i = 0; i < count; i++) { + String key = keys.get(random.nextInt(keys.size())); + if (!avoidDuplicates.add(key)) + continue; + Object o = values.get(random.nextInt(values.size())); + list.add(new KeyValue(key, key.endsWith("_s") ? o + "_s" : o)); + } + return list; + } + + // @RepeatedTest(1000) + public void ignoreRandomString() { + String s = ""; + long seed = new Random().nextLong(); + Random rand = new Random(seed); + for (int i = 0; i < 255; i++) { + s += (char) rand.nextInt(); + } + + s = cutString(s); + assertTrue(s.getBytes(UTF_CS).length <= 255, s.getBytes(UTF_CS).length + " -> seed " + seed); + } + + @Test + public void testCutString() { + String s = cutString("Бухарестская улица (http://ru.wikipedia.org/wiki/" + + "%D0%91%D1%83%D1%85%D0%B0%D1%80%D0%B5%D1%81%D1%82%D1%81%D0%BA%D0%B0%D1%8F_%D1%83%D0%BB%D0%B8%D1%86%D0%B0_(%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3))"); + assertEquals(250, s.getBytes(UTF_CS).length); + } + + @Test + public void testMax() { + long pointer = Integer.MAX_VALUE; + int storedPointer = (int) (pointer + 100); + assertTrue(storedPointer < 0); + assertEquals(pointer + 100, Helper.toUnsignedLong(storedPointer)); + } +} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/search/StringIndexTest.java b/core/src/test/java/com/graphhopper/search/StringIndexTest.java deleted file mode 100644 index 7c983a436e5..00000000000 --- a/core/src/test/java/com/graphhopper/search/StringIndexTest.java +++ /dev/null @@ -1,272 +0,0 @@ -package com.graphhopper.search; - -import com.carrotsearch.hppc.LongArrayList; -import com.graphhopper.storage.RAMDirectory; -import com.graphhopper.util.Helper; -import org.junit.jupiter.api.RepeatedTest; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.util.*; - -import static com.graphhopper.search.StringIndex.MAX_UNIQUE_KEYS; -import static org.junit.jupiter.api.Assertions.*; - -public class StringIndexTest { - - private StringIndex create() { - return new StringIndex(new RAMDirectory(), 1000, -1).create(1000); - } - - Map createMap(String... strings) { - if (strings.length % 2 != 0) - throw new IllegalArgumentException("Cannot create map from strings " + Arrays.toString(strings)); - Map map = new LinkedHashMap<>(); - for (int i = 0; i < strings.length; i += 2) { - map.put(strings[i], strings[i + 1]); - } - return map; - } - - @Test - public void putSame() { - StringIndex index = create(); - long aPointer = index.add(createMap("a", "same name", "b", "same name")); - - assertNull(index.get(aPointer, "")); - assertEquals("same name", index.get(aPointer, "a")); - assertEquals("same name", index.get(aPointer, "b")); - assertNull(index.get(aPointer, "c")); - - index = create(); - aPointer = index.add(createMap("a", "a name", "b", "same name")); - assertEquals("a name", index.get(aPointer, "a")); - } - - @Test - public void putAB() { - StringIndex index = create(); - long aPointer = index.add(createMap("a", "a name", "b", "b name")); - - assertNull(index.get(aPointer, "")); - assertEquals("a name", index.get(aPointer, "a")); - assertEquals("b name", index.get(aPointer, "b")); - } - - @Test - public void putEmpty() { - StringIndex index = create(); - assertEquals(1, index.add(createMap("", ""))); - assertEquals(5, index.add(createMap("", null))); - assertEquals(9, index.add(createMap(null, null))); - assertEquals("", index.get(0, "")); - - assertEquals(13, index.add(createMap("else", "else"))); - } - - @Test - public void putMany() { - StringIndex index = create(); - long aPointer = 0, tmpPointer = 0; - - for (int i = 0; i < 10000; i++) { - aPointer = index.add(createMap("a", "a name " + i, "b", "b name " + i, "c", "c name " + i)); - if (i == 567) - tmpPointer = aPointer; - } - - assertEquals("b name 9999", index.get(aPointer, "b")); - assertEquals("c name 9999", index.get(aPointer, "c")); - - assertEquals("a name 567", index.get(tmpPointer, "a")); - assertEquals("b name 567", index.get(tmpPointer, "b")); - assertEquals("c name 567", index.get(tmpPointer, "c")); - } - - @Test - public void putManyKeys() { - StringIndex index = create(); - // one key is already stored => empty key - for (int i = 1; i < MAX_UNIQUE_KEYS; i++) { - index.add(createMap("a" + i, "a name")); - } - try { - index.add(createMap("new", "a name")); - fail(); - } catch (IllegalArgumentException ex) { - } - } - - @Test - public void putDuplicate() { - StringIndex index = create(); - long aPointer = index.add(createMap("a", "longer name", "b", "longer name")); - long bPointer = index.add(createMap("c", "longer other name")); - // value storage: 1 byte for count, 2 bytes for keyIndex and 4 bytes for delta of dup_marker and 3 bytes (keyIndex + length for "longer name") - assertEquals(aPointer + 1 + (2 + 4) + 3 + "longer name".getBytes(Helper.UTF_CS).length, bPointer); - // no de-duplication as too short: - long cPointer = index.add(createMap("temp", "temp")); - assertEquals(bPointer + 1 + 3 + "longer other name".getBytes(Helper.UTF_CS).length, cPointer); - assertEquals("longer name", index.get(aPointer, "a")); - assertEquals("longer name", index.get(aPointer, "b")); - assertEquals("longer other name", index.get(bPointer, "c")); - assertEquals("temp", index.get(cPointer, "temp")); - - index = create(); - index.add(createMap("a", "longer name", "b", "longer name")); - bPointer = index.add(createMap("a", "longer name", "b", "longer name")); - cPointer = index.add(createMap("a", "longer name", "b", "longer name")); - assertEquals(bPointer, cPointer); - - assertEquals("{a=longer name, b=longer name}", index.getAll(aPointer).toString()); - assertEquals("{a=longer name, b=longer name}", index.getAll(cPointer).toString()); - } - - @Test - public void testNoErrorOnLargeName() { - StringIndex index = create(); - // 127 => bytes.length == 254 - String str = ""; - for (int i = 0; i < 127; i++) { - str += "ß"; - } - long result = index.add(createMap("", str)); - assertEquals(127, index.get(result, "").length()); - } - - @Test - public void testTooLongNameNoError() { - StringIndex index = create(); - index.throwExceptionIfTooLong = true; - try { - index.add(createMap("", "Бухарестская улица (http://ru.wikipedia.org/wiki/%D0%91%D1%83%D1%85%D0%B0%D1%80%D0%B5%D1%81%D1%82%D1%81%D0%BA%D0%B0%D1%8F_%D1%83%D0%BB%D0%B8%D1%86%D0%B0_(%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3))")); - fail(); - } catch (IllegalStateException ex) { - } - - String str = "sdfsdfds"; - for (int i = 0; i < 256 * 3; i++) { - str += "Б"; - } - try { - index.add(createMap("", str)); - fail(); - } catch (IllegalStateException ex) { - } - - index.throwExceptionIfTooLong = false; - long pointer = index.add(createMap("", "Бухарестская улица (http://ru.wikipedia.org/wiki/%D0%91%D1%83%D1%85%D0%B0%D1%80%D0%B5%D1%81%D1%82%D1%81%D0%BA%D0%B0%D1%8F_%D1%83%D0%BB%D0%B8%D1%86%D0%B0_(%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3))")); - assertTrue(index.get(pointer, "").startsWith("Бухарестская улица (h")); - } - - @Test - public void testFlush() { - String location = "./target/stringindex-store"; - Helper.removeDir(new File(location)); - - StringIndex index = new StringIndex(new RAMDirectory(location, true).create(), 1000, -1).create(1000); - long pointer = index.add(createMap("", "test")); - index.flush(); - index.close(); - - index = new StringIndex(new RAMDirectory(location, true), 1000, -1); - assertTrue(index.loadExisting()); - assertEquals("test", index.get(pointer, "")); - // make sure bytePointer is correctly set after loadExisting - long newPointer = index.add(createMap("", "testing")); - assertEquals(pointer + 1 + 3 + "test".getBytes().length, newPointer, newPointer + ">" + pointer); - index.close(); - - Helper.removeDir(new File(location)); - } - - @Test - public void testLoadKeys() { - String location = "./target/stringindex-store"; - Helper.removeDir(new File(location)); - - StringIndex index = new StringIndex(new RAMDirectory(location, true).create(), 1000, -1).create(1000); - long pointerA = index.add(createMap("c", "test value")); - assertEquals(2, index.getKeys().size()); - long pointerB = index.add(createMap("a", "value", "b", "another value")); - // empty string is always the first key - assertEquals("[, c, a, b]", index.getKeys().toString()); - index.flush(); - index.close(); - - index = new StringIndex(new RAMDirectory(location, true), 1000, -1); - assertTrue(index.loadExisting()); - assertEquals("[, c, a, b]", index.getKeys().toString()); - assertEquals("test value", index.get(pointerA, "c")); - assertNull(index.get(pointerA, "b")); - - assertNull(index.get(pointerB, "")); - assertEquals("value", index.get(pointerB, "a")); - assertEquals("another value", index.get(pointerB, "b")); - assertEquals("{a=value, b=another value}", index.getAll(pointerB).toString()); - index.close(); - - Helper.removeDir(new File(location)); - } - - @Test - public void testEmptyKey() { - StringIndex index = create(); - long pointerA = index.add(createMap("", "test value")); - long pointerB = index.add(createMap("a", "value", "b", "another value")); - - assertEquals("test value", index.get(pointerA, "")); - assertNull(index.get(pointerA, "a")); - - assertEquals("value", index.get(pointerB, "a")); - assertNull(index.get(pointerB, "")); - } - - @RepeatedTest(20) - public void testRandom() { - long seed = new Random().nextLong(); - try { - StringIndex index = create(); - Random random = new Random(seed); - List keys = createRandomList(random, "_key", 1000); - List values = createRandomList(random, "_value", 5000); - - int size = 20000; - LongArrayList pointers = new LongArrayList(size); - for (int i = 0; i < size; i++) { - Map map = createRandomMap(random, keys, values); - long pointer = index.add(map); - try { - assertEquals(map.size(), index.getAll(pointer).size(), "" + i); - } catch (Exception ex) { - throw new RuntimeException(i + " " + map + ", " + pointer, ex); - } - pointers.add(pointer); - } - - for (int i = 0; i < size; i++) { - Map map = index.getAll(pointers.get(i)); - assertTrue(map.size() > 0, i + " " + map); - } - } catch (Throwable t) { - throw new RuntimeException("seed:" + seed + ", error:" + t); - } - } - - private List createRandomList(Random random, String postfix, int size) { - List list = new ArrayList<>(); - for (int i = 0; i < size; i++) { - list.add(random.nextInt(size * 5) + postfix); - } - return list; - } - - private Map createRandomMap(Random random, List keys, List values) { - int count = random.nextInt(10) + 2; - Map map = new HashMap<>(); - for (int i = 0; i < count; i++) { - map.put(keys.get(random.nextInt(keys.size())), values.get(random.nextInt(values.size()))); - } - return map; - } -} \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/storage/AbstractGraphStorageTester.java b/core/src/test/java/com/graphhopper/storage/AbstractGraphStorageTester.java index 4b7cb689133..d7736765d51 100644 --- a/core/src/test/java/com/graphhopper/storage/AbstractGraphStorageTester.java +++ b/core/src/test/java/com/graphhopper/storage/AbstractGraphStorageTester.java @@ -19,7 +19,11 @@ import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.util.*; import com.graphhopper.util.shapes.BBox; import org.junit.jupiter.api.AfterEach; @@ -27,15 +31,14 @@ import org.junit.jupiter.api.Test; import java.io.File; -import java.util.ArrayList; -import java.util.List; -import static com.graphhopper.routing.util.EncodingManager.getKey; +import static com.graphhopper.search.KVStorage.KeyValue.STREET_NAME; +import static com.graphhopper.search.KVStorage.KeyValue.createKV; import static org.junit.jupiter.api.Assertions.*; /** * Abstract test class to be extended for implementations of the Graph interface. Graphs - * implementing GraphStorage should extend {@link GraphHopperStorageTest} instead. + * implementing GraphStorage should extend {@link BaseGraphTest} instead. *

* * @author Peter Karich @@ -44,15 +47,22 @@ public abstract class AbstractGraphStorageTester { private final String locationParent = "./target/graphstorage"; protected int defaultSize = 100; protected String defaultGraphLoc = "./target/graphstorage/default"; - protected FlagEncoder carEncoder = createCarFlagEncoder(); - protected EncodingManager encodingManager = new EncodingManager.Builder().add(carEncoder).add(FlagEncoders.createFoot()).build(); - protected BooleanEncodedValue carAccessEnc = carEncoder.getAccessEnc(); - protected DecimalEncodedValue carAvSpeedEnc = carEncoder.getAverageSpeedEnc(); - protected FlagEncoder footEncoder = encodingManager.getEncoder("foot"); - protected BooleanEncodedValue footAccessEnc = footEncoder.getAccessEnc(); + protected BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + protected DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + protected BooleanEncodedValue footAccessEnc = new SimpleBooleanEncodedValue("foot_access", true); + protected DecimalEncodedValue footSpeedEnc = new DecimalEncodedValueImpl("foot_speed", 4, 1, true); + protected EncodingManager encodingManager = createEncodingManager(); + + protected EncodingManager createEncodingManager() { + return new EncodingManager.Builder() + .add(carAccessEnc).add(carSpeedEnc) + .add(footAccessEnc).add(footSpeedEnc) + .build(); + } + protected BaseGraph graph; - EdgeFilter carOutFilter = AccessFilter.outEdges(carEncoder.getAccessEnc()); - EdgeFilter carInFilter = AccessFilter.inEdges(carEncoder.getAccessEnc()); + EdgeFilter carOutFilter = AccessFilter.outEdges(carAccessEnc); + EdgeFilter carInFilter = AccessFilter.inEdges(carAccessEnc); EdgeExplorer carOutExplorer; EdgeExplorer carInExplorer; EdgeExplorer carAllExplorer; @@ -87,10 +97,6 @@ public static int getIdOf(Graph g, double latitude, double longitude) { throw new IllegalArgumentException("did not find node with location " + (float) latitude + "," + (float) longitude); } - FlagEncoder createCarFlagEncoder() { - return FlagEncoders.createCar(); - } - protected BaseGraph createGHStorage() { BaseGraph g = createGHStorage(defaultGraphLoc, false); carOutExplorer = g.createEdgeExplorer(carOutFilter); @@ -260,7 +266,8 @@ public void testUpdateUnidirectional() { @Test public void testCopyProperties() { graph = createGHStorage(); - EdgeIteratorState edge = graph.edge(1, 3).setDistance(10).set(carAccessEnc, true, false).setName("testing").setWayGeometry(Helper.createPointList(1, 2)); + EdgeIteratorState edge = graph.edge(1, 3).setDistance(10).set(carAccessEnc, true, false). + setKeyValues(createKV(STREET_NAME, "testing")).setWayGeometry(Helper.createPointList(1, 2)); EdgeIteratorState newEdge = graph.edge(1, 3).setDistance(10).set(carAccessEnc, true, false); newEdge.copyPropertiesFrom(edge); @@ -431,19 +438,19 @@ public void testBounds() { public void testFlags() { graph = createGHStorage(); graph.edge(0, 1).set(carAccessEnc, true, true).setDistance(10) - .set(carAvSpeedEnc, 100); + .set(carSpeedEnc, 100); graph.edge(2, 3).set(carAccessEnc, true, false).setDistance(10) - .set(carAvSpeedEnc, 10); + .set(carSpeedEnc, 10); EdgeIterator iter = carAllExplorer.setBaseNode(0); assertTrue(iter.next()); - assertEquals(100, iter.get(carAvSpeedEnc), 1); + assertEquals(100, iter.get(carSpeedEnc), 1); assertTrue(iter.get(carAccessEnc)); assertTrue(iter.getReverse(carAccessEnc)); iter = carAllExplorer.setBaseNode(2); assertTrue(iter.next()); - assertEquals(10, iter.get(carAvSpeedEnc), 1); + assertEquals(10, iter.get(carSpeedEnc), 1); assertTrue(iter.get(carAccessEnc)); assertFalse(iter.getReverse(carAccessEnc)); @@ -612,7 +619,7 @@ public void testFootMix() { EdgeIteratorState edge = graph.edge(0, 3).setDistance(10); edge.set(footAccessEnc, true, true); edge.set(carAccessEnc, true, true); - EdgeExplorer footOutExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(footEncoder.getAccessEnc())); + EdgeExplorer footOutExplorer = graph.createEdgeExplorer(AccessFilter.outEdges(footAccessEnc)); assertEquals(GHUtility.asSet(3, 1), GHUtility.getNeighbors(footOutExplorer.setBaseNode(0))); assertEquals(GHUtility.asSet(3, 2), GHUtility.getNeighbors(carOutExplorer.setBaseNode(0))); } @@ -645,13 +652,13 @@ public void testGetAllEdges() { } @Test - public void testStringIndex() { + public void testKVStorage() { graph = createGHStorage(); EdgeIteratorState iter1 = graph.edge(0, 1).setDistance(10).set(carAccessEnc, true, true); - iter1.setName("named street1"); + iter1.setKeyValues(createKV(STREET_NAME, "named street1")); EdgeIteratorState iter2 = graph.edge(0, 1).setDistance(10).set(carAccessEnc, true, true); - iter2.setName("named street2"); + iter2.setKeyValues(createKV(STREET_NAME, "named street2")); assertEquals(graph.getEdgeIteratorState(iter1.getEdge(), iter1.getAdjNode()).getName(), "named street1"); assertEquals(graph.getEdgeIteratorState(iter2.getEdge(), iter2.getAdjNode()).getName(), "named street2"); @@ -659,10 +666,15 @@ public void testStringIndex() { @Test public void test8AndMoreBytesForEdgeFlags() { - List list = new ArrayList<>(); - list.add(FlagEncoders.createCar(new PMap("name=car0|speed_bits=29|speed_factor=0.001"))); - list.add(FlagEncoders.createCar(new PMap("speed_bits=29|speed_factor=0.001"))); - EncodingManager manager = EncodingManager.create(list); + BooleanEncodedValue access0Enc = new SimpleBooleanEncodedValue("car0_access", true); + DecimalEncodedValue speed0Enc = new DecimalEncodedValueImpl("car0_speed", 29, 0.001, false); + BooleanEncodedValue access1Enc = new SimpleBooleanEncodedValue("car1_access", true); + DecimalEncodedValue speed1Enc = new DecimalEncodedValueImpl("car1_speed", 29, 0.001, false); + + EncodingManager manager = EncodingManager.start() + .add(access0Enc).add(speed0Enc) + .add(access1Enc).add(speed1Enc) + .build(); graph = new BaseGraph.Builder(manager).create(); EdgeIteratorState edge = graph.edge(0, 1); @@ -675,33 +687,29 @@ public void test8AndMoreBytesForEdgeFlags() { graph = new BaseGraph.Builder(manager).create(); - DecimalEncodedValue avSpeed0Enc = manager.getDecimalEncodedValue(getKey("car0", "average_speed")); - BooleanEncodedValue access0Enc = manager.getBooleanEncodedValue(getKey("car0", "access")); - DecimalEncodedValue avSpeed1Enc = manager.getDecimalEncodedValue(getKey("car", "average_speed")); - BooleanEncodedValue access1Enc = manager.getBooleanEncodedValue(getKey("car", "access")); edge = graph.edge(0, 1); - GHUtility.setSpeed(99.123, true, true, list.get(0), edge); - assertEquals(99.123, edge.get(avSpeed0Enc), 1e-3); + GHUtility.setSpeed(99.123, true, true, access0Enc, speed0Enc, edge); + assertEquals(99.123, edge.get(speed0Enc), 1e-3); EdgeIteratorState edgeIter = GHUtility.getEdge(graph, 1, 0); - assertEquals(99.123, edgeIter.get(avSpeed0Enc), 1e-3); + assertEquals(99.123, edgeIter.get(speed0Enc), 1e-3); assertTrue(edgeIter.get(access0Enc)); assertTrue(edgeIter.getReverse(access0Enc)); edge = graph.edge(2, 3); - GHUtility.setSpeed(44.123, true, false, list.get(1), edge); - assertEquals(44.123, edge.get(avSpeed1Enc), 1e-3); + GHUtility.setSpeed(44.123, true, false, access1Enc, speed1Enc, edge); + assertEquals(44.123, edge.get(speed1Enc), 1e-3); edgeIter = GHUtility.getEdge(graph, 3, 2); - assertEquals(44.123, edgeIter.get(avSpeed1Enc), 1e-3); - assertEquals(44.123, edgeIter.getReverse(avSpeed1Enc), 1e-3); + assertEquals(44.123, edgeIter.get(speed1Enc), 1e-3); + assertEquals(44.123, edgeIter.getReverse(speed1Enc), 1e-3); assertFalse(edgeIter.get(access1Enc)); assertTrue(edgeIter.getReverse(access1Enc)); - list.clear(); - list.add(FlagEncoders.createCar(new PMap("name=car0|speed_bits=29|speed_factor=0.001"))); - list.add(FlagEncoders.createCar(new PMap("speed_bits=29|speed_factor=0.001"))); - list.add(FlagEncoders.createCar(new PMap("name=car2|speed_bits=30|speed_factor=0.001"))); - manager = EncodingManager.create(list); + manager = EncodingManager.start() + .add(new SimpleBooleanEncodedValue("car0_access", true)).add(new DecimalEncodedValueImpl("car0_speed", 29, 0.001, false)) + .add(new SimpleBooleanEncodedValue("car1_access", true)).add(new DecimalEncodedValueImpl("car1_speed", 29, 0.001, false)) + .add(new SimpleBooleanEncodedValue("car2_access", true)).add(new DecimalEncodedValueImpl("car2_speed", 30, 0.001, false)) + .build(); graph = new BaseGraph.Builder(manager).create(); edgeIter = graph.edge(0, 1).set(access0Enc, true, false); assertTrue(edgeIter.get(access0Enc)); diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageTest.java b/core/src/test/java/com/graphhopper/storage/BaseGraphTest.java similarity index 79% rename from core/src/test/java/com/graphhopper/storage/GraphHopperStorageTest.java rename to core/src/test/java/com/graphhopper/storage/BaseGraphTest.java index cdf3ad8a277..6c49fe793b6 100644 --- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageTest.java +++ b/core/src/test/java/com/graphhopper/storage/BaseGraphTest.java @@ -17,10 +17,17 @@ */ package com.graphhopper.storage; +import com.graphhopper.routing.ev.EnumEncodedValue; +import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.search.KVStorage.KeyValue; import com.graphhopper.util.*; import com.graphhopper.util.shapes.BBox; import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.List; + +import static com.graphhopper.search.KVStorage.KeyValue.STREET_NAME; import static com.graphhopper.util.EdgeIteratorState.REVERSE_STATE; import static com.graphhopper.util.FetchMode.*; import static org.junit.jupiter.api.Assertions.*; @@ -28,7 +35,7 @@ /** * @author Peter Karich */ -public class GraphHopperStorageTest extends AbstractGraphStorageTester { +public class BaseGraphTest extends AbstractGraphStorageTester { @Override public BaseGraph createGHStorage(String location, boolean enabled3D) { // reduce segment size in order to test the case where multiple segments come into the game @@ -57,13 +64,20 @@ public void testSave_and_fileFormat() { EdgeIteratorState iter2 = graph.edge(0, 1).setDistance(100).set(carAccessEnc, true, true); iter2.setWayGeometry(Helper.createPointList3D(1.5, 1, 0, 2, 3, 0)); EdgeIteratorState iter1 = graph.edge(0, 2).setDistance(200).set(carAccessEnc, true, true); + EdgeIteratorState iter3 = graph.edge(3, 4); iter1.setWayGeometry(Helper.createPointList3D(3.5, 4.5, 0, 5, 6, 0)); graph.edge(9, 10).setDistance(200).set(carAccessEnc, true, true); graph.edge(9, 11).setDistance(200).set(carAccessEnc, true, true); graph.edge(1, 2).setDistance(120).set(carAccessEnc, true, false); - iter1.setName("named street1"); - iter2.setName("named street2"); + iter1.setKeyValues(KeyValue.createKV(STREET_NAME, "named street1")); + iter2.setKeyValues(KeyValue.createKV(STREET_NAME, "named street2")); + + List list = new ArrayList<>(); + list.add(new KeyValue("keyA", "FORWARD", true, false)); + list.add(new KeyValue("keyB", "BACKWARD", false, true)); + list.add(new KeyValue("keyC", "BOTH", true, true)); + iter3.setKeyValues(list); checkGraph(graph); graph.flush(); @@ -77,7 +91,18 @@ public void testSave_and_fileFormat() { assertEquals("named street1", graph.getEdgeIteratorState(iter1.getEdge(), iter1.getAdjNode()).getName()); assertEquals("named street2", graph.getEdgeIteratorState(iter2.getEdge(), iter2.getAdjNode()).getName()); - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(3, 4).setDistance(123)). + iter3 = graph.getEdgeIteratorState(iter3.getEdge(), iter3.getAdjNode()); + assertEquals(list, iter3.getKeyValues()); + assertEquals(list, iter3.detach(true).getKeyValues()); + + assertEquals("FORWARD", iter3.getValue("keyA")); + assertNull(iter3.getValue("keyB")); + assertEquals("BOTH", iter3.getValue("keyC")); + assertNull(iter3.detach(true).getValue("keyA")); + assertEquals("BACKWARD", iter3.detach(true).getValue("keyB")); + assertEquals("BOTH", iter3.detach(true).getValue("keyC")); + + GHUtility.setSpeed(60, true, true, carAccessEnc, carSpeedEnc, graph.edge(3, 4).setDistance(123)). setWayGeometry(Helper.createPointList3D(4.4, 5.5, 0, 6.6, 7.7, 0)); checkGraph(graph); } @@ -198,9 +223,9 @@ public void testDecoupledEdgeIteratorStates() { Graph graph = storage.getBaseGraph(); IntsRef ref = encodingManager.createEdgeFlags(); ref.ints[0] = 12; - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 2).setDistance(10)).setFlags(ref); + GHUtility.setSpeed(60, true, true, carAccessEnc, carSpeedEnc, graph.edge(1, 2).setDistance(10)).setFlags(ref); ref.ints[0] = 13; - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(1, 3).setDistance(10)).setFlags(ref); + GHUtility.setSpeed(60, true, true, carAccessEnc, carSpeedEnc, graph.edge(1, 3).setDistance(10)).setFlags(ref); EdgeIterator iter = graph.createEdgeExplorer().setBaseNode(1); assertTrue(iter.next()); @@ -217,7 +242,7 @@ public void testDecoupledEdgeIteratorStates() { @Test public void testEdgeKey() { BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 1).setDistance(10)); + GHUtility.setSpeed(60, true, true, carAccessEnc, carSpeedEnc, g.edge(0, 1).setDistance(10)); // storage direction assertEdge(g.getEdgeIteratorState(0, Integer.MIN_VALUE), 0, 1, false, 0, 0); // reverse direction @@ -231,7 +256,7 @@ public void testEdgeKey() { @Test public void testEdgeKey_loop() { BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 0).setDistance(10)); + GHUtility.setSpeed(60, true, true, carAccessEnc, carSpeedEnc, g.edge(0, 0).setDistance(10)); // storage direction assertEdge(g.getEdgeIteratorState(0, Integer.MIN_VALUE), 0, 0, false, 0, 0); // reverse direction cannot be retrieved, we get forward direction anyway @@ -257,4 +282,26 @@ public void outOfBounds() { assertThrows(IllegalArgumentException.class, () -> graph.getEdgeIteratorState(0, Integer.MIN_VALUE)); } + @Test + public void setGetFlagsRaw() { + BaseGraph graph = new BaseGraph.Builder(1).create(); + EdgeIteratorState edge = graph.edge(0, 1); + IntsRef flags = new IntsRef(graph.getIntsForFlags()); + flags.ints[0] = 10; + edge.setFlags(flags); + assertEquals(10, edge.getFlags().ints[0]); + flags.ints[0] = 9; + edge.setFlags(flags); + assertEquals(9, edge.getFlags().ints[0]); + } + + @Test + public void setGetFlags() { + BaseGraph graph = createGHStorage(); + EnumEncodedValue rcEnc = encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); + EdgeIteratorState edge = graph.edge(0, 1).set(rcEnc, RoadClass.BRIDLEWAY); + assertEquals(RoadClass.BRIDLEWAY, edge.get(rcEnc)); + edge.set(rcEnc, RoadClass.CORRIDOR); + assertEquals(RoadClass.CORRIDOR, edge.get(rcEnc)); + } } diff --git a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageWithTurnCostsTest.java b/core/src/test/java/com/graphhopper/storage/BaseGraphWithTurnCostsTest.java similarity index 85% rename from core/src/test/java/com/graphhopper/storage/GraphHopperStorageWithTurnCostsTest.java rename to core/src/test/java/com/graphhopper/storage/BaseGraphWithTurnCostsTest.java index 2f632b68bd0..addeff1bf63 100644 --- a/core/src/test/java/com/graphhopper/storage/GraphHopperStorageWithTurnCostsTest.java +++ b/core/src/test/java/com/graphhopper/storage/BaseGraphWithTurnCostsTest.java @@ -17,27 +17,34 @@ */ package com.graphhopper.storage; -import com.graphhopper.routing.ev.EncodedValueLookup; +import com.graphhopper.routing.ev.DecimalEncodedValue; import com.graphhopper.routing.ev.TurnCost; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.search.KVStorage.KeyValue; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.Helper; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.Test; import java.util.Random; +import static com.graphhopper.search.KVStorage.KeyValue.STREET_NAME; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Karl Hübner */ -public class GraphHopperStorageWithTurnCostsTest extends GraphHopperStorageTest { +public class BaseGraphWithTurnCostsTest extends BaseGraphTest { + + private DecimalEncodedValue turnCostEnc; + @Override - FlagEncoder createCarFlagEncoder() { - return FlagEncoders.createCar(new PMap().putObject("max_turn_costs", 1400)); + protected EncodingManager createEncodingManager() { + turnCostEnc = TurnCost.create("car", 1400); + return EncodingManager.start() + .add(carAccessEnc).add(carSpeedEnc).addTurnCostEncodedValue(turnCostEnc) + .add(footAccessEnc).add(footSpeedEnc) + .build(); } @Override @@ -72,8 +79,8 @@ public void testSave_and_fileFormat() { setTurnCost(iter2.getEdge(), 0, iter1.getEdge(), 666); setTurnCost(iter1.getEdge(), 1, iter2.getEdge(), 815); - iter1.setName("named street1"); - iter2.setName("named street2"); + iter1.setKeyValues(KeyValue.createKV(STREET_NAME, "named street1")); + iter2.setKeyValues(KeyValue.createKV(STREET_NAME, "named street2")); checkGraph(graph); graph.flush(); @@ -156,10 +163,10 @@ public void testInitializeTurnCost() { } private double getTurnCost(EdgeIteratorState fromEdge, int viaNode, EdgeIteratorState toEdge) { - return graph.getTurnCostStorage().get(((EncodedValueLookup) encodingManager).getDecimalEncodedValue(TurnCost.key("car")), toEdge.getEdge(), viaNode, fromEdge.getEdge()); + return graph.getTurnCostStorage().get(turnCostEnc, toEdge.getEdge(), viaNode, fromEdge.getEdge()); } private void setTurnCost(int fromEdge, int viaNode, int toEdge, int cost) { - graph.getTurnCostStorage().set(((EncodedValueLookup) encodingManager).getDecimalEncodedValue(TurnCost.key("car")), fromEdge, viaNode, toEdge, cost); + graph.getTurnCostStorage().set(turnCostEnc, fromEdge, viaNode, toEdge, cost); } } diff --git a/core/src/test/java/com/graphhopper/storage/CHMatrixStorageTest.java b/core/src/test/java/com/graphhopper/storage/CHMatrixStorageTest.java index 4c5a7140f78..6ec48d738f3 100644 --- a/core/src/test/java/com/graphhopper/storage/CHMatrixStorageTest.java +++ b/core/src/test/java/com/graphhopper/storage/CHMatrixStorageTest.java @@ -15,8 +15,7 @@ class CHMatrixStorageTest { void setAndGetLevels() { RAMDirectory dir = new RAMDirectory(); CHStorage store = new CHStorage(dir, "ch1", -1, false); - store.create(); - store.init(30, 5); + store.create(30, 5); assertEquals(0, store.getLevel(store.toNodePointer(10))); store.setLevel(store.toNodePointer(10), 100); assertEquals(100, store.getLevel(store.toNodePointer(10))); @@ -30,16 +29,15 @@ void createAndLoad(@TempDir Path path) { GHDirectory dir = new GHDirectory(path.toAbsolutePath().toString(), DAType.RAM_INT_STORE); CHStorage chStorage = new CHStorage(dir, "car", -1, false); // we have to call create, because we want to create a new storage not load an existing one - chStorage.create(); + chStorage.create(5, 3); // init is needed as well, because we have to set the nodes capacity and we cannot do this in create() yet. // we can also not use init instead of create, because currently GraphHopperStorage needs to 'create' all // its data objects. if we want to change this lifecycle we need to change this in GraphHopperStorage first - chStorage.init(5, 3); - assertEquals(0, chStorage.shortcutNodeBased(0, 1, PrepareEncoder.getScFwdDir(), 10, 20,10,3, 5)); - assertEquals(1, chStorage.shortcutNodeBased(1, 2, PrepareEncoder.getScFwdDir(), 11, 21,11,4, 6)); - assertEquals(2, chStorage.shortcutNodeBased(2, 3, PrepareEncoder.getScFwdDir(), 12, 22,12,5, 7)); + assertEquals(0, chStorage.shortcutNodeBased(0, 1, PrepareEncoder.getScFwdDir(), 10, 20, 10, 3, 5)); + assertEquals(1, chStorage.shortcutNodeBased(1, 2, PrepareEncoder.getScFwdDir(), 11, 21, 11, 4, 6)); + assertEquals(2, chStorage.shortcutNodeBased(2, 3, PrepareEncoder.getScFwdDir(), 12, 22, 12, 5, 7)); // exceeding the number of expected shortcuts is ok, the container will just grow - assertEquals(3, chStorage.shortcutNodeBased(3, 4, PrepareEncoder.getScFwdDir(), 13,23,13, 6, 8)); + assertEquals(3, chStorage.shortcutNodeBased(3, 4, PrepareEncoder.getScFwdDir(), 13, 23, 13, 6, 8)); assertEquals(5, chStorage.getNodes()); assertEquals(4, chStorage.getShortcuts()); chStorage.flush(); @@ -66,7 +64,7 @@ void createAndLoad(@TempDir Path path) { @Test public void testBigWeight() { CHStorage g = new CHStorage(new RAMDirectory(), "abc", 1024, false); - g.shortcutNodeBased(0, 0, 0, 10,20,30, 0, 1); + g.shortcutNodeBased(0, 0, 0, 10, 20, 30, 0, 1); g.setWeight(0, Integer.MAX_VALUE / 1000d + 1000); assertEquals(Integer.MAX_VALUE / 1000d + 1000, g.getWeight(0)); @@ -85,7 +83,7 @@ public void testBigWeight() { @Test public void testBigDistance() { CHStorage g = new CHStorage(new RAMDirectory(), "abc", 1024, false); - g.shortcutNodeBased(0, 0, 0, 10,20,30, 0, 1); + g.shortcutNodeBased(0, 0, 0, 10, 20, 30, 0, 1); g.setDistance(0, Integer.MAX_VALUE / 1000d + 1000); assertEquals(Integer.MAX_VALUE / 1000d + 1000, g.getDistance(0)); @@ -104,7 +102,7 @@ public void testBigDistance() { @Test public void testBigTime() { CHStorage g = new CHStorage(new RAMDirectory(), "abc", 1024, false); - g.shortcutNodeBased(0, 0, 0, 10,20,30, 0, 1); + g.shortcutNodeBased(0, 0, 0, 10, 20, 30, 0, 1); g.setTime(0, Integer.MAX_VALUE / 1000L + 1000); assertEquals(Integer.MAX_VALUE / 1000L + 1000, g.getTime(0)); @@ -112,5 +110,5 @@ public void testBigTime() { g.setTime(0, ((long) Integer.MAX_VALUE << 1) / 1000L); assertEquals(((long) Integer.MAX_VALUE << 1) / 1000L, g.getTime(0)); } - + } \ No newline at end of file diff --git a/core/src/test/java/com/graphhopper/storage/CHStorageTest.java b/core/src/test/java/com/graphhopper/storage/CHStorageTest.java index bbea4ebde17..4524f8b7f3c 100644 --- a/core/src/test/java/com/graphhopper/storage/CHStorageTest.java +++ b/core/src/test/java/com/graphhopper/storage/CHStorageTest.java @@ -15,8 +15,7 @@ class CHStorageTest { void setAndGetLevels() { RAMDirectory dir = new RAMDirectory(); CHStorage store = new CHStorage(dir, "ch1", -1, false); - store.create(); - store.init(30, 5); + store.create(30, 5); assertEquals(0, store.getLevel(store.toNodePointer(10))); store.setLevel(store.toNodePointer(10), 100); assertEquals(100, store.getLevel(store.toNodePointer(10))); @@ -30,11 +29,7 @@ void createAndLoad(@TempDir Path path) { GHDirectory dir = new GHDirectory(path.toAbsolutePath().toString(), DAType.RAM_INT_STORE); CHStorage chStorage = new CHStorage(dir, "car", -1, false); // we have to call create, because we want to create a new storage not load an existing one - chStorage.create(); - // init is needed as well, because we have to set the nodes capacity and we cannot do this in create() yet. - // we can also not use init instead of create, because currently GraphHopperStorage needs to 'create' all - // its data objects. if we want to change this lifecycle we need to change this in GraphHopperStorage first - chStorage.init(5, 3); + chStorage.create(5, 3); assertEquals(0, chStorage.shortcutNodeBased(0, 1, PrepareEncoder.getScFwdDir(), 10, 3, 5)); assertEquals(1, chStorage.shortcutNodeBased(1, 2, PrepareEncoder.getScFwdDir(), 11, 4, 6)); assertEquals(2, chStorage.shortcutNodeBased(2, 3, PrepareEncoder.getScFwdDir(), 12, 5, 7)); diff --git a/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java b/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java deleted file mode 100644 index 675ea269d38..00000000000 --- a/core/src/test/java/com/graphhopper/storage/GraphEdgeIdFinderTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.storage; - -import com.graphhopper.routing.util.*; -import com.graphhopper.storage.index.LocationIndex; -import com.graphhopper.storage.index.LocationIndexTree; -import com.graphhopper.util.GHUtility; -import org.junit.jupiter.api.Test; - -import java.util.Set; -import java.util.TreeSet; - -import static com.graphhopper.util.GHUtility.updateDistancesFor; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -/** - * @author Peter Karich - */ -public class GraphEdgeIdFinderTest { - - @Test - public void testParseStringHints() { - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); - BaseGraph graph = new BaseGraph.Builder(em).create(); - // 0-1-2 - // | | - // 3-4 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 3).setDistance(1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 4).setDistance(1)); - updateDistancesFor(graph, 0, 0.01, 0.00); - updateDistancesFor(graph, 1, 0.01, 0.01); - updateDistancesFor(graph, 2, 0.01, 0.02); - updateDistancesFor(graph, 3, 0.00, 0.00); - updateDistancesFor(graph, 4, 0.00, 0.01); - - LocationIndex locationIndex = new LocationIndexTree(graph, new RAMDirectory()) - .prepareIndex(); - - GraphEdgeIdFinder graphFinder = new GraphEdgeIdFinder(graph, locationIndex); - GraphEdgeIdFinder.BlockArea blockArea = graphFinder.parseBlockArea("0.01,0.005,1", AccessFilter.allEdges(encoder.getAccessEnc()), 1000 * 1000); - assertEquals("[0]", blockArea.toString(0)); - - // big area => no edgeIds are collected up-front - graphFinder = new GraphEdgeIdFinder(graph, locationIndex); - blockArea = graphFinder.parseBlockArea("0,0,1000", AccessFilter.allEdges(encoder.getAccessEnc()), 1000 * 1000); - assertFalse(blockArea.hasCachedEdgeIds(0)); - } - - @Test - public void testBlockAreasWithPolygon() { - FlagEncoder encoder = FlagEncoders.createCar(); - EncodingManager em = EncodingManager.create(encoder); - BaseGraph graph = new BaseGraph.Builder(em).create(); - - // 00-01-02-03 - // | | - // 04-05-06-07 - // | | - // 08-09-10-11 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1).setDistance(1)); // 0 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2).setDistance(1)); // 1 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3).setDistance(1)); // 2 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 4).setDistance(1)); // 3 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 5).setDistance(1)); // 4 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 5).setDistance(1)); // 5 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6).setDistance(1)); // 6 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(6, 7).setDistance(1)); // 7 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(4, 8).setDistance(1)); // 8 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 9).setDistance(1)); // 9 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(8, 9).setDistance(1)); // 10 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(9, 10).setDistance(1)); // 11 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(10, 11).setDistance(1)); // 12 - - updateDistancesFor(graph, 0, 2, 0); - updateDistancesFor(graph, 1, 2, 1); - updateDistancesFor(graph, 2, 2, 2); - updateDistancesFor(graph, 3, 2, 3); - updateDistancesFor(graph, 4, 1, 0); - updateDistancesFor(graph, 5, 1, 1); - updateDistancesFor(graph, 6, 1, 2); - updateDistancesFor(graph, 7, 1, 3); - updateDistancesFor(graph, 8, 0, 0); - updateDistancesFor(graph, 9, 0, 1); - updateDistancesFor(graph, 10, 0, 2); - updateDistancesFor(graph, 11, 0, 3); - - LocationIndex locationIndex = new LocationIndexTree(graph, new RAMDirectory()) - .prepareIndex(); - - GraphEdgeIdFinder graphFinder = new GraphEdgeIdFinder(graph, locationIndex); - // big value => the polygon is small => force edgeId optimization - double area = 500_000L * 500_000L; - GraphEdgeIdFinder.BlockArea blockArea = graphFinder.parseBlockArea("2.1,1, -1.1,2, 2,3", AccessFilter.allEdges(encoder.getAccessEnc()), area); - assertEquals("[1, 2, 6, 7, 11, 12]", blockArea.toString(0)); - assertEdges(graph, "[1, 2, 6, 7, 11, 12]", blockArea); - - // small value => same polygon is now "large" => do not pre-calculate edgeId set => check only geometries - blockArea = graphFinder.parseBlockArea("2.1,1, 0.9,3, 0.9,2, -0.3,0", AccessFilter.allEdges(encoder.getAccessEnc()), 1000 * 1000); - assertFalse(blockArea.hasCachedEdgeIds(0)); - assertEdges(graph, "[0, 1, 4, 5, 6, 7, 9, 10]", blockArea); - - blockArea = graphFinder.parseBlockArea("1.5,3,100000", AccessFilter.allEdges(encoder.getAccessEnc()), area); - assertEquals("[2, 7]", blockArea.toString(0)); - assertEdges(graph, "[2, 7]", blockArea); - } - - private void assertEdges(Graph g, String assertSetContent, GraphEdgeIdFinder.BlockArea blockArea) { - Set blockedEdges = new TreeSet<>(); - AllEdgesIterator edgeIterator = g.getAllEdges(); - while (edgeIterator.next()) { - if (blockArea.intersects(edgeIterator)) - blockedEdges.add(edgeIterator.getEdge()); - } - assertEquals(assertSetContent, blockedEdges.toString()); - } -} diff --git a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java index e05064df780..8653aa956f8 100644 --- a/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java +++ b/core/src/test/java/com/graphhopper/storage/ShortcutUnpackerTest.java @@ -4,17 +4,13 @@ import com.carrotsearch.hppc.IntArrayList; import com.graphhopper.routing.ch.PrepareEncoder; import com.graphhopper.routing.ch.ShortcutUnpacker; -import com.graphhopper.routing.ev.EncodedValueLookup; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.TurnCostProvider; import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -34,16 +30,20 @@ public class ShortcutUnpackerTest { private static final class Fixture { private final boolean edgeBased; private final EncodingManager encodingManager; - private final FlagEncoder encoder; + private final BooleanEncodedValue accessEnc; + private final DecimalEncodedValue speedEnc; + private final DecimalEncodedValue turnCostEnc; private final BaseGraph graph; private CHStorageBuilder chBuilder; private RoutingCHGraph routingCHGraph; Fixture(boolean edgeBased) { this.edgeBased = edgeBased; - encoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", 10).putObject("speed_two_directions", true)); - encodingManager = EncodingManager.create(encoder); - graph = new BaseGraph.Builder(encodingManager).create(); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, true); + turnCostEnc = TurnCost.create("car", 10); + encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).addTurnCostEncodedValue(turnCostEnc).build(); + graph = new BaseGraph.Builder(encodingManager).withTurnCosts(true).create(); } @Override @@ -53,8 +53,8 @@ public String toString() { private void freeze() { graph.freeze(); - TurnCostProvider turnCostProvider = edgeBased ? new DefaultTurnCostProvider(encoder, graph.getTurnCostStorage()) : NO_TURN_COST_PROVIDER; - CHConfig chConfig = new CHConfig("profile", new FastestWeighting(encoder, turnCostProvider), edgeBased); + TurnCostProvider turnCostProvider = edgeBased ? new DefaultTurnCostProvider(turnCostEnc, graph.getTurnCostStorage()) : NO_TURN_COST_PROVIDER; + CHConfig chConfig = new CHConfig("profile", new FastestWeighting(accessEnc, speedEnc, turnCostProvider), edgeBased); CHStorage chStore = CHStorage.fromGraph(graph, chConfig); chBuilder = new CHStorageBuilder(chStore); routingCHGraph = RoutingCHGraphImpl.fromGraph(graph, chStore, chConfig); @@ -79,15 +79,15 @@ private ShortcutUnpacker createShortcutUnpacker(ShortcutUnpacker.Visitor visitor } private void setTurnCost(int fromEdge, int viaNode, int toEdge, double cost) { - graph.getTurnCostStorage().set(((EncodedValueLookup) encodingManager).getDecimalEncodedValue(TurnCost.key(encoder.toString())), fromEdge, viaNode, toEdge, cost); + graph.getTurnCostStorage().set(turnCostEnc, fromEdge, viaNode, toEdge, cost); } - private void shortcut(int baseNode, int adjNode, int skip1, int skip2, int origFirst, int origLast, boolean reverse) { + private void shortcut(int baseNode, int adjNode, int skip1, int skip2, int origKeyFirst, int origKeyLast, boolean reverse) { // shortcut weight/distance is not important for us here double weight = 1; int flags = reverse ? PrepareEncoder.getScFwdDir() : PrepareEncoder.getScBwdDir(); if (edgeBased) { - chBuilder.addShortcutEdgeBased(baseNode, adjNode, flags, weight, skip1, skip2, origFirst, origLast); + chBuilder.addShortcutEdgeBased(baseNode, adjNode, flags, weight, skip1, skip2, origKeyFirst, origKeyLast); } else { chBuilder.addShortcutNodeBased(baseNode, adjNode, flags, weight, skip1, skip2); } @@ -108,7 +108,7 @@ public Stream provideArguments(ExtensionContext context) { @ArgumentsSource(FixtureProvider.class) public void testUnpacking(Fixture f) { // 0-1-2-3-4-5-6 - GHUtility.setSpeed(60, 30, f.encoder, + GHUtility.setSpeed(60, 30, f.accessEnc, f.speedEnc, f.graph.edge(0, 1).setDistance(1), f.graph.edge(1, 2).setDistance(1), f.graph.edge(2, 3).setDistance(1), @@ -119,11 +119,11 @@ public void testUnpacking(Fixture f) { f.freeze(); f.setCHLevels(1, 3, 5, 4, 2, 0, 6); - f.shortcut(4, 2, 2, 3, 2, 3, true); - f.shortcut(4, 6, 4, 5, 4, 5, false); - f.shortcut(2, 0, 0, 1, 0, 1, true); - f.shortcut(2, 6, 6, 7, 2, 5, false); - f.shortcut(0, 6, 8, 9, 0, 5, false); + f.shortcut(4, 2, 2, 3, 4, 6, true); + f.shortcut(4, 6, 4, 5, 8, 10, false); + f.shortcut(2, 0, 0, 1, 0, 2, true); + f.shortcut(2, 6, 6, 7, 4, 10, false); + f.shortcut(0, 6, 8, 9, 0, 10, false); { // unpack the shortcut 0->6, traverse original edges in 'forward' order (from node 0 to 6) @@ -197,7 +197,7 @@ public void loopShortcut(Fixture f) { // 2 4 // \ / // 0 - 1 - 5 - GHUtility.setSpeed(60, 30, f.encoder, + GHUtility.setSpeed(60, 30, f.accessEnc, f.speedEnc, f.graph.edge(0, 1).setDistance(1), f.graph.edge(1, 2).setDistance(1), f.graph.edge(2, 3).setDistance(1), @@ -207,11 +207,11 @@ public void loopShortcut(Fixture f) { f.freeze(); f.setCHLevels(2, 4, 3, 1, 5, 0); - f.shortcut(3, 1, 1, 2, 1, 2, true); - f.shortcut(3, 1, 3, 4, 3, 4, false); - f.shortcut(1, 1, 6, 7, 1, 4, false); - f.shortcut(1, 0, 0, 8, 0, 4, true); - f.shortcut(5, 0, 9, 5, 0, 5, true); + f.shortcut(3, 1, 1, 2, 2, 4, true); + f.shortcut(3, 1, 3, 4, 6, 8, false); + f.shortcut(1, 1, 6, 7, 2, 8, false); + f.shortcut(1, 0, 0, 8, 0, 8, true); + f.shortcut(5, 0, 9, 5, 0, 10, true); { // unpack the shortcut 0->5, traverse original edges in 'forward' order (from node 0 to 5) @@ -274,7 +274,7 @@ public void withTurnWeighting(Fixture f) { // prev 0-1-2-3-4-5-6 next // 1 0 1 4 2 3 2 turn costs <- EdgeIteratorState edge0, edge1, edge2, edge3, edge4, edge5; - GHUtility.setSpeed(60, 30, f.encoder, + GHUtility.setSpeed(60, 30, f.accessEnc, f.speedEnc, edge0 = f.graph.edge(0, 1).setDistance(1), edge1 = f.graph.edge(1, 2).setDistance(1), edge2 = f.graph.edge(2, 3).setDistance(1), @@ -301,11 +301,11 @@ public void withTurnWeighting(Fixture f) { f.setTurnCost(edge0.getEdge(), 0, PREV_EDGE, 1.0); f.setCHLevels(1, 3, 5, 4, 2, 0, 6); - f.shortcut(4, 2, 2, 3, 2, 3, true); - f.shortcut(4, 6, 4, 5, 4, 5, false); - f.shortcut(2, 0, 0, 1, 0, 1, true); - f.shortcut(2, 6, 6, 7, 2, 5, false); - f.shortcut(0, 6, 8, 9, 0, 5, false); + f.shortcut(4, 2, 2, 3, 4, 6, true); + f.shortcut(4, 6, 4, 5, 8, 10, false); + f.shortcut(2, 0, 0, 1, 0, 2, true); + f.shortcut(2, 6, 6, 7, 4, 10, false); + f.shortcut(0, 6, 8, 9, 0, 10, false); { // unpack the shortcut 0->6, traverse original edges in 'forward' order (from node 0 to 6) diff --git a/core/src/test/java/com/graphhopper/storage/TurnCostStorageTest.java b/core/src/test/java/com/graphhopper/storage/TurnCostStorageTest.java index 588705e4de9..12433258c43 100644 --- a/core/src/test/java/com/graphhopper/storage/TurnCostStorageTest.java +++ b/core/src/test/java/com/graphhopper/storage/TurnCostStorageTest.java @@ -18,13 +18,9 @@ package com.graphhopper.storage; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.util.GHUtility; -import com.graphhopper.util.PMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,12 +36,20 @@ public class TurnCostStorageTest { private EncodingManager manager; + private DecimalEncodedValue carTurnCostEnc; + private DecimalEncodedValue bikeTurnCostEnc; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; @BeforeEach public void setup() { - FlagEncoder carEncoder = FlagEncoders.createCar(new PMap().putObject("max_turn_costs", 3)); - FlagEncoder bikeEncoder = FlagEncoders.createBike(new PMap().putObject("max_turn_costs", 3)); - manager = EncodingManager.create(carEncoder, bikeEncoder); + accessEnc = new SimpleBooleanEncodedValue("car_access", true); + speedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + carTurnCostEnc = TurnCost.create("car", 3); + bikeTurnCostEnc = TurnCost.create("bike", 3); + manager = EncodingManager.start() + .add(accessEnc).add(speedEnc) + .addTurnCostEncodedValue(carTurnCostEnc).addTurnCostEncodedValue(bikeTurnCostEnc).build(); } // 0---1 @@ -53,8 +57,8 @@ public void setup() { // 2--3 // | // 4 - public static void initGraph(BaseGraph g, FlagEncoder encoder) { - GHUtility.setSpeed(60, 60, encoder, + public static void initGraph(BaseGraph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, g.edge(0, 1).setDistance(3), g.edge(0, 2).setDistance(1), g.edge(1, 3).setDistance(1), @@ -67,12 +71,12 @@ public static void initGraph(BaseGraph g, FlagEncoder encoder) { */ @Test public void testMultipleTurnCosts() { - BaseGraph g = new BaseGraph.Builder(manager).create(); - initGraph(g, manager.getEncoder("car")); + BaseGraph g = new BaseGraph.Builder(manager).withTurnCosts(true).create(); + initGraph(g, accessEnc, speedEnc); TurnCostStorage turnCostStorage = g.getTurnCostStorage(); - DecimalEncodedValue carEnc = manager.getDecimalEncodedValue(TurnCost.key("car")); - DecimalEncodedValue bikeEnc = manager.getDecimalEncodedValue(TurnCost.key("bike")); + DecimalEncodedValue carEnc = carTurnCostEnc; + DecimalEncodedValue bikeEnc = bikeTurnCostEnc; int edge42 = getEdge(g, 4, 2).getEdge(); int edge23 = getEdge(g, 2, 3).getEdge(); int edge31 = getEdge(g, 3, 1).getEdge(); @@ -105,31 +109,31 @@ public void testMultipleTurnCosts() { assertEquals(Double.POSITIVE_INFINITY, turnCostStorage.get(carEnc, edge02, 2, edge23), 0); assertEquals(Double.POSITIVE_INFINITY, turnCostStorage.get(bikeEnc, edge02, 2, edge23), 0); - Set> allTurnRelations = new HashSet<>(); - TurnCostStorage.TurnRelationIterator iterator = turnCostStorage.getAllTurnRelations(); + Set> turnCosts = new HashSet<>(); + TurnCostStorage.Iterator iterator = turnCostStorage.getAllTurnCosts(); while (iterator.next()) { - allTurnRelations.add(Arrays.asList(iterator.getFromEdge(), iterator.getViaNode(), iterator.getToEdge(), + turnCosts.add(Arrays.asList(iterator.getFromEdge(), iterator.getViaNode(), iterator.getToEdge(), (int) iterator.getCost(carEnc), (int) iterator.getCost(bikeEnc))); } - Set> expectedTurnRelations = new HashSet<>(); - expectedTurnRelations.add(Arrays.asList(edge31, 1, edge10, 2, Integer.MAX_VALUE)); - expectedTurnRelations.add(Arrays.asList(edge42, 2, edge23, Integer.MAX_VALUE, Integer.MAX_VALUE)); - expectedTurnRelations.add(Arrays.asList(edge02, 2, edge24, 0, Integer.MAX_VALUE)); - expectedTurnRelations.add(Arrays.asList(edge02, 2, edge23, Integer.MAX_VALUE, Integer.MAX_VALUE)); - expectedTurnRelations.add(Arrays.asList(edge23, 3, edge31, Integer.MAX_VALUE, 2)); + Set> expectedTurnCosts = new HashSet<>(); + expectedTurnCosts.add(Arrays.asList(edge31, 1, edge10, 2, Integer.MAX_VALUE)); + expectedTurnCosts.add(Arrays.asList(edge42, 2, edge23, Integer.MAX_VALUE, Integer.MAX_VALUE)); + expectedTurnCosts.add(Arrays.asList(edge02, 2, edge24, 0, Integer.MAX_VALUE)); + expectedTurnCosts.add(Arrays.asList(edge02, 2, edge23, Integer.MAX_VALUE, Integer.MAX_VALUE)); + expectedTurnCosts.add(Arrays.asList(edge23, 3, edge31, Integer.MAX_VALUE, 2)); - assertEquals(expectedTurnRelations, allTurnRelations); + assertEquals(expectedTurnCosts, turnCosts); } @Test public void testMergeFlagsBeforeAdding() { - BaseGraph g = new BaseGraph.Builder(manager).create(); - initGraph(g, manager.getEncoder("car")); + BaseGraph g = new BaseGraph.Builder(manager).withTurnCosts(true).create(); + initGraph(g, accessEnc, speedEnc); TurnCostStorage turnCostStorage = g.getTurnCostStorage(); - DecimalEncodedValue carEnc = manager.getDecimalEncodedValue(TurnCost.key("car")); - DecimalEncodedValue bikeEnc = manager.getDecimalEncodedValue(TurnCost.key("bike")); + DecimalEncodedValue carEnc = carTurnCostEnc; + DecimalEncodedValue bikeEnc = bikeTurnCostEnc; int edge23 = getEdge(g, 2, 3).getEdge(); int edge02 = getEdge(g, 0, 2).getEdge(); @@ -138,26 +142,26 @@ public void testMergeFlagsBeforeAdding() { assertEquals(Double.POSITIVE_INFINITY, turnCostStorage.get(carEnc, edge02, 2, edge23), 0); assertEquals(Double.POSITIVE_INFINITY, turnCostStorage.get(bikeEnc, edge02, 2, edge23), 0); - Set> allTurnRelations = new HashSet<>(); - TurnCostStorage.TurnRelationIterator iterator = turnCostStorage.getAllTurnRelations(); + Set> turnCosts = new HashSet<>(); + TurnCostStorage.Iterator iterator = turnCostStorage.getAllTurnCosts(); while (iterator.next()) { - allTurnRelations.add(Arrays.asList(iterator.getFromEdge(), iterator.getViaNode(), iterator.getToEdge(), + turnCosts.add(Arrays.asList(iterator.getFromEdge(), iterator.getViaNode(), iterator.getToEdge(), (int) iterator.getCost(carEnc), (int) iterator.getCost(bikeEnc))); } - Set> expectedTurnRelations = new HashSet<>(); - expectedTurnRelations.add(Arrays.asList(edge02, 2, edge23, Integer.MAX_VALUE, Integer.MAX_VALUE)); + Set> expectedTurnCosts = new HashSet<>(); + expectedTurnCosts.add(Arrays.asList(edge02, 2, edge23, Integer.MAX_VALUE, Integer.MAX_VALUE)); - assertEquals(expectedTurnRelations, allTurnRelations); + assertEquals(expectedTurnCosts, turnCosts); } @Test public void testIterateEmptyStore() { - BaseGraph g = new BaseGraph.Builder(manager).create(); - initGraph(g, manager.getEncoder("car")); + BaseGraph g = new BaseGraph.Builder(manager).withTurnCosts(true).create(); + initGraph(g, accessEnc, speedEnc); TurnCostStorage turnCostStorage = g.getTurnCostStorage(); - TurnCostStorage.TurnRelationIterator iterator = turnCostStorage.getAllTurnRelations(); + TurnCostStorage.Iterator iterator = turnCostStorage.getAllTurnCosts(); assertFalse(iterator.next()); } diff --git a/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeTest.java b/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeTest.java index ab318cd48a8..40c891bef85 100644 --- a/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeTest.java +++ b/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeTest.java @@ -18,8 +18,11 @@ package com.graphhopper.storage.index; import com.carrotsearch.hppc.IntArrayList; -import com.graphhopper.routing.ev.BooleanEncodedValue; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.AccessFilter; +import com.graphhopper.routing.util.AllEdgesIterator; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.storage.*; import com.graphhopper.util.*; import com.graphhopper.util.shapes.BBox; @@ -37,9 +40,11 @@ * @author Peter Karich */ public class LocationIndexTreeTest { - protected final EncodingManager encodingManager = EncodingManager.create("car"); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + protected final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); - public static void initSimpleGraph(Graph g, EncodingManager em) { + public static void initSimpleGraph(Graph g) { // 6 | 4 // 5 | // | 6 @@ -69,10 +74,6 @@ public static void initSimpleGraph(Graph g, EncodingManager em) { g.edge(3, 5), // make sure 6 is connected g.edge(6, 4)); - for (FlagEncoder encoder : em.fetchEdgeEncoders()) { - double speed = encoder.getMaxSpeed() / 2; - GHUtility.setSpeed(speed, speed, encoder, list); - } } private LocationIndexTree createIndexNoPrepare(Graph g, int resolution) { @@ -87,8 +88,7 @@ private LocationIndexTree createIndexNoPrepare(Graph g, int resolution) { // |1----3-\| // |____/ 4 // 2-------/ - Graph createTestGraph(EncodingManager em) { - FlagEncoder encoder = em.getEncoder("car"); + Graph createTestGraph(EncodingManager em, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { BaseGraph graph = new BaseGraph.Builder(em).create(); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 0.5, -0.5); @@ -96,19 +96,19 @@ Graph createTestGraph(EncodingManager em) { na.setNode(2, -1, -1); na.setNode(3, -0.4, 0.9); na.setNode(4, -0.6, 1.6); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 4)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4)); return graph; } @Test public void testSnappedPointAndGeometry() { - Graph graph = createTestGraph(encodingManager); + Graph graph = createTestGraph(encodingManager, accessEnc, speedEnc); LocationIndex index = createIndexNoPrepare(graph, 500000).prepareIndex(); // query directly the tower node Snap res = index.findClosest(-0.4, 0.9, EdgeFilter.ALL_EDGES); @@ -148,17 +148,16 @@ public void testBoundingBoxQuery1() { @Test public void testMoreReal() { - FlagEncoder encoder = FlagEncoders.createCar(); - BaseGraph graph = new BaseGraph.Builder(EncodingManager.create(encoder)).create(); + BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); NodeAccess na = graph.getNodeAccess(); na.setNode(1, 51.2492152, 9.4317166); na.setNode(0, 52, 9); na.setNode(2, 51.2, 9.4); na.setNode(3, 49, 10); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 0)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 3)).setWayGeometry(Helper.createPointList(51.21, 9.43)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 0)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 3)).setWayGeometry(Helper.createPointList(51.21, 9.43)); LocationIndex index = createIndexNoPrepare(graph, 500000).prepareIndex(); assertEquals(1, findClosestEdge(index, 51.2, 9.4)); } @@ -174,21 +173,20 @@ public void testMoreReal() { // | private Graph createTestGraphWithWayGeometry() { BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); - FlagEncoder encoder = encodingManager.getEncoder("car"); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 0.5, -0.5); na.setNode(1, -0.5, -0.5); na.setNode(2, -1, -1); na.setNode(3, -0.4, 0.9); na.setNode(4, -0.6, 1.6); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 2)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 2)); // insert A and B, without this we would get 0 for 0,0 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(0, 4)).setWayGeometry(Helper.createPointList(1, 1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 3)).setWayGeometry(Helper.createPointList(0, 0)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(2, 4)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(3, 4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(0, 4)).setWayGeometry(Helper.createPointList(1, 1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 3)).setWayGeometry(Helper.createPointList(0, 0)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(2, 4)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(3, 4)); return graph; } @@ -205,15 +203,14 @@ public void testWayGeometry() { @Test public void testFindingWayGeometry() { BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - FlagEncoder encoder = encodingManager.getEncoder("car"); NodeAccess na = g.getNodeAccess(); na.setNode(10, 51.2492152, 9.4317166); na.setNode(20, 52, 9); na.setNode(30, 51.2, 9.4); na.setNode(50, 49, 10); - GHUtility.setSpeed(60, true, true, encoder, g.edge(20, 50)).setWayGeometry(Helper.createPointList(51.25, 9.43)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(10, 20)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(20, 30)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(20, 50)).setWayGeometry(Helper.createPointList(51.25, 9.43)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(10, 20)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(20, 30)); LocationIndex index = createIndexNoPrepare(g, 2000).prepareIndex(); assertEquals(0, findClosestEdge(index, 51.25, 9.43)); @@ -221,7 +218,7 @@ public void testFindingWayGeometry() { @Test public void testEdgeFilter() { - Graph graph = createTestGraph(encodingManager); + Graph graph = createTestGraph(encodingManager, accessEnc, speedEnc); LocationIndexTree index = (LocationIndexTree) createIndexNoPrepare(graph, 500000).prepareIndex(); assertEquals(1, index.findClosest(-.6, -.6, EdgeFilter.ALL_EDGES).getClosestNode()); @@ -230,7 +227,6 @@ public void testEdgeFilter() { // see testgraph2.jpg Graph createTestGraph2() { - FlagEncoder encoder = encodingManager.getEncoder("car"); BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); NodeAccess na = graph.getNodeAccess(); @@ -282,7 +278,7 @@ Graph createTestGraph2() { // top right na.setNode(101, 49.96053, 11.58814); - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 1), graph.edge(1, 2), graph.edge(2, 3), @@ -326,7 +322,7 @@ Graph createTestGraph2() { @Test public void testRMin() { - Graph graph = createTestGraph(encodingManager); + Graph graph = createTestGraph(encodingManager, accessEnc, speedEnc); LocationIndexTree index = (LocationIndexTree) createIndexNoPrepare(graph, 50000).prepareIndex(); DistanceCalc distCalc = new DistancePlaneProjection(); double rmin2 = index.calculateRMin(0.05, -0.3, 1); @@ -336,10 +332,12 @@ public void testRMin() { @Test public void testSearchWithFilter_issue318() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - FlagEncoder bikeEncoder = FlagEncoders.createBike(); + BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + BooleanEncodedValue bikeAccessEnc = new SimpleBooleanEncodedValue("bike_access", true); + DecimalEncodedValue bikeSpeedEnc = new DecimalEncodedValueImpl("bike_speed", 4, 2, false); - EncodingManager tmpEM = EncodingManager.create(carEncoder, bikeEncoder); + EncodingManager tmpEM = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).add(bikeAccessEnc).add(bikeSpeedEnc).build(); BaseGraph graph = new BaseGraph.Builder(tmpEM).create(); NodeAccess na = graph.getNodeAccess(); @@ -350,33 +348,32 @@ public void testSearchWithFilter_issue318() { int index = lonIdx * 10 + latIdx; na.setNode(index, 0.01 * latIdx, 0.01 * lonIdx); if (latIdx < MAX - 1) - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(index, index + 1)); + GHUtility.setSpeed(60, true, true, carAccessEnc, carSpeedEnc, graph.edge(index, index + 1)); if (lonIdx < MAX - 1) - GHUtility.setSpeed(60, true, true, carEncoder, graph.edge(index, index + 10)); + GHUtility.setSpeed(60, true, true, carAccessEnc, carSpeedEnc, graph.edge(index, index + 10)); } } // reduce access for bike to two edges only AllEdgesIterator iter = graph.getAllEdges(); - BooleanEncodedValue accessEnc = bikeEncoder.getAccessEnc(); while (iter.next()) { - iter.set(accessEnc, false, false); + iter.set(bikeAccessEnc, false, false); } for (EdgeIteratorState edge : Arrays.asList(GHUtility.getEdge(graph, 0, 1), GHUtility.getEdge(graph, 1, 2))) { - edge.set(accessEnc, true, true); + edge.set(bikeAccessEnc, true, true); } LocationIndexTree index = createIndexNoPrepare(graph, 500); index.prepareIndex(); index.setMaxRegionSearch(8); - EdgeFilter carFilter = AccessFilter.allEdges(carEncoder.getAccessEnc()); + EdgeFilter carFilter = AccessFilter.allEdges(carAccessEnc); Snap snap = index.findClosest(0.03, 0.03, carFilter); assertTrue(snap.isValid()); assertEquals(33, snap.getClosestNode()); - EdgeFilter bikeFilter = AccessFilter.allEdges(bikeEncoder.getAccessEnc()); + EdgeFilter bikeFilter = AccessFilter.allEdges(bikeAccessEnc); snap = index.findClosest(0.03, 0.03, bikeFilter); assertTrue(snap.isValid()); assertEquals(2, snap.getClosestNode()); @@ -387,7 +384,6 @@ public void testSearchWithFilter_issue318() { // 4--5--6--7 @Test public void testCrossBoundaryNetwork_issue667() { - FlagEncoder encoder = encodingManager.getEncoder("car"); BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); NodeAccess na = graph.getNodeAccess(); na.setNode(0, 0.1, 179.5); @@ -400,7 +396,7 @@ public void testCrossBoundaryNetwork_issue667() { na.setNode(7, 0, -179.5); // just use 1 as distance which is incorrect but does not matter in this unit case - GHUtility.setSpeed(60, 60, encoder, + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, graph.edge(0, 1), graph.edge(0, 4), graph.edge(1, 5), @@ -414,9 +410,9 @@ public void testCrossBoundaryNetwork_issue667() { // as last edges: create cross boundary edges // See #667 where the recommendation is to adjust the import and introduce two pillar nodes // where the connection is cross boundary and would be okay if ignored as real length is 0 - GHUtility.setSpeed(60, true, true, encoder, graph.edge(1, 2)).setWayGeometry(Helper.createPointList(0, 180, 0, -180)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(1, 2)).setWayGeometry(Helper.createPointList(0, 180, 0, -180)); // but this unit test succeeds even without this adjusted import: - GHUtility.setSpeed(60, true, true, encoder, graph.edge(5, 6)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(5, 6)); LocationIndexTree index = createIndexNoPrepare(graph, 500); index.prepareIndex(); @@ -440,10 +436,14 @@ private int findClosestEdge(LocationIndex index, double lat, double lon) { @Test public void testSimpleGraph() { - EncodingManager em = EncodingManager.create("car"); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph g = new BaseGraph.Builder(em).create(); - initSimpleGraph(g, em); - + initSimpleGraph(g); + AllEdgesIterator edge = g.getAllEdges(); + while (edge.next()) + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, edge); LocationIndexTree idx = (LocationIndexTree) createIndexNoPrepare(g, 500000).prepareIndex(); assertEquals(3, findClosestEdge(idx, 5, 2)); assertEquals(3, findClosestEdge(idx, 1.5, 2)); @@ -454,9 +454,14 @@ public void testSimpleGraph() { @Test public void testSimpleGraph2() { - EncodingManager em = EncodingManager.create("car"); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager em = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph g = new BaseGraph.Builder(em).create(); - initSimpleGraph(g, em); + initSimpleGraph(g); + AllEdgesIterator edge = g.getAllEdges(); + while (edge.next()) + GHUtility.setSpeed(60, 60, accessEnc, speedEnc, edge); LocationIndexTree idx = (LocationIndexTree) createIndexNoPrepare(g, 500000).prepareIndex(); assertEquals(3, findClosestEdge(idx, 5, 2)); @@ -472,7 +477,7 @@ public void testSimpleGraph2() { @Test public void testSinglePoints120() { - BaseGraph g = createSampleGraph(EncodingManager.create("car")); + BaseGraph g = createSampleGraph(encodingManager, accessEnc, speedEnc); LocationIndexTree idx = (LocationIndexTree) createIndexNoPrepare(g, 500000).prepareIndex(); assertEquals(3, findClosestEdge(idx, 1.637, 2.23)); @@ -487,7 +492,7 @@ public void testSinglePoints120() { @Test public void testSinglePoints32() { - BaseGraph g = createSampleGraph(EncodingManager.create("car")); + BaseGraph g = createSampleGraph(encodingManager, accessEnc, speedEnc); LocationIndexTree idx = (LocationIndexTree) createIndexNoPrepare(g, 500000).prepareIndex(); assertEquals(10, findClosestEdge(idx, 3.649, 1.375)); @@ -499,7 +504,7 @@ public void testSinglePoints32() { @Test public void testNoErrorOnEdgeCase_lastIndex() { - final EncodingManager encodingManager = EncodingManager.create("car"); + final EncodingManager encodingManager = new EncodingManager.Builder().build(); int locs = 10000; BaseGraph g = new BaseGraph.Builder(encodingManager).create(); NodeAccess na = g.getNodeAccess(); @@ -511,7 +516,7 @@ public void testNoErrorOnEdgeCase_lastIndex() { g.close(); } - public BaseGraph createSampleGraph(EncodingManager encodingManager) { + public BaseGraph createSampleGraph(EncodingManager encodingManager, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { BaseGraph graph = new BaseGraph.Builder(encodingManager).create(); // length does not matter here but lat,lon and outgoing edges do! @@ -567,49 +572,53 @@ public BaseGraph createSampleGraph(EncodingManager encodingManager) { na.setNode(16, 5, 5); // => 17 locations - FlagEncoder encoder = encodingManager.getEncoder("car"); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(a0, b1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(c2, b1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(c2, d3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(f5, b1)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(e4, f5)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(m12, d3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(e4, k10)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(f5, d3)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(f5, i8)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(f5, j9)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(k10, g6)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(j9, l11)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(i8, l11)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(i8, h7)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(k10, n13)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(k10, o14)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(l11, p15)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(m12, p15)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(q16, p15)); - GHUtility.setSpeed(60, true, true, encoder, graph.edge(q16, m12)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(a0, b1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(c2, b1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(c2, d3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(f5, b1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(e4, f5)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(m12, d3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(e4, k10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(f5, d3)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(f5, i8)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(f5, j9)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(k10, g6)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(j9, l11)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(i8, l11)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(i8, h7)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(k10, n13)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(k10, o14)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(l11, p15)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(m12, p15)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(q16, p15)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, graph.edge(q16, m12)); return graph; } @Test public void testDifferentVehicles() { - final EncodingManager encodingManager = EncodingManager.create("car,foot"); - BaseGraph g = new BaseGraph.Builder(encodingManager).create(); - initSimpleGraph(g, encodingManager); + BooleanEncodedValue carAccessEnc = new SimpleBooleanEncodedValue("car_access", true); + DecimalEncodedValue carSpeedEnc = new DecimalEncodedValueImpl("car_speed", 5, 5, false); + BooleanEncodedValue footAccessEnc = new SimpleBooleanEncodedValue("foot_access", true); + DecimalEncodedValue footSpeedEnc = new DecimalEncodedValueImpl("foot_speed", 4, 1, false); + EncodingManager em = EncodingManager.start().add(carAccessEnc).add(carSpeedEnc).add(footAccessEnc).add(footSpeedEnc).build(); + BaseGraph g = new BaseGraph.Builder(em).create(); + initSimpleGraph(g); + AllEdgesIterator edge = g.getAllEdges(); + while (edge.next()) { + GHUtility.setSpeed(60, 60, carAccessEnc, carSpeedEnc, edge); + GHUtility.setSpeed(10, 10, footAccessEnc, footSpeedEnc, edge); + } LocationIndexTree idx = (LocationIndexTree) createIndexNoPrepare(g, 500000).prepareIndex(); assertEquals(0, findClosestEdge(idx, 1, -1)); // now make all edges from node 1 accessible for CAR only EdgeIterator iter = g.createEdgeExplorer().setBaseNode(1); - FlagEncoder encoder = encodingManager.getEncoder("foot"); - BooleanEncodedValue accessEnc = encoder.getAccessEnc(); - while (iter.next()) { - iter.set(accessEnc, false, false); - } + while (iter.next()) + iter.set(footAccessEnc, false, false); idx = (LocationIndexTree) createIndexNoPrepare(g, 500000).prepareIndex(); - FlagEncoder footEncoder = encodingManager.getEncoder("foot"); - assertEquals(2, idx.findClosest(1, -1, AccessFilter.allEdges(footEncoder.getAccessEnc())).getClosestNode()); + assertEquals(2, idx.findClosest(1, -1, AccessFilter.allEdges(footAccessEnc)).getClosestNode()); g.close(); } diff --git a/core/src/test/java/com/graphhopper/util/GHUtilityTest.java b/core/src/test/java/com/graphhopper/util/GHUtilityTest.java index 362bdfa39d1..bc60fd2b423 100644 --- a/core/src/test/java/com/graphhopper/util/GHUtilityTest.java +++ b/core/src/test/java/com/graphhopper/util/GHUtilityTest.java @@ -18,10 +18,12 @@ package com.graphhopper.util; import com.graphhopper.coll.GHIntLongHashMap; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.Graph; import com.graphhopper.storage.NodeAccess; @@ -33,8 +35,9 @@ * @author Peter Karich */ public class GHUtilityTest { - private final FlagEncoder carEncoder = FlagEncoders.createCar(); - private final EncodingManager encodingManager = EncodingManager.create(carEncoder); + private final BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + private final DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + private final EncodingManager encodingManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph createGraph() { return new BaseGraph.Builder(encodingManager).create(); @@ -47,7 +50,7 @@ BaseGraph createGraph() { // 6 \1 // ______/ // 0/ - Graph initUnsorted(Graph g, FlagEncoder encoder) { + Graph initUnsorted(Graph g, BooleanEncodedValue accessEnc, DecimalEncodedValue speedEnc) { NodeAccess na = g.getNodeAccess(); na.setNode(0, 0, 1); na.setNode(1, 2.5, 4.5); @@ -58,12 +61,12 @@ Graph initUnsorted(Graph g, FlagEncoder encoder) { na.setNode(6, 2.3, 2.2); na.setNode(7, 5, 1.5); na.setNode(8, 4.6, 4); - GHUtility.setSpeed(60, true, true, encoder, g.edge(8, 2).setDistance(0.5)); - GHUtility.setSpeed(60, true, false, encoder, g.edge(7, 3).setDistance(2.1)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 0).setDistance(3.9)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(7, 5).setDistance(0.7)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(1, 2).setDistance(1.9)); - GHUtility.setSpeed(60, true, true, encoder, g.edge(8, 1).setDistance(2.05)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 2).setDistance(0.5)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(7, 3).setDistance(2.1)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 0).setDistance(3.9)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 5).setDistance(0.7)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(1.9)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 1).setDistance(2.05)); return g; } @@ -82,7 +85,7 @@ Graph initUnsorted(Graph g, FlagEncoder encoder) { @Test public void testSort() { - Graph g = initUnsorted(createGraph(), carEncoder); + Graph g = initUnsorted(createGraph(), accessEnc, speedEnc); Graph newG = GHUtility.sortDFS(g, createGraph()); assertEquals(g.getNodes(), newG.getNodes()); assertEquals(g.getEdges(), newG.getEdges()); @@ -116,27 +119,17 @@ public void testSortDirected() { na.setNode(0, 0, 1); na.setNode(1, 2.5, 2); na.setNode(2, 3.5, 3); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(0, 1).setDistance(1.1)); - GHUtility.setSpeed(60, true, false, carEncoder, g.edge(2, 1).setDistance(1.1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(0, 1).setDistance(1.1)); + GHUtility.setSpeed(60, true, false, accessEnc, speedEnc, g.edge(2, 1).setDistance(1.1)); GHUtility.sortDFS(g, createGraph()); } @Test public void testEdgeStuff() { - assertEquals(6, GHUtility.createEdgeKey(1, 2, 3, false)); - assertEquals(7, GHUtility.createEdgeKey(2, 1, 3, false)); - assertEquals(7, GHUtility.createEdgeKey(1, 2, 3, true)); - assertEquals(6, GHUtility.createEdgeKey(2, 1, 3, true)); - - assertEquals(8, GHUtility.createEdgeKey(1, 2, 4, false)); - assertEquals(9, GHUtility.createEdgeKey(2, 1, 4, false)); - - assertEquals(6, GHUtility.createEdgeKey(1, 1, 3, false)); - assertEquals(6, GHUtility.createEdgeKey(1, 1, 3, true)); - - assertTrue(GHUtility.isSameEdgeKeys(GHUtility.createEdgeKey(1, 2, 4, false), GHUtility.createEdgeKey(1, 2, 4, false))); - assertTrue(GHUtility.isSameEdgeKeys(GHUtility.createEdgeKey(2, 1, 4, false), GHUtility.createEdgeKey(1, 2, 4, false))); - assertFalse(GHUtility.isSameEdgeKeys(GHUtility.createEdgeKey(1, 2, 4, false), GHUtility.createEdgeKey(1, 2, 5, false))); + assertEquals(2, GHUtility.createEdgeKey(1, false, false)); + assertEquals(2, GHUtility.createEdgeKey(1, true, false)); + assertEquals(2, GHUtility.createEdgeKey(1, true, true)); + assertEquals(3, GHUtility.createEdgeKey(1, false, true)); } @Test diff --git a/core/src/test/java/com/graphhopper/util/InstructionListTest.java b/core/src/test/java/com/graphhopper/util/InstructionListTest.java index be889df036d..f7ace539773 100644 --- a/core/src/test/java/com/graphhopper/util/InstructionListTest.java +++ b/core/src/test/java/com/graphhopper/util/InstructionListTest.java @@ -22,12 +22,9 @@ import com.graphhopper.routing.Dijkstra; import com.graphhopper.routing.InstructionsFromEdges; import com.graphhopper.routing.Path; -import com.graphhopper.routing.ev.DecimalEncodedValue; -import com.graphhopper.routing.ev.EnumEncodedValue; -import com.graphhopper.routing.ev.RoadClass; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; +import com.graphhopper.routing.util.PriorityCode; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.DefaultTurnCostProvider; import com.graphhopper.routing.weighting.FastestWeighting; @@ -45,6 +42,8 @@ import java.util.List; import java.util.Locale; +import static com.graphhopper.search.KVStorage.KeyValue.STREET_NAME; +import static com.graphhopper.search.KVStorage.KeyValue.createKV; import static org.junit.jupiter.api.Assertions.*; /** @@ -55,12 +54,14 @@ public class InstructionListTest { private static final Translation usTR = trMap.getWithFallBack(Locale.US); private final TraversalMode tMode = TraversalMode.NODE_BASED; private EncodingManager carManager; - private FlagEncoder carEncoder; + private BooleanEncodedValue accessEnc; + private DecimalEncodedValue speedEnc; @BeforeEach public void setUp() { - carEncoder = FlagEncoders.createCar(); - carManager = EncodingManager.create(carEncoder); + accessEnc = new SimpleBooleanEncodedValue("access", true); + speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + carManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); } private static List getTurnDescriptions(InstructionList instructionList) { @@ -95,30 +96,30 @@ Graph createTestGraph() { na.setNode(6, 1.0, 1.0); na.setNode(7, 1.0, 1.1); na.setNode(8, 1.0, 1.2); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 1).setDistance(10000)).setName("0-1"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 2).setDistance(11000)).setName("1-2"); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "0-1")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "1-2")); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(0, 3).setDistance(11000)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 4).setDistance(10000)).setName("1-4"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 5).setDistance(11000)).setName("5-2"); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(0, 3).setDistance(11000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 4).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "1-4")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 5).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "5-2")); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 6).setDistance(11000)).setName("3-6"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(4, 7).setDistance(10000)).setName("4-7"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(5, 8).setDistance(10000)).setName("5-8"); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 6).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "3-6")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 7).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "4-7")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(5, 8).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "5-8")); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(6, 7).setDistance(11000)).setName("6-7"); - EdgeIteratorState iter = GHUtility.setSpeed(60, true, true, carEncoder, g.edge(7, 8).setDistance(10000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(6, 7).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "6-7")); + EdgeIteratorState iter = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(7, 8).setDistance(10000)); PointList list = new PointList(); list.add(1.0, 1.15); list.add(1.0, 1.16); iter.setWayGeometry(list); - iter.setName("7-8"); + iter.setKeyValues(createKV(STREET_NAME, "7-8")); // missing edge name - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(9, 10).setDistance(10000)); - EdgeIteratorState iter2 = GHUtility.setSpeed(60, true, true, carEncoder, g.edge(8, 9).setDistance(20000)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(9, 10).setDistance(10000)); + EdgeIteratorState iter2 = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(8, 9).setDistance(20000)); list.clear(); list.add(1.0, 1.3); - iter2.setName("8-9"); + iter2.setKeyValues(createKV(STREET_NAME, "8-9")); iter2.setWayGeometry(list); return g; } @@ -127,7 +128,7 @@ Graph createTestGraph() { public void testWayList() { Graph g = createTestGraph(); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, TraversalMode.NODE_BASED).calcPath(0, 10); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); List tmpList = getTurnDescriptions(wayList); @@ -187,16 +188,16 @@ public void testWayList2() { na.setNode(3, 10.0, 10.08); na.setNode(4, 10.1, 10.10); na.setNode(5, 10.2, 10.13); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 4).setDistance(100)).setName("3-4"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(4, 5).setDistance(100)).setName("4-5"); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(100)).setKeyValues(createKV(STREET_NAME, "3-4")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(100)).setKeyValues(createKV(STREET_NAME, "4-5")); - EdgeIteratorState iter = GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 4).setDistance(100)); - iter.setName("2-4"); + EdgeIteratorState iter = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(100)); + iter.setKeyValues(createKV(STREET_NAME, "2-4")); PointList list = new PointList(); list.add(10.20, 10.05); iter.setWayGeometry(list); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(2, 3); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); @@ -226,16 +227,16 @@ public void testNoInstructionIfSameStreet() { na.setNode(3, 10.0, 10.05); na.setNode(4, 10.1, 10.10); na.setNode(5, 10.2, 10.15); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 4).setDistance(100)).setName("street"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(4, 5).setDistance(100)).setName("4-5"); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(100)).setKeyValues(createKV(STREET_NAME, "street")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(100)).setKeyValues(createKV(STREET_NAME, "4-5")); - EdgeIteratorState iter = GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 4).setDistance(100)); - iter.setName("street"); + EdgeIteratorState iter = GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(100)); + iter.setKeyValues(createKV(STREET_NAME, "street")); PointList list = new PointList(); list.add(10.20, 10.05); iter.setWayGeometry(list); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(2, 3); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); List tmpList = getTurnDescriptions(wayList); @@ -260,11 +261,11 @@ public void testNoInstructionIfSlightTurnAndAlternativeIsSharp() { na.setNode(2, 51.73458, 9.225442); na.setNode(3, 51.734643, 9.22541); na.setNode(4, 51.734451, 9.225436); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 2).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 4).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(10)); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(1, 3); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); List tmpList = getTurnDescriptions(wayList); @@ -289,11 +290,11 @@ public void testNoInstructionIfSlightTurnAndAlternativeIsSharp2() { na.setNode(2, 48.748577, 9.322152); na.setNode(3, 48.748776, 9.321889); na.setNode(4, 48.74847, 9.322299); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 2).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 3).setDistance(10)); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 4).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(10)); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(10)); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(1, 3); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); List tmpList = getTurnDescriptions(wayList); @@ -302,8 +303,9 @@ public void testNoInstructionIfSlightTurnAndAlternativeIsSharp2() { @Test public void testNoInstructionIfSlightTurnAndAlternativeIsSharp3() { - FlagEncoder bike = FlagEncoders.createBike(); - EncodingManager tmpEM = new EncodingManager.Builder().add(bike).build(); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, false); + EncodingManager tmpEM = new EncodingManager.Builder().add(accessEnc).add(speedEnc).build(); EnumEncodedValue rcEV = tmpEM.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BaseGraph g = new BaseGraph.Builder(tmpEM).create(); // real world example: https://graphhopper.com/maps/?point=48.411549,15.599567&point=48.411663%2C15.600527&profile=bike @@ -321,15 +323,15 @@ public void testNoInstructionIfSlightTurnAndAlternativeIsSharp3() { na.setNode(3, 48.411610, 15.600409); na.setNode(4, 48.411322, 15.600459); - GHUtility.setSpeed(18, true, true, bike, g.edge(1, 2).setDistance(20)); - GHUtility.setSpeed(18, true, true, bike, g.edge(2, 3).setDistance(20)); - GHUtility.setSpeed(4, true, true, bike, g.edge(2, 4).setDistance(20)); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(20)); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(20)); + GHUtility.setSpeed(4, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(20)); - g.edge(1, 2).set(rcEV, RoadClass.RESIDENTIAL).setName("pfarr"); - g.edge(2, 3).set(rcEV, RoadClass.RESIDENTIAL).setName("pfarr"); - g.edge(2, 4).set(rcEV, RoadClass.PEDESTRIAN).setName("markt"); + g.edge(1, 2).set(rcEV, RoadClass.RESIDENTIAL).setKeyValues(createKV(STREET_NAME, "pfarr")); + g.edge(2, 3).set(rcEV, RoadClass.RESIDENTIAL).setKeyValues(createKV(STREET_NAME, "pfarr")); + g.edge(2, 4).set(rcEV, RoadClass.PEDESTRIAN).setKeyValues(createKV(STREET_NAME, "markt")); - FastestWeighting weighting = new FastestWeighting(bike); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(1, 3); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); List tmpList = getTurnDescriptions(wayList); @@ -340,8 +342,9 @@ public void testNoInstructionIfSlightTurnAndAlternativeIsSharp3() { @Test public void testInstructionIfTurn() { - FlagEncoder bike = FlagEncoders.createBike(); - EncodingManager tmpEM = new EncodingManager.Builder().add(bike).build(); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 2, false); + EncodingManager tmpEM = new EncodingManager.Builder().add(accessEnc).add(speedEnc).build(); EnumEncodedValue rcEV = tmpEM.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BaseGraph g = new BaseGraph.Builder(tmpEM).create(); // real world example: https://graphhopper.com/maps/?point=48.412169%2C15.604888&point=48.412251%2C15.60543&profile=bike @@ -359,15 +362,14 @@ public void testInstructionIfTurn() { na.setNode(3, 48.412614, 15.604872); na.setNode(4, 48.412148, 15.605543); - GHUtility.setSpeed(18, true, true, bike, g.edge(1, 2).setDistance(20)); - GHUtility.setSpeed(18, true, true, bike, g.edge(2, 3).setDistance(20)); - GHUtility.setSpeed(18, true, true, bike, g.edge(2, 4).setDistance(20)); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(20)) + .set(rcEV, RoadClass.RESIDENTIAL); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(20)) + .set(rcEV, RoadClass.SECONDARY); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(20)) + .set(rcEV, RoadClass.SECONDARY); - g.edge(1, 2).set(rcEV, RoadClass.RESIDENTIAL).setName("land"); - g.edge(2, 3).set(rcEV, RoadClass.SECONDARY).setName("ring"); - g.edge(2, 4).set(rcEV, RoadClass.SECONDARY).setName("ring"); - - FastestWeighting weighting = new FastestWeighting(bike); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(1, 4); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); List tmpList = getTurnDescriptions(wayList); @@ -378,8 +380,10 @@ public void testInstructionIfTurn() { @Test public void testInstructionIfSlightTurnForCustomProfile() { - FlagEncoder foot = FlagEncoders.createFoot(); - EncodingManager tmpEM = new EncodingManager.Builder().add(foot).build(); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 4, 1, false); + DecimalEncodedValue priorityEnc = new DecimalEncodedValueImpl("priority", 4, PriorityCode.getFactor(1), false); + EncodingManager tmpEM = new EncodingManager.Builder().add(accessEnc).add(speedEnc).add(priorityEnc).build(); BaseGraph g = new BaseGraph.Builder(tmpEM).create(); // real world example: https://graphhopper.com/maps/?point=43.729379,7.417697&point=43.729798,7.417263&profile=foot // From 4 to 3 and 4 to 1 @@ -398,14 +402,19 @@ public void testInstructionIfSlightTurnForCustomProfile() { na.setNode(3, 43.729821, 7.41725); na.setNode(4, 43.729476, 7.417633); - DecimalEncodedValue priorityEnc = tmpEM.getDecimalEncodedValue(EncodingManager.getKey(foot.toString(), "priority")); // default is priority=0 so set it to 1 - GHUtility.setSpeed(5, true, true, foot, g.edge(1, 2).setDistance(20).setName("myroad").set(priorityEnc, 1)); - GHUtility.setSpeed(5, true, true, foot, g.edge(2, 3).setDistance(20).setName("myroad").set(priorityEnc, 1)); + // default is priority=0 so set it to 1 + GHUtility.setSpeed(5, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(20). + setKeyValues(createKV(STREET_NAME, "myroad")).set(priorityEnc, 1)); + GHUtility.setSpeed(5, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(20). + setKeyValues(createKV(STREET_NAME, "myroad")).set(priorityEnc, 1)); PointList pointList = new PointList(); pointList.add(43.729627, 7.41749); - GHUtility.setSpeed(5, true, true, foot, g.edge(2, 4).setDistance(20).setName("myroad").set(priorityEnc, 1).setWayGeometry(pointList)); + GHUtility.setSpeed(5, true, true, accessEnc, speedEnc, g.edge(2, 4).setDistance(20). + setKeyValues(createKV(STREET_NAME, "myroad")).set(priorityEnc, 1).setWayGeometry(pointList)); - Weighting weighting = CustomModelParser.createWeighting(foot, tmpEM, DefaultTurnCostProvider.NO_TURN_COST_PROVIDER, new CustomModel().setDistanceInfluence(0)); + Weighting weighting = CustomModelParser.createWeighting(accessEnc, speedEnc, + priorityEnc, tmpEM, DefaultTurnCostProvider.NO_TURN_COST_PROVIDER, + new CustomModel().setDistanceInfluence(0d)); Path p = new Dijkstra(g, weighting, tMode).calcPath(4, 3); assertTrue(p.isFound()); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); @@ -424,8 +433,9 @@ public void testInstructionIfSlightTurnForCustomProfile() { @Test public void testInstructionWithHighlyCustomProfileWithRoadsBase() { - FlagEncoder roads = FlagEncoders.createRoads(); - EncodingManager tmpEM = EncodingManager.create(roads); + BooleanEncodedValue roadsAccessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue roadsSpeedEnc = new DecimalEncodedValueImpl("speed", 7, 2, true); + EncodingManager tmpEM = EncodingManager.start().add(roadsAccessEnc).add(roadsSpeedEnc).build(); EnumEncodedValue rcEV = tmpEM.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); BaseGraph g = new BaseGraph.Builder(tmpEM).create(); // real world example: https://graphhopper.com/maps/?point=55.691214%2C12.57065&point=55.689957%2C12.570387 @@ -444,14 +454,14 @@ public void testInstructionWithHighlyCustomProfileWithRoadsBase() { na.setNode(4, 55.690849, 12.571004); na.setNode(5, 55.690864, 12.570886); - GHUtility.setSpeed(50, true, true, roads, g.edge(3, 2).setDistance(10)); - GHUtility.setSpeed(40, true, true, roads, g.edge(2, 4).setDistance(10)); - GHUtility.setSpeed(40, true, true, roads, g.edge(2, 1).setDistance(10)); - GHUtility.setSpeed(10, true, true, roads, g.edge(2, 5).setDistance(10).set(rcEV, RoadClass.PEDESTRIAN)); + GHUtility.setSpeed(50, true, true, roadsAccessEnc, roadsSpeedEnc, g.edge(3, 2).setDistance(10)); + GHUtility.setSpeed(40, true, true, roadsAccessEnc, roadsSpeedEnc, g.edge(2, 4).setDistance(10)); + GHUtility.setSpeed(40, true, true, roadsAccessEnc, roadsSpeedEnc, g.edge(2, 1).setDistance(10)); + GHUtility.setSpeed(10, true, true, roadsAccessEnc, roadsSpeedEnc, g.edge(2, 5).setDistance(10).set(rcEV, RoadClass.PEDESTRIAN)); CustomModel customModel = new CustomModel(); - customModel.addToPriority(Statement.If("road_class == PEDESTRIAN", Statement.Op.MULTIPLY, 0)); - Weighting weighting = CustomModelParser.createWeighting(roads, tmpEM, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); + customModel.addToPriority(Statement.If("road_class == PEDESTRIAN", Statement.Op.MULTIPLY, "0")); + Weighting weighting = CustomModelParser.createWeighting(roadsAccessEnc, roadsSpeedEnc, null, tmpEM, TurnCostProvider.NO_TURN_COST_PROVIDER, customModel); Path p = new Dijkstra(g, weighting, tMode).calcPath(3, 4); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, tmpEM, usTR); List tmpList = getTurnDescriptions(wayList); @@ -462,7 +472,7 @@ public void testInstructionWithHighlyCustomProfileWithRoadsBase() { public void testEmptyList() { BaseGraph g = new BaseGraph.Builder(carManager).create(); g.getNodeAccess().setNode(1, 0, 0); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(0, 1); InstructionList il = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); assertEquals(0, il.size()); @@ -487,14 +497,14 @@ public void testFind() { na.setNode(6, 15.1, 10.1); na.setNode(7, 15.1, 9.8); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(1, 2).setDistance(10000)).setName("1-2"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 3).setDistance(10000)).setName("2-3"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(2, 6).setDistance(10000)).setName("2-6"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 4).setDistance(10000)).setName("3-4").setWayGeometry(waypoint); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(3, 7).setDistance(10000)).setName("3-7"); - GHUtility.setSpeed(60, true, true, carEncoder, g.edge(4, 5).setDistance(10000)).setName("4-5"); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "1-2")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 3).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "2-3")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(2, 6).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "2-6")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 4).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "3-4")).setWayGeometry(waypoint); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(3, 7).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "3-7")); + GHUtility.setSpeed(60, true, true, accessEnc, speedEnc, g.edge(4, 5).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "4-5")); - FastestWeighting weighting = new FastestWeighting(carEncoder); + FastestWeighting weighting = new FastestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(1, 5); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); diff --git a/core/src/test/java/com/graphhopper/util/PathSimplificationTest.java b/core/src/test/java/com/graphhopper/util/PathSimplificationTest.java index b9c7c61c361..9fc29d71c91 100644 --- a/core/src/test/java/com/graphhopper/util/PathSimplificationTest.java +++ b/core/src/test/java/com/graphhopper/util/PathSimplificationTest.java @@ -21,9 +21,11 @@ import com.graphhopper.routing.Dijkstra; import com.graphhopper.routing.InstructionsFromEdges; import com.graphhopper.routing.Path; +import com.graphhopper.routing.ev.BooleanEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.DecimalEncodedValueImpl; +import com.graphhopper.routing.ev.SimpleBooleanEncodedValue; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.FlagEncoders; import com.graphhopper.routing.util.TraversalMode; import com.graphhopper.routing.weighting.ShortestWeighting; import com.graphhopper.storage.BaseGraph; @@ -35,6 +37,8 @@ import java.util.*; +import static com.graphhopper.search.KVStorage.KeyValue.STREET_NAME; +import static com.graphhopper.search.KVStorage.KeyValue.createKV; import static com.graphhopper.util.Parameters.Details.AVERAGE_SPEED; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -50,8 +54,9 @@ public class PathSimplificationTest { @Test public void testScenario() { - FlagEncoder carEncoder = FlagEncoders.createCar(); - EncodingManager carManager = EncodingManager.create(carEncoder); + BooleanEncodedValue accessEnc = new SimpleBooleanEncodedValue("access", true); + DecimalEncodedValue speedEnc = new DecimalEncodedValueImpl("speed", 5, 5, false); + EncodingManager carManager = EncodingManager.start().add(accessEnc).add(speedEnc).build(); BaseGraph g = new BaseGraph.Builder(carManager).create(); // 0-1-2 // | | | @@ -72,55 +77,63 @@ public void testScenario() { na.setNode(7, 1.0, 1.1); na.setNode(8, 1.0, 1.2); - GHUtility.setSpeed(9, true, true, carEncoder, g.edge(0, 1).setDistance(10000)).setName("0-1"); - GHUtility.setSpeed(9, true, true, carEncoder, g.edge(1, 2).setDistance(11000)).setName("1-2"); + GHUtility.setSpeed(9, true, true, accessEnc, speedEnc, g.edge(0, 1).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "0-1")); + GHUtility.setSpeed(9, true, true, accessEnc, speedEnc, g.edge(1, 2).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "1-2")); - GHUtility.setSpeed(18, true, true, carEncoder, g.edge(0, 3).setDistance(11000)); - GHUtility.setSpeed(18, true, true, carEncoder, g.edge(1, 4).setDistance(10000)).setName("1-4"); - GHUtility.setSpeed(18, true, true, carEncoder, g.edge(2, 5).setDistance(11000)).setName("5-2"); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(0, 3).setDistance(11000)); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(1, 4).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "1-4")); + GHUtility.setSpeed(18, true, true, accessEnc, speedEnc, g.edge(2, 5).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "5-2")); - GHUtility.setSpeed(27, true, true, carEncoder, g.edge(3, 6).setDistance(11000)).setName("3-6"); - GHUtility.setSpeed(27, true, true, carEncoder, g.edge(4, 7).setDistance(10000)).setName("4-7"); - GHUtility.setSpeed(27, true, true, carEncoder, g.edge(5, 8).setDistance(10000)).setName("5-8"); + GHUtility.setSpeed(27, true, true, accessEnc, speedEnc, g.edge(3, 6).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "3-6")); + GHUtility.setSpeed(27, true, true, accessEnc, speedEnc, g.edge(4, 7).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "4-7")); + GHUtility.setSpeed(27, true, true, accessEnc, speedEnc, g.edge(5, 8).setDistance(10000)).setKeyValues(createKV(STREET_NAME, "5-8")); - GHUtility.setSpeed(36, true, true, carEncoder, g.edge(6, 7).setDistance(11000)).setName("6-7"); - EdgeIteratorState tmpEdge = GHUtility.setSpeed(36, true, true, carEncoder, g.edge(7, 8).setDistance(10000)); + GHUtility.setSpeed(36, true, true, accessEnc, speedEnc, g.edge(6, 7).setDistance(11000)).setKeyValues(createKV(STREET_NAME, "6-7")); + EdgeIteratorState tmpEdge = GHUtility.setSpeed(36, true, true, accessEnc, speedEnc, g.edge(7, 8).setDistance(10000)); PointList list = new PointList(); list.add(1.0, 1.15); list.add(1.0, 1.16); tmpEdge.setWayGeometry(list); - tmpEdge.setName("7-8"); + tmpEdge.setKeyValues(createKV(STREET_NAME, "7-8")); // missing edge name - GHUtility.setSpeed(45, true, true, carEncoder, g.edge(9, 10).setDistance(10000)); - tmpEdge = GHUtility.setSpeed(45, true, true, carEncoder, g.edge(8, 9).setDistance(20000)); + GHUtility.setSpeed(45, true, true, accessEnc, speedEnc, g.edge(9, 10).setDistance(10000)); + tmpEdge = GHUtility.setSpeed(45, true, true, accessEnc, speedEnc, g.edge(8, 9).setDistance(20000)); list.clear(); list.add(1.0, 1.3); list.add(1.0, 1.3001); list.add(1.0, 1.3002); list.add(1.0, 1.3003); - tmpEdge.setName("8-9"); + tmpEdge.setKeyValues(createKV(STREET_NAME, "8-9")); tmpEdge.setWayGeometry(list); // Path is: [0 0-1, 3 1-4, 6 4-7, 9 7-8, 11 8-9, 10 9-10] - ShortestWeighting weighting = new ShortestWeighting(carEncoder); + ShortestWeighting weighting = new ShortestWeighting(accessEnc, speedEnc); Path p = new Dijkstra(g, weighting, tMode).calcPath(0, 10); InstructionList wayList = InstructionsFromEdges.calcInstructions(p, g, weighting, carManager, usTR); Map> details = PathDetailsFromEdges.calcDetails(p, carManager, weighting, - Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0); + Arrays.asList(AVERAGE_SPEED), new PathDetailsBuilderFactory(), 0, g); + + PointList points = p.calcPoints(); + PointList waypoints = new PointList(2, g.getNodeAccess().is3D()); + waypoints.add(g.getNodeAccess(), 0); + waypoints.add(g.getNodeAccess(), 10); + List waypointIndices = Arrays.asList(0, points.size() - 1); ResponsePath responsePath = new ResponsePath(); responsePath.setInstructions(wayList); responsePath.addPathDetails(details); - responsePath.setPoints(p.calcPoints()); + responsePath.setPoints(points); + responsePath.setWaypoints(waypoints); + responsePath.setWaypointIndices(waypointIndices); - int numberOfPoints = p.calcPoints().size(); + int numberOfPoints = points.size(); - DouglasPeucker douglasPeucker = new DouglasPeucker(); + RamerDouglasPeucker ramerDouglasPeucker = new RamerDouglasPeucker(); // Do not simplify anything - douglasPeucker.setMaxDistance(0); + ramerDouglasPeucker.setMaxDistance(0); - PathSimplification.simplify(responsePath, douglasPeucker, true); + PathSimplification.simplify(responsePath, ramerDouglasPeucker, true); assertEquals(numberOfPoints, responsePath.getPoints().size()); @@ -128,9 +141,11 @@ public void testScenario() { responsePath.setInstructions(wayList); responsePath.addPathDetails(details); responsePath.setPoints(p.calcPoints()); + responsePath.setWaypoints(waypoints); + responsePath.setWaypointIndices(waypointIndices); - douglasPeucker.setMaxDistance(100000000); - PathSimplification.simplify(responsePath, douglasPeucker, true); + ramerDouglasPeucker.setMaxDistance(100000000); + PathSimplification.simplify(responsePath, ramerDouglasPeucker, true); assertTrue(numberOfPoints > responsePath.getPoints().size()); } @@ -138,7 +153,7 @@ public void testScenario() { @Test public void testSinglePartition() { // points are chosen such that DP will remove those marked with an x - // todo: we could go further and replace DouglasPeucker with some abstract thing that makes this easier to test + // todo: we could go further and replace Ramer-Douglas-Peucker with some abstract thing that makes this easier to test PointList points = new PointList(); points.add(48.89107, 9.33161); // 0 -> 0 points.add(48.89104, 9.33102); // 1 x @@ -159,14 +174,14 @@ public void testSinglePartition() { .add(7, 7); // end List partitions = new ArrayList<>(); partitions.add(partition); - PathSimplification.simplify(points, partitions, new DouglasPeucker()); + PathSimplification.simplify(points, partitions, new RamerDouglasPeucker()); // check points were modified correctly assertEquals(5, points.size()); origPoints.set(1, Double.NaN, Double.NaN, Double.NaN); origPoints.set(2, Double.NaN, Double.NaN, Double.NaN); origPoints.set(5, Double.NaN, Double.NaN, Double.NaN); - DouglasPeucker.removeNaN(origPoints); + RamerDouglasPeucker.removeNaN(origPoints); assertEquals(origPoints, points); // check partition was modified correctly @@ -185,7 +200,7 @@ public void testSinglePartition() { public void testMultiplePartitions() { // points are chosen such that DP will remove those marked with an x // got this data from running a request like this: - // http://localhost:8989/maps/?point=48.891273%2C9.325418&point=48.891005%2C9.322865&point=48.889877%2C9.32102&point=48.88975%2C9.31999&vehicle=car&weighting=fastest&elevation=true&debug=true&details=max_speed&details=street_name& + // http://localhost:8989/maps/?point=48.891273%2C9.325418&point=48.891005%2C9.322865&point=48.889877%2C9.32102&point=48.88975%2C9.31999&profile=car&weighting=fastest&elevation=true&debug=true&details=max_speed&details=street_name& PointList points = new PointList(20, true); points.add(48.89089, 9.32538, 270.0); // 0 -> 0 points.add(48.89090, 9.32527, 269.0); // 1 x @@ -233,7 +248,7 @@ public void testMultiplePartitions() { partitions.add(partition1); partitions.add(partition2); partitions.add(partition3); - PathSimplification.simplify(points, partitions, new DouglasPeucker()); + PathSimplification.simplify(points, partitions, new RamerDouglasPeucker()); // check points were modified correctly assertEquals(12, points.size()); @@ -241,7 +256,7 @@ public void testMultiplePartitions() { origPoints.set(2, Double.NaN, Double.NaN, Double.NaN); origPoints.set(5, Double.NaN, Double.NaN, Double.NaN); origPoints.set(13, Double.NaN, Double.NaN, Double.NaN); - DouglasPeucker.removeNaN(origPoints); + RamerDouglasPeucker.removeNaN(origPoints); assertEquals(origPoints, points); // check partitions were modified correctly diff --git a/core/src/test/java/com/graphhopper/util/DouglasPeuckerTest.java b/core/src/test/java/com/graphhopper/util/RamerDouglasPeuckerTest.java similarity index 90% rename from core/src/test/java/com/graphhopper/util/DouglasPeuckerTest.java rename to core/src/test/java/com/graphhopper/util/RamerDouglasPeuckerTest.java index 31dd87ca7fa..57e6fa7c31a 100644 --- a/core/src/test/java/com/graphhopper/util/DouglasPeuckerTest.java +++ b/core/src/test/java/com/graphhopper/util/RamerDouglasPeuckerTest.java @@ -29,7 +29,7 @@ /** * @author Peter Karich */ -public class DouglasPeuckerTest { +public class RamerDouglasPeuckerTest { // get some real life points from graphhopper API // http://217.92.216.224:8080/?point=49.945642,11.571436&point=49.946001,11.580706 @@ -57,7 +57,7 @@ public void testPathSimplify() { PointList pointList = new PointList(); pointList.parse2DJSON(points1); assertEquals(32, pointList.size()); - new DouglasPeucker().setMaxDistance(.5).simplify(pointList); + new RamerDouglasPeucker().setMaxDistance(.5).simplify(pointList); // Arrays.asList(2, 4, 6, 7, 8, 9, 12, 14, 15, 17, 18, 19, 20, 22, 24, 27, 28, 29, 31, 33), assertEquals(20, pointList.size()); } @@ -66,7 +66,7 @@ public void testPathSimplify() { public void testSimplifyCheckPointCount() { PointList pointList = new PointList(); pointList.parse2DJSON(points1); - DouglasPeucker dp = new DouglasPeucker().setMaxDistance(.5); + RamerDouglasPeucker dp = new RamerDouglasPeucker().setMaxDistance(.5); assertEquals(32, pointList.size()); dp.simplify(pointList); assertEquals(20, pointList.size()); @@ -93,7 +93,7 @@ public void testSimplifyCheckPointOrder() { PointList pointList = new PointList(); pointList.parse2DJSON(points2); assertEquals(13, pointList.size()); - new DouglasPeucker().setMaxDistance(.5).simplify(pointList); + new RamerDouglasPeucker().setMaxDistance(.5).simplify(pointList); assertEquals(11, pointList.size()); assertFalse(pointList.toString().contains("NaN"), pointList.toString()); assertEquals("(50.203764443183644,9.961074440801317), (50.20318963087774,9.960999562464645), (50.202952888673984,9.96094144793469), (50.20267889356641,9.96223002587773), (50.201853928011374,9.961859918278305), " @@ -123,10 +123,10 @@ public void testRemoveNaN() { pl.add(14, 14, 14); pl.add(Double.NaN, Double.NaN, Double.NaN); - DouglasPeucker.removeNaN(pl); + RamerDouglasPeucker.removeNaN(pl); // doing it again should be no problem - DouglasPeucker.removeNaN(pl); - DouglasPeucker.removeNaN(pl); + RamerDouglasPeucker.removeNaN(pl); + RamerDouglasPeucker.removeNaN(pl); assertEquals(8, pl.size()); List expected = Arrays.asList(1, 5, 6, 7, 8, 9, 10, 14); List given = new ArrayList<>(); @@ -146,7 +146,7 @@ public void test3dPathSimplify() { pointList.add(0.02, 0, 20); // can be removed pointList.add(0.03, 0, 30); // can't be removed pointList.add(0.04, 0, 50); - new DouglasPeucker().setMaxDistance(1).setElevationMaxDistance(1).simplify(pointList); + new RamerDouglasPeucker().setMaxDistance(1).setElevationMaxDistance(1).simplify(pointList); assertEquals("(0.0,0.0,0.0), (0.03,0.0,30.0), (0.04,0.0,50.0)", pointList.toString()); } @@ -156,7 +156,7 @@ public void test3dPathSimplifyElevationDisabled() { pointList.add(0, 0, 0); pointList.add(0.03, 0, 30); // would be kept, if we cared about elevation pointList.add(0.04, 0, 50); - new DouglasPeucker().setMaxDistance(1).setElevationMaxDistance(Double.MAX_VALUE).simplify(pointList); + new RamerDouglasPeucker().setMaxDistance(1).setElevationMaxDistance(Double.MAX_VALUE).simplify(pointList); assertEquals("(0.0,0.0,0.0), (0.04,0.0,50.0)", pointList.toString()); } @@ -168,7 +168,7 @@ public void test3dPathSimplifyElevationMaxDistFive() { pointList.add(0.02, 0, 20); // on straight line, remove pointList.add(0.03, 0, 30); // >5m from straight line, keep pointList.add(0.04, 0, 50); - new DouglasPeucker().setMaxDistance(1).setElevationMaxDistance(5).simplify(pointList); + new RamerDouglasPeucker().setMaxDistance(1).setElevationMaxDistance(5).simplify(pointList); assertEquals("(0.0,0.0,0.0), (0.03,0.0,30.0), (0.04,0.0,50.0)", pointList.toString()); } @@ -179,7 +179,7 @@ public void test3dPathSimplifyWithMissingElevation() { pointList.add(0, 0.5, Double.NaN); // on straight line in 2d space, ignore elevation pointList.add(0, 1, 14); // <5m from straight line (10), remove pointList.add(1, 1, 20); - new DouglasPeucker().setMaxDistance(1).setElevationMaxDistance(1).simplify(pointList); + new RamerDouglasPeucker().setMaxDistance(1).setElevationMaxDistance(1).simplify(pointList); assertEquals("(0.0,0.0,0.0), (0.0,1.0,14.0), (1.0,1.0,20.0)", pointList.toString()); } @@ -189,7 +189,7 @@ public void test3dSimplifyStartEndSame() { pointList.add(0, 0, 0); pointList.add(0.03, 0, 30); pointList.add(0, 0, 0); - new DouglasPeucker().setMaxDistance(1).setElevationMaxDistance(1).simplify(pointList); + new RamerDouglasPeucker().setMaxDistance(1).setElevationMaxDistance(1).simplify(pointList); assertEquals("(0.0,0.0,0.0), (0.03,0.0,30.0), (0.0,0.0,0.0)", pointList.toString()); } @@ -199,7 +199,7 @@ public void test2dSimplifyStartEndSame() { pointList.add(0, 0); pointList.add(0.03, 0); pointList.add(0, 0); - new DouglasPeucker().setMaxDistance(1).setElevationMaxDistance(1).simplify(pointList); + new RamerDouglasPeucker().setMaxDistance(1).setElevationMaxDistance(1).simplify(pointList); assertEquals("(0.0,0.0), (0.03,0.0), (0.0,0.0)", pointList.toString()); } } diff --git a/core/src/test/resources/com/graphhopper/reader/osm/test-multiple-conditional-turn-restrictions.xml b/core/src/test/resources/com/graphhopper/reader/osm/test-multiple-conditional-turn-restrictions.xml index 4927c2006a2..6e458d38477 100644 --- a/core/src/test/resources/com/graphhopper/reader/osm/test-multiple-conditional-turn-restrictions.xml +++ b/core/src/test/resources/com/graphhopper/reader/osm/test-multiple-conditional-turn-restrictions.xml @@ -14,11 +14,11 @@ (5,1)->(1,2): no_right_turn --> - - - - - + + + + + @@ -77,8 +77,7 @@ - - + @@ -87,8 +86,7 @@ - - + diff --git a/core/src/test/resources/com/graphhopper/reader/osm/test-osm7.xml b/core/src/test/resources/com/graphhopper/reader/osm/test-osm7.xml deleted file mode 100644 index 27cc623a5fb..00000000000 --- a/core/src/test/resources/com/graphhopper/reader/osm/test-osm7.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core/src/test/resources/com/graphhopper/reader/osm/test-restrictions.xml b/core/src/test/resources/com/graphhopper/reader/osm/test-restrictions.xml index 2a7744a1217..6a935391c64 100644 --- a/core/src/test/resources/com/graphhopper/reader/osm/test-restrictions.xml +++ b/core/src/test/resources/com/graphhopper/reader/osm/test-restrictions.xml @@ -1,18 +1,18 @@ + none @@ -56,7 +61,6 @@ core reader-gtfs tools - hmm-lib map-matching web-bundle web-api @@ -71,14 +75,20 @@ io.dropwizard dropwizard-dependencies - 2.0.21 + 2.0.34 pom import com.graphhopper.external jackson-datatype-jts - 0.12-2.5-1 + 2.14 + + + com.fasterxml.jackson.core + jackson-databind + + com.carrotsearch @@ -88,18 +98,7 @@ org.locationtech.jts jts-core - 1.15.1 - - - com.wdtinc - mapbox-vector-tile - 3.1.0 - - - com.google.protobuf - protobuf-java - - + 1.19.0 org.apache.commons @@ -212,6 +211,10 @@ + + maven-antrun-plugin + 1.8 + @@ -298,7 +301,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts @@ -312,20 +315,21 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.13 true ossrh https://oss.sonatype.org/ true + 10 - org.apache.maven.plugins maven-javadoc-plugin - 3.1.1 + 3.4.1 + true @@ -340,7 +344,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.0 + 3.2.1 attach-sources diff --git a/reader-gtfs/README.md b/reader-gtfs/README.md index dc01f957dd9..276b637824a 100644 --- a/reader-gtfs/README.md +++ b/reader-gtfs/README.md @@ -12,7 +12,7 @@ git clone https://github.com/graphhopper/graphhopper cd graphhopper # download GTFS from Berlin & Brandenburg in Germany (VBB) and the 'surrounding' OpenStreetMap data for the walk network -wget -O gtfs-vbb.zip https://www.vbb.de/fileadmin/user_upload/VBB/Dokumente/API-Datensaetze/GTFS.zip +wget -O gtfs-vbb.zip https://www.vbb.de/fileadmin/user_upload/VBB/Dokumente/API-Datensaetze/gtfs-mastscharf/GTFS.zip wget http://download.geofabrik.de/europe/germany/brandenburg-latest.osm.pbf mvn clean package -DskipTests diff --git a/reader-gtfs/config-example-pt.yml b/reader-gtfs/config-example-pt.yml index e618e2635ec..89fe1f4daa8 100644 --- a/reader-gtfs/config-example-pt.yml +++ b/reader-gtfs/config-example-pt.yml @@ -1,5 +1,6 @@ graphhopper: datareader.file: brandenburg-latest.osm.pbf + # for multiple files you can use: gtfs.file: file1.zip,file2.zip,file3.zip gtfs.file: gtfs-vbb.zip graph.location: graphs/brandenburg-with-transit diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphExplorer.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphExplorer.java index f38a3c3ae41..13a381cadff 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphExplorer.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphExplorer.java @@ -290,11 +290,9 @@ public int getId() { public Label.NodeId getAdjNode() { if (ptEdge != null) { - Integer streetNode = gtfsStorage.getPtToStreet().get(ptEdge.getAdjNode()); - return new Label.NodeId(streetNode != null ? streetNode : -1, ptEdge.getAdjNode()); + return new Label.NodeId(gtfsStorage.getPtToStreet().getOrDefault(ptEdge.getAdjNode(), -1), ptEdge.getAdjNode()); } else { - Integer ptNode = gtfsStorage.getStreetToPt().get(adjNode); - return new Label.NodeId(adjNode, ptNode != null ? ptNode : -1); + return new Label.NodeId(adjNode, gtfsStorage.getStreetToPt().getOrDefault(adjNode, -1)); } } diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphHopperGtfs.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphHopperGtfs.java index 23f08c1a11e..05023b60fd8 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphHopperGtfs.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GraphHopperGtfs.java @@ -21,9 +21,10 @@ import com.conveyal.gtfs.model.Transfer; import com.graphhopper.GraphHopper; import com.graphhopper.GraphHopperConfig; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.QueryGraph; +import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.storage.GraphHopperStorage; import com.graphhopper.storage.index.InMemConstructionIndex; import com.graphhopper.storage.index.IndexStructureInfo; import com.graphhopper.storage.index.LineIntIndex; @@ -56,15 +57,16 @@ protected void importOSM() { if (ghConfig.has("datareader.file")) { super.importOSM(); } else { - getGraphHopperStorage().create(1000); + createBaseGraphAndProperties(); + writeEncodingManagerToProperties(); } } @Override protected void importPublicTransit() { - ptGraph = new PtGraph(getGraphHopperStorage().getDirectory(), 100); - gtfsStorage = new GtfsStorage(getGraphHopperStorage().getDirectory()); - LineIntIndex stopIndex = new LineIntIndex(new BBox(-180.0, 180.0, -90.0, 90.0), getGraphHopperStorage().getDirectory(), "stop_index"); + ptGraph = new PtGraph(getBaseGraph().getDirectory(), 100); + gtfsStorage = new GtfsStorage(getBaseGraph().getDirectory()); + LineIntIndex stopIndex = new LineIntIndex(new BBox(-180.0, 180.0, -90.0, 90.0), getBaseGraph().getDirectory(), "stop_index"); if (getGtfsStorage().loadExisting()) { ptGraph.loadExisting(); stopIndex.loadExisting(); @@ -86,8 +88,9 @@ protected void importPublicTransit() { getGtfsStorage().getGtfsFeeds().forEach((id, gtfsFeed) -> { Transfers transfers = new Transfers(gtfsFeed); allTransfers.put(id, transfers); - GtfsReader gtfsReader = new GtfsReader(id, getGraphHopperStorage(), ptGraph, ptGraph, getGtfsStorage(), getLocationIndex(), transfers, indexBuilder); - gtfsReader.connectStopsToStreetNetwork(); + GtfsReader gtfsReader = new GtfsReader(id, ptGraph, ptGraph, getGtfsStorage(), getLocationIndex(), transfers, indexBuilder); + Weighting weighting = createWeighting(getProfile("foot"), new PMap()); + gtfsReader.connectStopsToStreetNetwork(new DefaultSnapFilter(weighting, getEncodingManager().getBooleanEncodedValue(Subnetwork.key("foot")))); LOGGER.info("Building transit graph for feed {}", gtfsFeed.feedId); gtfsReader.buildPtNetwork(); allReaders.put(id, gtfsReader); @@ -97,6 +100,7 @@ protected void importPublicTransit() { throw new RuntimeException("Error while constructing transit network. Is your GTFS file valid? Please check log for possible causes.", e); } ptGraph.flush(); + getGtfsStorage().flush(); stopIndex.store(indexBuilder); stopIndex.flush(); } @@ -107,14 +111,10 @@ protected void importPublicTransit() { private void interpolateTransfers(HashMap readers, Map allTransfers) { LOGGER.info("Looking for transfers"); final int maxTransferWalkTimeSeconds = ghConfig.getInt("gtfs.max_transfer_interpolation_walk_time_seconds", 120); - GraphHopperStorage graphHopperStorage = getGraphHopperStorage(); - QueryGraph queryGraph = QueryGraph.create(graphHopperStorage.getBaseGraph(), Collections.emptyList()); + QueryGraph queryGraph = QueryGraph.create(getBaseGraph(), Collections.emptyList()); Weighting transferWeighting = createWeighting(getProfile("foot"), new PMap()); final GraphExplorer graphExplorer = new GraphExplorer(queryGraph, ptGraph, transferWeighting, getGtfsStorage(), RealtimeFeed.empty(), true, true, false, 5.0, false, 0); - getGtfsStorage().getStationNodes().values().stream().distinct().map(n -> { - int streetNode = Optional.ofNullable(gtfsStorage.getPtToStreet().get(n)).orElse(-1); - return new Label.NodeId(streetNode, n); - }).forEach(stationNode -> { + getGtfsStorage().getStationNodes().values().stream().distinct().map(n -> new Label.NodeId(gtfsStorage.getPtToStreet().getOrDefault(n, -1), n)).forEach(stationNode -> { MultiCriteriaLabelSetting router = new MultiCriteriaLabelSetting(graphExplorer, true, false, false, 0, new ArrayList<>()); router.setLimitStreetTime(Duration.ofSeconds(maxTransferWalkTimeSeconds).toMillis()); for (Label label : router.calcLabels(stationNode, Instant.ofEpochMilli(0))) { @@ -150,7 +150,7 @@ private void insertInterpolatedTransfer(Label label, GtfsStorage.PlatformDescrip List transitions = Label.getTransitions(label.parent, true); int[] skippedEdgesForTransfer = transitions.stream().filter(t -> t.edge != null).mapToInt(t -> { Label.NodeId adjNode = t.label.node; - EdgeIteratorState edgeIteratorState = getGraphHopperStorage().getEdgeIteratorState(t.edge.getId(), adjNode.streetNode); + EdgeIteratorState edgeIteratorState = getBaseGraph().getEdgeIteratorState(t.edge.getId(), adjNode.streetNode); return edgeIteratorState.getEdgeKey(); }).toArray(); if (skippedEdgesForTransfer.length > 0) { // TODO: Elsewhere, we distinguish empty path ("at" a node) from no path @@ -162,7 +162,7 @@ private void insertInterpolatedTransfer(Label label, GtfsStorage.PlatformDescrip } private boolean isValidPath(int[] edgeKeys) { - List edges = Arrays.stream(edgeKeys).mapToObj(i -> getGraphHopperStorage().getEdgeIteratorStateForKey(i)).collect(Collectors.toList()); + List edges = Arrays.stream(edgeKeys).mapToObj(i -> getBaseGraph().getEdgeIteratorStateForKey(i)).collect(Collectors.toList()); for (int i = 1; i < edges.size(); i++) { if (edges.get(i).getBaseNode() != edges.get(i-1).getAdjNode()) return false; diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GtfsReader.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GtfsReader.java index caf38ef7150..95d18400a83 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GtfsReader.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GtfsReader.java @@ -22,13 +22,7 @@ import com.conveyal.gtfs.model.*; import com.google.common.collect.HashMultimap; import com.google.transit.realtime.GtfsRealtime; -import com.graphhopper.routing.ev.Subnetwork; -import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.weighting.FastestWeighting; -import com.graphhopper.storage.GraphHopperStorage; import com.graphhopper.storage.index.InMemConstructionIndex; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; @@ -77,8 +71,7 @@ static class TripWithStopTimes { private static final Logger LOGGER = LoggerFactory.getLogger(GtfsReader.class); - private final GraphHopperStorage graph; - private final LocationIndex walkNetworkIndex; + private final LocationIndex streetNetworkIndex; private final GtfsStorage gtfsStorage; private final Transfers transfers; @@ -87,11 +80,10 @@ static class TripWithStopTimes { private final Map>> departureTimelinesByStop = new HashMap<>(); private final Map>> arrivalTimelinesByStop = new HashMap<>(); - GtfsReader(String id, GraphHopperStorage graph, PtGraph ptGraph, PtGraphOut out, GtfsStorage gtfsStorage, LocationIndex walkNetworkIndex, Transfers transfers, InMemConstructionIndex indexBuilder) { + GtfsReader(String id, PtGraph ptGraph, PtGraphOut out, GtfsStorage gtfsStorage, LocationIndex streetNetworkIndex, Transfers transfers, InMemConstructionIndex indexBuilder) { this.id = id; - this.graph = graph; this.gtfsStorage = gtfsStorage; - this.walkNetworkIndex = walkNetworkIndex; + this.streetNetworkIndex = streetNetworkIndex; this.feed = this.gtfsStorage.getGtfsFeeds().get(id); this.transfers = transfers; this.startDate = feed.getStartDate(); @@ -101,17 +93,14 @@ static class TripWithStopTimes { this.indexBuilder = indexBuilder; } - void connectStopsToStreetNetwork() { - EncodingManager em = graph.getEncodingManager(); - FlagEncoder footEncoder = em.getEncoder("foot"); - final EdgeFilter filter = new DefaultSnapFilter(new FastestWeighting(footEncoder), em.getBooleanEncodedValue(Subnetwork.key("foot"))); + void connectStopsToStreetNetwork(EdgeFilter filter) { for (Stop stop : feed.stops.values()) { if (stop.location_type == 0) { // Only stops. Not interested in parent stations for now. - Snap locationSnap = walkNetworkIndex.findClosest(stop.stop_lat, stop.stop_lon, filter); - Integer stopNode; + Snap locationSnap = streetNetworkIndex.findClosest(stop.stop_lat, stop.stop_lon, filter); + int stopNode; if (locationSnap.isValid()) { - stopNode = gtfsStorage.getStreetToPt().get(locationSnap.getClosestNode()); - if (stopNode == null) { + stopNode = gtfsStorage.getStreetToPt().getOrDefault(locationSnap.getClosestNode(), -1); + if (stopNode == -1) { stopNode = out.createNode(); indexBuilder.addToAllTilesOnLine(stopNode, stop.stop_lat, stop.stop_lon, stop.stop_lat, stop.stop_lon); gtfsStorage.getPtToStreet().put(stopNode, locationSnap.getClosestNode()); diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GtfsStorage.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GtfsStorage.java index f8840d2586c..01f87aba735 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/GtfsStorage.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/GtfsStorage.java @@ -18,6 +18,10 @@ package com.graphhopper.gtfs; +import com.carrotsearch.hppc.IntIntHashMap; +import com.carrotsearch.hppc.IntObjectHashMap; +import com.carrotsearch.hppc.cursors.IntIntCursor; +import com.carrotsearch.hppc.cursors.IntObjectCursor; import com.conveyal.gtfs.GTFSFeed; import com.conveyal.gtfs.model.Fare; import com.graphhopper.storage.Directory; @@ -27,10 +31,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; +import java.io.*; import java.nio.file.Files; +import java.nio.file.Paths; import java.time.LocalDate; import java.time.ZoneId; import java.util.*; @@ -57,7 +60,7 @@ public void setPtGraph(PtGraph ptGraph) { this.ptGraph = ptGraph; } - public Map getSkippedEdgesForTransfer() { + public IntObjectHashMap getSkippedEdgesForTransfer() { return skippedEdgesForTransfer; } @@ -146,10 +149,10 @@ public String toString() { private Map gtfsFeeds = new HashMap<>(); private Map> faresByFeed; private Map stationNodes; - private Map skippedEdgesForTransfer; + private IntObjectHashMap skippedEdgesForTransfer; - private Map ptToStreet; - private Map streetToPt; + private IntIntHashMap ptToStreet; + private IntIntHashMap streetToPt; public enum EdgeType { HIGHWAY, ENTER_TIME_EXPANDED_NETWORK, LEAVE_TIME_EXPANDED_NETWORK, ENTER_PT, EXIT_PT, HOP, DWELL, BOARD, ALIGHT, OVERNIGHT, TRANSFER, WAIT, WAIT_ARRIVAL @@ -180,10 +183,42 @@ boolean loadExisting() { GTFSFeed feed = new GTFSFeed(dbFile); this.gtfsFeeds.put(gtfsFeedId, feed); } + ptToStreet = deserialize("pt_to_street"); + streetToPt = deserialize("street_to_pt"); + skippedEdgesForTransfer = deserializeIntoIntObjectHashMap("skipped_edges_for_transfer"); postInit(); return true; } + + private IntIntHashMap deserialize(String filename) { + try (FileInputStream in = new FileInputStream(dir.getLocation() + filename)) { + ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(in)); + int size = ois.readInt(); + IntIntHashMap result = new IntIntHashMap(); + for (int i = 0; i < size; i++) { + result.put(ois.readInt(), ois.readInt()); + } + return result; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private IntObjectHashMap deserializeIntoIntObjectHashMap(String filename) { + try (FileInputStream in = new FileInputStream(dir.getLocation() + filename)) { + ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(in)); + int size = ois.readInt(); + IntObjectHashMap result = new IntObjectHashMap<>(); + for (int i = 0; i < size; i++) { + result.put(ois.readInt(), ((int[]) ois.readObject())); + } + return result; + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + void create() { this.dir.create(); final File file = new File(dir.getLocation() + "/transit_schedule"); @@ -199,9 +234,9 @@ void create() { private void init() { this.gtfsFeedIds = data.getHashSet("gtfsFeeds"); this.stationNodes = data.getHashMap("stationNodes"); - this.ptToStreet = data.getHashMap("ptToStreet"); - this.streetToPt = data.getHashMap("streetToPt"); - this.skippedEdgesForTransfer = data.getHashMap("skippedEdgesForTransfer"); + this.ptToStreet = new IntIntHashMap(); + this.streetToPt = new IntIntHashMap(); + this.skippedEdgesForTransfer = new IntObjectHashMap<>(); } void loadGtfsFromZipFileOrDirectory(String id, File zipFileOrDirectory) { @@ -240,11 +275,11 @@ public Map> getFares() { return faresByFeed; } - public Map getPtToStreet() { + public IntIntHashMap getPtToStreet() { return ptToStreet; } - public Map getStreetToPt() { + public IntIntHashMap getStreetToPt() { return streetToPt; } @@ -256,6 +291,36 @@ public Map getStationNodes() { return stationNodes; } + public void flush() { + serialize("pt_to_street", ptToStreet); + serialize("street_to_pt", streetToPt); + serialize("skipped_edges_for_transfer", skippedEdgesForTransfer); + } + + private void serialize(String filename, IntObjectHashMap data) { + try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(Files.newOutputStream(Paths.get(dir.getLocation() + filename))))) { + oos.writeInt(data.size()); + for (IntObjectCursor e : data) { + oos.writeInt(e.key); + oos.writeObject(e.value); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void serialize(String filename, IntIntHashMap data) { + try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(Files.newOutputStream(Paths.get(dir.getLocation() + filename))))) { + oos.writeInt(data.size()); + for (IntIntCursor e : data) { + oos.writeInt(e.key); + oos.writeInt(e.value); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public abstract static class PlatformDescriptor implements Serializable { public String feed_id; public String stop_id; diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtLocationSnapper.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtLocationSnapper.java index da45de5b754..d15a6696f15 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtLocationSnapper.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtLocationSnapper.java @@ -6,7 +6,7 @@ import com.conveyal.gtfs.model.Stop; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; import com.graphhopper.util.PointList; @@ -16,7 +16,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.function.Supplier; public class PtLocationSnapper { @@ -33,12 +32,12 @@ public Result(QueryGraph queryGraph, List nodes, PointList points) } } - GraphHopperStorage graphHopperStorage; + BaseGraph baseGraph; LocationIndex locationIndex; GtfsStorage gtfsStorage; - public PtLocationSnapper(GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage) { - this.graphHopperStorage = graphHopperStorage; + public PtLocationSnapper(BaseGraph baseGraph, LocationIndex locationIndex, GtfsStorage gtfsStorage) { + this.baseGraph = baseGraph; this.locationIndex = locationIndex; this.gtfsStorage = gtfsStorage; } @@ -65,22 +64,22 @@ public Result snapAll(List locations, List snapFilters) Stop stop = gtfsStorage.getGtfsFeeds().get(e.getKey().feedId).stops.get(e.getKey().stopId); final Snap stopSnap = new Snap(stop.stop_lat, stop.stop_lon); stopSnap.setClosestNode(stopNodeId.value); - allSnaps.add(() -> new Label.NodeId(Optional.ofNullable(gtfsStorage.getPtToStreet().get(stopSnap.getClosestNode())).orElse(-1), stopSnap.getClosestNode())); + allSnaps.add(() -> new Label.NodeId(gtfsStorage.getPtToStreet().getOrDefault(stopSnap.getClosestNode(), -1), stopSnap.getClosestNode())); points.add(stopSnap.getQueryPoint().lat, stopSnap.getQueryPoint().lon); } } } else { pointSnaps.add(closest); - allSnaps.add(() -> new Label.NodeId(closest.getClosestNode(), Optional.ofNullable(gtfsStorage.getStreetToPt().get(closest.getClosestNode())).orElse(-1))); + allSnaps.add(() -> new Label.NodeId(closest.getClosestNode(), gtfsStorage.getStreetToPt().getOrDefault(closest.getClosestNode(), -1))); points.add(closest.getSnappedPoint()); } } else if (location instanceof GHStationLocation) { final Snap stopSnap = findByStopId((GHStationLocation) location, i); - allSnaps.add(() -> new Label.NodeId(Optional.ofNullable(gtfsStorage.getPtToStreet().get(stopSnap.getClosestNode())).orElse(-1), stopSnap.getClosestNode())); + allSnaps.add(() -> new Label.NodeId(gtfsStorage.getPtToStreet().getOrDefault(stopSnap.getClosestNode(), -1), stopSnap.getClosestNode())); points.add(stopSnap.getQueryPoint().lat, stopSnap.getQueryPoint().lon); } } - QueryGraph queryGraph = QueryGraph.create(graphHopperStorage.getBaseGraph(), pointSnaps); // modifies pointSnaps! + QueryGraph queryGraph = QueryGraph.create(baseGraph.getBaseGraph(), pointSnaps); // modifies pointSnaps! List nodes = new ArrayList<>(); for (Supplier supplier : allSnaps) { diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtRouterFreeWalkImpl.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtRouterFreeWalkImpl.java index c94dfaf8449..a0452862d4b 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtRouterFreeWalkImpl.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtRouterFreeWalkImpl.java @@ -30,9 +30,9 @@ import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.routing.weighting.FastestWeighting; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.util.*; import com.graphhopper.util.details.PathDetailsBuilderFactory; @@ -50,7 +50,8 @@ public final class PtRouterFreeWalkImpl implements PtRouter { private final GraphHopperConfig config; private final TranslationMap translationMap; private final Weighting accessEgressWeighting; - private final GraphHopperStorage graphHopperStorage; + private final BaseGraph baseGraph; + private final EncodingManager encodingManager; private final LocationIndex locationIndex; private final GtfsStorage gtfsStorage; private final RealtimeFeed realtimeFeed; @@ -59,12 +60,14 @@ public final class PtRouterFreeWalkImpl implements PtRouter { private final PtGraph ptGraph; @Inject - public PtRouterFreeWalkImpl(GraphHopperConfig config, TranslationMap translationMap, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage, RealtimeFeed realtimeFeed, PathDetailsBuilderFactory pathDetailsBuilderFactory) { + public PtRouterFreeWalkImpl(GraphHopperConfig config, TranslationMap translationMap, BaseGraph baseGraph, EncodingManager encodingManager, LocationIndex locationIndex, GtfsStorage gtfsStorage, RealtimeFeed realtimeFeed, PathDetailsBuilderFactory pathDetailsBuilderFactory) { this.config = config; - this.weightingFactory = new DefaultWeightingFactory(graphHopperStorage.getBaseGraph(), graphHopperStorage.getEncodingManager()); - this.accessEgressWeighting = new FastestWeighting(graphHopperStorage.getEncodingManager().getEncoder("foot")); + this.weightingFactory = new DefaultWeightingFactory(baseGraph.getBaseGraph(), encodingManager); + Profile accessEgressProfile = config.getProfiles().stream().filter(p -> p.getName().equals("foot")).findFirst().get(); + this.accessEgressWeighting = weightingFactory.createWeighting(accessEgressProfile, new PMap(), false); this.translationMap = translationMap; - this.graphHopperStorage = graphHopperStorage; + this.baseGraph = baseGraph; + this.encodingManager = encodingManager; this.locationIndex = locationIndex; this.gtfsStorage = gtfsStorage; this.realtimeFeed = realtimeFeed; @@ -80,15 +83,17 @@ public GHResponse route(Request request) { public static class Factory { private final GraphHopperConfig config; private final TranslationMap translationMap; - private final GraphHopperStorage graphHopperStorage; + private final BaseGraph baseGraph; + private final EncodingManager encodingManager; private final LocationIndex locationIndex; private final GtfsStorage gtfsStorage; private final Map transfers; - public Factory(GraphHopperConfig config, TranslationMap translationMap, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage) { + public Factory(GraphHopperConfig config, TranslationMap translationMap, BaseGraph baseGraph, EncodingManager encodingManager, LocationIndex locationIndex, GtfsStorage gtfsStorage) { this.config = config; this.translationMap = translationMap; - this.graphHopperStorage = graphHopperStorage; + this.baseGraph = baseGraph; + this.encodingManager = encodingManager; this.locationIndex = locationIndex; this.gtfsStorage = gtfsStorage; this.transfers = new HashMap<>(); @@ -100,11 +105,11 @@ public Factory(GraphHopperConfig config, TranslationMap translationMap, GraphHop public PtRouter createWith(GtfsRealtime.FeedMessage realtimeFeed) { Map realtimeFeeds = new HashMap<>(); realtimeFeeds.put("gtfs_0", realtimeFeed); - return new PtRouterFreeWalkImpl(config, translationMap, graphHopperStorage, locationIndex, gtfsStorage, RealtimeFeed.fromProtobuf(graphHopperStorage, gtfsStorage, this.transfers, realtimeFeeds), new PathDetailsBuilderFactory()); + return new PtRouterFreeWalkImpl(config, translationMap, baseGraph, encodingManager, locationIndex, gtfsStorage, RealtimeFeed.fromProtobuf(gtfsStorage, this.transfers, realtimeFeeds), new PathDetailsBuilderFactory()); } public PtRouter createWithoutRealtimeFeed() { - return new PtRouterFreeWalkImpl(config, translationMap, graphHopperStorage, locationIndex, gtfsStorage, RealtimeFeed.empty(), new PathDetailsBuilderFactory()); + return new PtRouterFreeWalkImpl(config, translationMap, baseGraph, encodingManager, locationIndex, gtfsStorage, RealtimeFeed.empty(), new PathDetailsBuilderFactory()); } } @@ -161,15 +166,15 @@ private class RequestHandler { requestedPathDetails = request.getPathDetails(); accessProfile = config.getProfiles().stream().filter(p -> p.getName().equals(request.getAccessProfile())).findFirst().get(); accessWeighting = weightingFactory.createWeighting(accessProfile, new PMap(), false); - accessSnapFilter = new DefaultSnapFilter(new FastestWeighting(graphHopperStorage.getEncodingManager().getEncoder(accessProfile.getVehicle())), graphHopperStorage.getEncodingManager().getBooleanEncodedValue(Subnetwork.key(accessProfile.getVehicle()))); + accessSnapFilter = new DefaultSnapFilter(accessWeighting, encodingManager.getBooleanEncodedValue(Subnetwork.key(accessProfile.getVehicle()))); egressProfile = config.getProfiles().stream().filter(p -> p.getName().equals(request.getEgressProfile())).findFirst().get(); egressWeighting = weightingFactory.createWeighting(egressProfile, new PMap(), false); - egressSnapFilter = new DefaultSnapFilter(new FastestWeighting(graphHopperStorage.getEncodingManager().getEncoder(egressProfile.getVehicle())), graphHopperStorage.getEncodingManager().getBooleanEncodedValue(Subnetwork.key(egressProfile.getVehicle()))); + egressSnapFilter = new DefaultSnapFilter(egressWeighting, encodingManager.getBooleanEncodedValue(Subnetwork.key(egressProfile.getVehicle()))); } GHResponse route() { StopWatch stopWatch = new StopWatch().start(); - PtLocationSnapper.Result result = new PtLocationSnapper(graphHopperStorage, locationIndex, gtfsStorage).snapAll(Arrays.asList(enter, exit), Arrays.asList(accessSnapFilter, egressSnapFilter)); + PtLocationSnapper.Result result = new PtLocationSnapper(baseGraph, locationIndex, gtfsStorage).snapAll(Arrays.asList(enter, exit), Arrays.asList(accessSnapFilter, egressSnapFilter)); queryGraph = result.queryGraph; response.addDebugInfo("idLookup:" + stopWatch.stop().getSeconds() + "s"); @@ -188,7 +193,7 @@ GHResponse route() { } private void parseSolutionsAndAddToResponse(List> solutions, PointList waypoints) { - TripFromLabel tripFromLabel = new TripFromLabel(queryGraph, graphHopperStorage.getEncodingManager(), gtfsStorage, realtimeFeed, pathDetailsBuilderFactory, walkSpeedKmH); + TripFromLabel tripFromLabel = new TripFromLabel(queryGraph, encodingManager, gtfsStorage, realtimeFeed, pathDetailsBuilderFactory, walkSpeedKmH); for (List solution : solutions) { final ResponsePath responsePath = tripFromLabel.createResponsePath(translation, waypoints, queryGraph, accessWeighting, egressWeighting, solution, requestedPathDetails); responsePath.setImpossible(solution.stream().anyMatch(t -> t.label.impossible)); diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtRouterImpl.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtRouterImpl.java index 99e78e73d65..9f0313881d9 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtRouterImpl.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/PtRouterImpl.java @@ -30,9 +30,9 @@ import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.routing.weighting.FastestWeighting; +import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.util.*; import com.graphhopper.util.details.PathDetailsBuilderFactory; @@ -51,7 +51,8 @@ public final class PtRouterImpl implements PtRouter { private final GraphHopperConfig config; private final TranslationMap translationMap; - private final GraphHopperStorage graphHopperStorage; + private final BaseGraph baseGraph; + private final EncodingManager encodingManager; private final LocationIndex locationIndex; private final GtfsStorage gtfsStorage; private final PtGraph ptGraph; @@ -60,11 +61,12 @@ public final class PtRouterImpl implements PtRouter { private final WeightingFactory weightingFactory; @Inject - public PtRouterImpl(GraphHopperConfig config, TranslationMap translationMap, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage, RealtimeFeed realtimeFeed, PathDetailsBuilderFactory pathDetailsBuilderFactory) { + public PtRouterImpl(GraphHopperConfig config, TranslationMap translationMap, BaseGraph baseGraph, EncodingManager encodingManager, LocationIndex locationIndex, GtfsStorage gtfsStorage, RealtimeFeed realtimeFeed, PathDetailsBuilderFactory pathDetailsBuilderFactory) { this.config = config; - this.weightingFactory = new DefaultWeightingFactory(graphHopperStorage.getBaseGraph(), graphHopperStorage.getEncodingManager()); + this.weightingFactory = new DefaultWeightingFactory(baseGraph, encodingManager); this.translationMap = translationMap; - this.graphHopperStorage = graphHopperStorage; + this.baseGraph = baseGraph; + this.encodingManager = encodingManager; this.locationIndex = locationIndex; this.gtfsStorage = gtfsStorage; this.ptGraph = gtfsStorage.getPtGraph(); @@ -80,15 +82,17 @@ public GHResponse route(Request request) { public static class Factory { private final GraphHopperConfig config; private final TranslationMap translationMap; - private final GraphHopperStorage graphHopperStorage; + private final BaseGraph baseGraph; + private final EncodingManager encodingManager; private final LocationIndex locationIndex; private final GtfsStorage gtfsStorage; private final Map transfers; - public Factory(GraphHopperConfig config, TranslationMap translationMap, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex, GtfsStorage gtfsStorage) { + public Factory(GraphHopperConfig config, TranslationMap translationMap, BaseGraph baseGraph, EncodingManager encodingManager, LocationIndex locationIndex, GtfsStorage gtfsStorage) { this.config = config; this.translationMap = translationMap; - this.graphHopperStorage = graphHopperStorage; + this.baseGraph = baseGraph; + this.encodingManager = encodingManager; this.locationIndex = locationIndex; this.gtfsStorage = gtfsStorage; this.transfers = new HashMap<>(); @@ -100,11 +104,11 @@ public Factory(GraphHopperConfig config, TranslationMap translationMap, GraphHop public PtRouter createWith(GtfsRealtime.FeedMessage realtimeFeed) { Map realtimeFeeds = new HashMap<>(); realtimeFeeds.put("gtfs_0", realtimeFeed); - return new PtRouterImpl(config, translationMap, graphHopperStorage, locationIndex, gtfsStorage, RealtimeFeed.fromProtobuf(graphHopperStorage, gtfsStorage, this.transfers, realtimeFeeds), new PathDetailsBuilderFactory()); + return new PtRouterImpl(config, translationMap, baseGraph, encodingManager, locationIndex, gtfsStorage, RealtimeFeed.fromProtobuf(gtfsStorage, this.transfers, realtimeFeeds), new PathDetailsBuilderFactory()); } public PtRouter createWithoutRealtimeFeed() { - return new PtRouterImpl(config, translationMap, graphHopperStorage, locationIndex, gtfsStorage, RealtimeFeed.empty(), new PathDetailsBuilderFactory()); + return new PtRouterImpl(config, translationMap, baseGraph, encodingManager, locationIndex, gtfsStorage, RealtimeFeed.empty(), new PathDetailsBuilderFactory()); } } @@ -161,15 +165,15 @@ private class RequestHandler { requestedPathDetails = request.getPathDetails(); accessProfile = config.getProfiles().stream().filter(p -> p.getName().equals(request.getAccessProfile())).findFirst().get(); accessWeighting = weightingFactory.createWeighting(accessProfile, new PMap(), false); - accessSnapFilter = new DefaultSnapFilter(new FastestWeighting(graphHopperStorage.getEncodingManager().getEncoder(accessProfile.getVehicle())), graphHopperStorage.getEncodingManager().getBooleanEncodedValue(Subnetwork.key(accessProfile.getVehicle()))); + accessSnapFilter = new DefaultSnapFilter(accessWeighting, encodingManager.getBooleanEncodedValue(Subnetwork.key(accessProfile.getVehicle()))); egressProfile = config.getProfiles().stream().filter(p -> p.getName().equals(request.getEgressProfile())).findFirst().get(); egressWeighting = weightingFactory.createWeighting(egressProfile, new PMap(), false); - egressSnapFilter = new DefaultSnapFilter(new FastestWeighting(graphHopperStorage.getEncodingManager().getEncoder(egressProfile.getVehicle())), graphHopperStorage.getEncodingManager().getBooleanEncodedValue(Subnetwork.key(egressProfile.getVehicle()))); + egressSnapFilter = new DefaultSnapFilter(egressWeighting, encodingManager.getBooleanEncodedValue(Subnetwork.key(egressProfile.getVehicle()))); } GHResponse route() { StopWatch stopWatch = new StopWatch().start(); - PtLocationSnapper.Result result = new PtLocationSnapper(graphHopperStorage, locationIndex, gtfsStorage).snapAll(Arrays.asList(enter, exit), Arrays.asList(accessSnapFilter, egressSnapFilter)); + PtLocationSnapper.Result result = new PtLocationSnapper(baseGraph, locationIndex, gtfsStorage).snapAll(Arrays.asList(enter, exit), Arrays.asList(accessSnapFilter, egressSnapFilter)); queryGraph = result.queryGraph; response.addDebugInfo("idLookup:" + stopWatch.stop().getSeconds() + "s"); @@ -188,7 +192,7 @@ GHResponse route() { } private void parseSolutionsAndAddToResponse(List> solutions, PointList waypoints) { - TripFromLabel tripFromLabel = new TripFromLabel(queryGraph, graphHopperStorage.getEncodingManager(), gtfsStorage, realtimeFeed, pathDetailsBuilderFactory, walkSpeedKmH); + TripFromLabel tripFromLabel = new TripFromLabel(queryGraph, encodingManager, gtfsStorage, realtimeFeed, pathDetailsBuilderFactory, walkSpeedKmH); for (List solution : solutions) { final ResponsePath responsePath = tripFromLabel.createResponsePath(translation, waypoints, queryGraph, accessWeighting, egressWeighting, solution, requestedPathDetails); responsePath.setImpossible(solution.stream().anyMatch(t -> t.label.impossible)); diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/RealtimeFeed.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/RealtimeFeed.java index ba5ff98c3b7..43162c92696 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/RealtimeFeed.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/RealtimeFeed.java @@ -26,7 +26,6 @@ import com.conveyal.gtfs.model.StopTime; import com.conveyal.gtfs.model.Trip; import com.google.transit.realtime.GtfsRealtime; -import com.graphhopper.storage.GraphHopperStorage; import org.mapdb.Fun; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,7 +66,7 @@ public static RealtimeFeed empty() { return new RealtimeFeed(Collections.emptyMap(), new IntHashSet(), new IntLongHashMap(), new IntLongHashMap(), Collections.emptyList()); } - public static RealtimeFeed fromProtobuf(GraphHopperStorage graphHopperStorage, GtfsStorage staticGtfs, Map transfers, Map feedMessages) { + public static RealtimeFeed fromProtobuf(GtfsStorage staticGtfs, Map transfers, Map feedMessages) { final IntHashSet blockedEdges = new IntHashSet(); final IntLongHashMap delaysForBoardEdges = new IntLongHashMap(); final IntLongHashMap delaysForAlightEdges = new IntLongHashMap(); @@ -94,7 +93,7 @@ public int createNode() { GTFSFeed feed = staticGtfs.getGtfsFeeds().get(feedKey); ZoneId timezone = ZoneId.of(feed.agency.values().stream().findFirst().get().agency_timezone); PtGraph ptGraphNodesAndEdges = staticGtfs.getPtGraph(); - final GtfsReader gtfsReader = new GtfsReader(feedKey, graphHopperStorage, ptGraphNodesAndEdges, overlayGraph, staticGtfs, null, transfers.get(feedKey), null); + final GtfsReader gtfsReader = new GtfsReader(feedKey, ptGraphNodesAndEdges, overlayGraph, staticGtfs, null, transfers.get(feedKey), null); Instant timestamp = Instant.ofEpochSecond(feedMessage.getHeader().getTimestamp()); LocalDate dateToChange = timestamp.atZone(timezone).toLocalDate(); //FIXME BitSet validOnDay = new BitSet(); @@ -380,9 +379,9 @@ public static GtfsReader.TripWithStopTimes toTripWithStopTimes(GTFSFeed feed, Gt stopTimes.add(stopTime); logger.trace("Number of stop times: {}", stopTimes.size()); } else { - // http://localhost:3000/route?point=45.51043713898763%2C-122.68381118774415&point=45.522104713562825%2C-122.6455307006836&weighting=fastest&pt.earliest_departure_time=2018-08-24T16%3A56%3A17Z&arrive_by=false&pt.max_walk_distance_per_leg=1000&pt.limit_solutions=5&locale=en-US&vehicle=pt&elevation=false&use_miles=false&points_encoded=false&pt.profile=true + // http://localhost:3000/route?point=45.51043713898763%2C-122.68381118774415&point=45.522104713562825%2C-122.6455307006836&weighting=fastest&pt.earliest_departure_time=2018-08-24T16%3A56%3A17Z&arrive_by=false&pt.max_walk_distance_per_leg=1000&pt.limit_solutions=5&locale=en-US&profile=pt&elevation=false&use_miles=false&points_encoded=false&pt.profile=true // long query: - // http://localhost:3000/route?point=45.518526513612244%2C-122.68612861633302&point=45.52908004573869%2C-122.6862144470215&weighting=fastest&pt.earliest_departure_time=2018-08-24T16%3A51%3A20Z&arrive_by=false&pt.max_walk_distance_per_leg=10000&pt.limit_solutions=4&locale=en-US&vehicle=pt&elevation=false&use_miles=false&points_encoded=false&pt.profile=true + // http://localhost:3000/route?point=45.518526513612244%2C-122.68612861633302&point=45.52908004573869%2C-122.6862144470215&weighting=fastest&pt.earliest_departure_time=2018-08-24T16%3A51%3A20Z&arrive_by=false&pt.max_walk_distance_per_leg=10000&pt.limit_solutions=4&locale=en-US&profile=pt&elevation=false&use_miles=false&points_encoded=false&pt.profile=true throw new RuntimeException(); } } diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/TripFromLabel.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/TripFromLabel.java index 82ca316c784..605a6ea746e 100644 --- a/reader-gtfs/src/main/java/com/graphhopper/gtfs/TripFromLabel.java +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/TripFromLabel.java @@ -401,7 +401,7 @@ private List parsePartitionToLegs(List path, Graph g pathh.setFromNode(path.get(0).label.node.streetNode); pathh.setEndNode(path.get(path.size() - 1).label.node.streetNode); pathh.setFound(true); - Map> pathDetails = PathDetailsFromEdges.calcDetails(pathh, encodedValueLookup, weighting, requestedPathDetails, pathDetailsBuilderFactory, 0); + Map> pathDetails = PathDetailsFromEdges.calcDetails(pathh, encodedValueLookup, weighting, requestedPathDetails, pathDetailsBuilderFactory, 0, graph); final Instant departureTime = Instant.ofEpochMilli(path.get(0).label.currentTime); final Instant arrivalTime = Instant.ofEpochMilli(path.get(path.size() - 1).label.currentTime); diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/analysis/Analysis.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/analysis/Analysis.java new file mode 100644 index 00000000000..3fb84fdae45 --- /dev/null +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/analysis/Analysis.java @@ -0,0 +1,59 @@ +package com.graphhopper.gtfs.analysis; + +import com.carrotsearch.hppc.BitSetIterator; +import com.carrotsearch.hppc.IntArrayList; +import com.carrotsearch.hppc.cursors.IntCursor; +import com.graphhopper.gtfs.GtfsStorage; +import com.graphhopper.gtfs.PtGraph; +import com.graphhopper.routing.subnetwork.TarjanSCC; +import com.graphhopper.routing.util.EdgeFilter; + +import java.util.*; + +import static com.graphhopper.gtfs.GtfsStorage.EdgeType.ENTER_PT; + +public class Analysis { + + + public static List> findStronglyConnectedComponentsOfStopGraph(PtGraph ptGraph) { + PtGraphAsAdjacencyList ptGraphAsAdjacencyList = new PtGraphAsAdjacencyList(ptGraph); + TarjanSCC.ConnectedComponents components = TarjanSCC.findComponents(ptGraphAsAdjacencyList, EdgeFilter.ALL_EDGES, false); + List> stronglyConnectedComponentsOfStopGraph = new ArrayList<>(); + for (IntArrayList component : components.getComponents()) { + ArrayList stopsOfComponent = new ArrayList<>(); + for (IntCursor intCursor : component) { + stopsOfComponent.addAll(getStopsForNode(ptGraph, intCursor.value)); + } + if (!stopsOfComponent.isEmpty()) { + stronglyConnectedComponentsOfStopGraph.add(stopsOfComponent); + } + } + BitSetIterator iter = components.getSingleNodeComponents().iterator(); + for (int i = iter.nextSetBit(); i >= 0; i = iter.nextSetBit()) { + List stopsForNode = getStopsForNode(ptGraph, i); + if (!stopsForNode.isEmpty()) { + stronglyConnectedComponentsOfStopGraph.add(stopsForNode); + } + } + return stronglyConnectedComponentsOfStopGraph; + } + + public static List getStopsForNode(PtGraph ptGraph, int i) { + EnumSet inEdgeTypes = EnumSet.noneOf(GtfsStorage.EdgeType.class); + for (PtGraph.PtEdge ptEdge : ptGraph.backEdgesAround(i)) { + inEdgeTypes.add(ptEdge.getType()); + } + EnumSet outEdgeTypes = EnumSet.noneOf(GtfsStorage.EdgeType.class); + for (PtGraph.PtEdge ptEdge : ptGraph.edgesAround(i)) { + outEdgeTypes.add(ptEdge.getType()); + } + if (inEdgeTypes.equals(EnumSet.of(GtfsStorage.EdgeType.EXIT_PT)) && outEdgeTypes.equals((EnumSet.of(ENTER_PT)))) { + Set stops = new HashSet<>(); + ptGraph.backEdgesAround(i).forEach(e -> stops.add(new GtfsStorage.FeedIdWithStopId(e.getAttrs().platformDescriptor.feed_id, e.getAttrs().platformDescriptor.stop_id))); + ptGraph.edgesAround(i).forEach(e -> stops.add(new GtfsStorage.FeedIdWithStopId(e.getAttrs().platformDescriptor.feed_id, e.getAttrs().platformDescriptor.stop_id))); + return new ArrayList<>(stops); + } else { + return Collections.emptyList(); + } + } +} diff --git a/reader-gtfs/src/main/java/com/graphhopper/gtfs/analysis/PtGraphAsAdjacencyList.java b/reader-gtfs/src/main/java/com/graphhopper/gtfs/analysis/PtGraphAsAdjacencyList.java new file mode 100644 index 00000000000..b9670f3bf5c --- /dev/null +++ b/reader-gtfs/src/main/java/com/graphhopper/gtfs/analysis/PtGraphAsAdjacencyList.java @@ -0,0 +1,332 @@ +package com.graphhopper.gtfs.analysis; + +import com.graphhopper.gtfs.PtGraph; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.AllEdgesIterator; +import com.graphhopper.routing.util.EdgeFilter; +import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.search.KVStorage; +import com.graphhopper.storage.*; +import com.graphhopper.util.*; +import com.graphhopper.util.shapes.BBox; + +import java.util.Iterator; +import java.util.List; + +class PtGraphAsAdjacencyList implements Graph { + private final PtGraph ptGraph; + + public PtGraphAsAdjacencyList(PtGraph ptGraph) { + this.ptGraph = ptGraph; + } + + @Override + public BaseGraph getBaseGraph() { + throw new RuntimeException(); + } + + @Override + public int getNodes() { + return ptGraph.getNodeCount(); + } + + @Override + public int getEdges() { + throw new RuntimeException(); + } + + @Override + public NodeAccess getNodeAccess() { + throw new RuntimeException(); + } + + @Override + public BBox getBounds() { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState edge(int a, int b) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState getEdgeIteratorState(int edgeId, int adjNode) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState getEdgeIteratorStateForKey(int edgeKey) { + throw new RuntimeException(); + } + + @Override + public int getOtherNode(int edge, int node) { + throw new RuntimeException(); + } + + @Override + public boolean isAdjacentToNode(int edge, int node) { + throw new RuntimeException(); + } + + @Override + public AllEdgesIterator getAllEdges() { + throw new RuntimeException(); + } + + @Override + public EdgeExplorer createEdgeExplorer(EdgeFilter filter) { + return new StationGraphEdgeExplorer(); + } + + @Override + public TurnCostStorage getTurnCostStorage() { + throw new RuntimeException(); + } + + @Override + public Weighting wrapWeighting(Weighting weighting) { + throw new RuntimeException(); + } + + private class StationGraphEdgeExplorer implements EdgeExplorer { + private int baseNode; + + @Override + public EdgeIterator setBaseNode(int baseNode) { + this.baseNode = baseNode; + return new StationGraphEdgeIterator(ptGraph.edgesAround(baseNode).iterator()); + } + + private class StationGraphEdgeIterator implements EdgeIterator { + private final Iterator iterator; + private PtGraph.PtEdge currentElement; + + public StationGraphEdgeIterator(Iterator iterator) { + this.iterator = iterator; + } + + @Override + public boolean next() { + if (iterator.hasNext()) { + this.currentElement = iterator.next(); + return true; + } else { + return false; + } + } + + @Override + public int getEdge() { + throw new RuntimeException(); + } + + @Override + public int getEdgeKey() { + throw new RuntimeException(); + } + + @Override + public int getReverseEdgeKey() { + throw new RuntimeException(); + } + + @Override + public int getBaseNode() { + throw new RuntimeException(); + } + + @Override + public int getAdjNode() { + assert currentElement.getBaseNode() == baseNode; + return currentElement.getAdjNode(); + } + + @Override + public PointList fetchWayGeometry(FetchMode mode) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState setWayGeometry(PointList list) { + throw new RuntimeException(); + } + + @Override + public double getDistance() { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState setDistance(double dist) { + throw new RuntimeException(); + } + + @Override + public IntsRef getFlags() { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState setFlags(IntsRef edgeFlags) { + throw new RuntimeException(); + } + + @Override + public boolean get(BooleanEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState set(BooleanEncodedValue property, boolean value) { + throw new RuntimeException(); + } + + @Override + public boolean getReverse(BooleanEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState setReverse(BooleanEncodedValue property, boolean value) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState set(BooleanEncodedValue property, boolean fwd, boolean bwd) { + throw new RuntimeException(); + } + + @Override + public int get(IntEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState set(IntEncodedValue property, int value) { + throw new RuntimeException(); + } + + @Override + public int getReverse(IntEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState setReverse(IntEncodedValue property, int value) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState set(IntEncodedValue property, int fwd, int bwd) { + throw new RuntimeException(); + } + + @Override + public double get(DecimalEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState set(DecimalEncodedValue property, double value) { + throw new RuntimeException(); + } + + @Override + public double getReverse(DecimalEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState setReverse(DecimalEncodedValue property, double value) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState set(DecimalEncodedValue property, double fwd, double bwd) { + throw new RuntimeException(); + } + + @Override + public > T get(EnumEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public > EdgeIteratorState set(EnumEncodedValue property, T value) { + throw new RuntimeException(); + } + + @Override + public > T getReverse(EnumEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public > EdgeIteratorState setReverse(EnumEncodedValue property, T value) { + throw new RuntimeException(); + } + + @Override + public > EdgeIteratorState set(EnumEncodedValue property, T fwd, T bwd) { + throw new RuntimeException(); + } + + @Override + public String get(StringEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState set(StringEncodedValue property, String value) { + throw new RuntimeException(); + } + + @Override + public String getReverse(StringEncodedValue property) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState setReverse(StringEncodedValue property, String value) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState set(StringEncodedValue property, String fwd, String bwd) { + throw new RuntimeException(); + } + + @Override + public String getName() { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState setKeyValues(List list) { + throw new RuntimeException(); + } + + @Override + public List getKeyValues() { + throw new RuntimeException(); + } + + @Override + public Object getValue(String key) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState detach(boolean reverse) { + throw new RuntimeException(); + } + + @Override + public EdgeIteratorState copyPropertiesFrom(EdgeIteratorState e) { + throw new RuntimeException(); + } + } + } +} diff --git a/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java b/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java index 4dce057aef0..bba801c3629 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java @@ -40,7 +40,8 @@ import static com.graphhopper.gtfs.GtfsHelper.time; import static com.graphhopper.util.Parameters.Details.EDGE_KEY; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; public class AnotherAgencyIT { @@ -53,6 +54,7 @@ public class AnotherAgencyIT { public static void init() { GraphHopperConfig ghConfig = new GraphHopperConfig(); ghConfig.putObject("graph.location", GRAPH_LOC); + ghConfig.putObject("import.osm.ignored_highways", ""); ghConfig.putObject("datareader.file", "files/beatty.osm"); ghConfig.putObject("gtfs.file", "files/sample-feed,files/another-sample-feed"); ghConfig.setProfiles(Arrays.asList( @@ -62,7 +64,7 @@ public static void init() { graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); graphHopperGtfs.importOrLoad(); - ptRouter = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getGraphHopperStorage(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()) + ptRouter = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getBaseGraph(), graphHopperGtfs.getEncodingManager(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()) .createWithoutRealtimeFeed(); } @@ -170,7 +172,7 @@ public void testWalkTransferBetweenFeeds() { assertEquals("10:00", LocalDateTime.ofInstant(walkDepartureTime, zoneId).toLocalTime().toString()); assertEquals(readWktLineString("LINESTRING (-116.76164 36.906093, -116.761812 36.905928, -116.76217 36.905659)"), transitSolution.getLegs().get(1).geometry); Instant walkArrivalTime = Instant.ofEpochMilli(transitSolution.getLegs().get(1).getArrivalTime().getTime()); - assertEquals("10:08:06.660", LocalDateTime.ofInstant(walkArrivalTime, zoneId).toLocalTime().toString()); + assertEquals("10:08:06.670", LocalDateTime.ofInstant(walkArrivalTime, zoneId).toLocalTime().toString()); assertEquals("EMSI,DADAN", ((Trip.PtLeg) transitSolution.getLegs().get(2)).stops.stream().map(s -> s.stop_id).collect(Collectors.joining(","))); } @@ -188,7 +190,7 @@ public void testMuseumToEmsi() { GHResponse route = ptRouter.route(ghRequest); ResponsePath walkRoute = route.getBest(); assertEquals(1, walkRoute.getLegs().size()); - assertEquals(486660, walkRoute.getTime()); // < 10 min, so the transfer in test above works ^^ + assertEquals(486670, walkRoute.getTime()); // < 10 min, so the transfer in test above works ^^ assertEquals(readWktLineString("LINESTRING (-116.76164 36.906093, -116.761812 36.905928, -116.76217 36.905659)"), walkRoute.getLegs().get(0).geometry); assertFalse(route.hasErrors()); } diff --git a/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java b/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java index e660fb00dac..756f5a7a013 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java @@ -50,6 +50,7 @@ public static void init() { GraphHopperConfig ghConfig = new GraphHopperConfig(); ghConfig.putObject("graph.location", GRAPH_LOC); ghConfig.putObject("gtfs.file", "files/another-sample-feed-extended-route-type.zip"); + ghConfig.putObject("import.osm.ignored_highways", ""); ghConfig.setProfiles(Arrays.asList( new Profile("foot").setVehicle("foot").setWeighting("fastest"), new Profile("car").setVehicle("car").setWeighting("fastest"))); @@ -57,7 +58,7 @@ public static void init() { graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); graphHopperGtfs.importOrLoad(); - ptRouter = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getGraphHopperStorage(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()).createWithoutRealtimeFeed(); + ptRouter = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getBaseGraph(), graphHopperGtfs.getEncodingManager(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()).createWithoutRealtimeFeed(); } @AfterAll diff --git a/reader-gtfs/src/test/java/com/graphhopper/FreeWalkIT.java b/reader-gtfs/src/test/java/com/graphhopper/FreeWalkIT.java index 9cfbcdb7207..d1e2e54b527 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/FreeWalkIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/FreeWalkIT.java @@ -30,7 +30,9 @@ import org.locationtech.jts.io.WKTReader; import java.io.File; -import java.time.*; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.Arrays; import java.util.stream.Collectors; @@ -53,6 +55,7 @@ public static void init() { ghConfig.putObject("datareader.file", "files/beatty.osm"); ghConfig.putObject("gtfs.file", "files/sample-feed,files/another-sample-feed"); ghConfig.putObject("gtfs.max_transfer_interpolation_walk_time_seconds", 0); + ghConfig.putObject("import.osm.ignored_highways", ""); // TODO: This setting vv is currently "dead", as in production it switches to PtRouterFreeWalkImpl, but // TODO: here it is instantiated directly. Refactor by having only one Router but two Solvers, similar // TODO: to the street router. @@ -64,7 +67,7 @@ public static void init() { graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); graphHopperGtfs.importOrLoad(); - ptRouter = new PtRouterFreeWalkImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getGraphHopperStorage(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()) + ptRouter = new PtRouterFreeWalkImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getBaseGraph(), graphHopperGtfs.getEncodingManager(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()) .createWithoutRealtimeFeed(); } @@ -95,7 +98,7 @@ public void testWalkTransferBetweenFeeds() { assertEquals("JUSTICE_COURT,MUSEUM", firstLeg.stops.stream().map(s -> s.stop_id).collect(Collectors.joining(","))); assertEquals("EMSI,DADAN", secondLeg.stops.stream().map(s -> s.stop_id).collect(Collectors.joining(","))); assertEquals(LocalDateTime.parse("2007-01-01T10:00:00").atZone(zoneId).toInstant(), transferLeg.getDepartureTime().toInstant()); - assertEquals(LocalDateTime.parse("2007-01-01T10:08:06.660").atZone(zoneId).toInstant(), transferLeg.getArrivalTime().toInstant()); + assertEquals(LocalDateTime.parse("2007-01-01T10:08:06.670").atZone(zoneId).toInstant(), transferLeg.getArrivalTime().toInstant()); assertEquals(readWktLineString("LINESTRING (-116.76164 36.906093, -116.761812 36.905928, -116.76217 36.905659)"), transitSolution.getLegs().get(1).geometry); diff --git a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java index 5b224f006c2..f73f83642f9 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java @@ -56,6 +56,7 @@ public class GraphHopperGtfsIT { public static void init() { GraphHopperConfig ghConfig = new GraphHopperConfig(); ghConfig.putObject("graph.location", GRAPH_LOC); + ghConfig.putObject("import.osm.ignored_highways", ""); ghConfig.putObject("gtfs.file", "files/sample-feed"); ghConfig.setProfiles(Arrays.asList( new Profile("foot").setVehicle("foot").setWeighting("fastest"), @@ -64,7 +65,7 @@ public static void init() { graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); graphHopperGtfs.importOrLoad(); - ptRouter = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getGraphHopperStorage(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()) + ptRouter = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getBaseGraph(), graphHopperGtfs.getEncodingManager(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()) .createWithoutRealtimeFeed(); } diff --git a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java index 7ed35fa89ad..50074d923c5 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java @@ -57,6 +57,7 @@ public class GraphHopperMultimodalIT { public static void init() { GraphHopperConfig ghConfig = new GraphHopperConfig(); ghConfig.putObject("datareader.file", "files/beatty.osm"); + ghConfig.putObject("import.osm.ignored_highways", ""); ghConfig.putObject("gtfs.file", "files/sample-feed"); ghConfig.putObject("graph.location", GRAPH_LOC); ghConfig.setProfiles(Arrays.asList( @@ -66,8 +67,15 @@ public static void init() { graphHopperGtfs = new GraphHopperGtfs(ghConfig); graphHopperGtfs.init(ghConfig); graphHopperGtfs.importOrLoad(); + + graphHopperGtfs.close(); + // Re-load read only + graphHopperGtfs = new GraphHopperGtfs(ghConfig); + graphHopperGtfs.init(ghConfig); + graphHopperGtfs.importOrLoad(); + locationIndex = graphHopperGtfs.getLocationIndex(); - graphHopper = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getGraphHopperStorage(), locationIndex, graphHopperGtfs.getGtfsStorage()) + graphHopper = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getBaseGraph(), graphHopperGtfs.getEncodingManager(), locationIndex, graphHopperGtfs.getGtfsStorage()) .createWithoutRealtimeFeed(); } @@ -93,11 +101,11 @@ public void testDepartureTimeOfAccessLegInProfileQuery() { ResponsePath firstTransitSolution = response.getAll().stream().filter(p -> p.getLegs().size() > 1).findFirst().get(); assertThat(firstTransitSolution.getLegs().get(0).getDepartureTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:41:04.827")); + .isEqualTo(LocalTime.parse("06:41:04.826")); assertThat(firstTransitSolution.getLegs().get(0).getArrivalTime().toInstant()) .isEqualTo(firstTransitSolution.getLegs().get(1).getDepartureTime().toInstant()); assertThat(firstTransitSolution.getLegs().get(2).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:52:02.640")); + .isEqualTo(LocalTime.parse("06:52:02.641")); // I like walking exactly as I like riding a bus (per travel time unit) // Now we get a walk solution which arrives earlier than the transit solutions. @@ -129,11 +137,11 @@ public void testDepartureTimeOfAccessLeg() { ResponsePath firstTransitSolution = response.getAll().stream().filter(p -> p.getLegs().size() > 1).findFirst().get(); // There can be a walk-only trip. assertThat(firstTransitSolution.getLegs().get(0).getDepartureTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:41:04.827")); + .isEqualTo(LocalTime.parse("06:41:04.826")); assertThat(firstTransitSolution.getLegs().get(0).getArrivalTime().toInstant()) .isEqualTo(firstTransitSolution.getLegs().get(1).getDepartureTime().toInstant()); assertThat(firstTransitSolution.getLegs().get(2).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:52:02.640")); + .isEqualTo(LocalTime.parse("06:52:02.641")); double EXPECTED_TOTAL_WALKING_DISTANCE = 496.96631386761055; assertThat(firstTransitSolution.getLegs().get(0).distance + firstTransitSolution.getLegs().get(2).distance) @@ -158,7 +166,7 @@ public void testDepartureTimeOfAccessLeg() { // In principle, this would dominate the transit solution, since it's faster, but // walking gets a penalty. assertThat(walkSolution.getLegs().get(0).getArrivalTime().toInstant().atZone(zoneId).toLocalTime()) - .isEqualTo(LocalTime.parse("06:51:10.301")); + .isEqualTo(LocalTime.parse("06:51:10.306")); assertThat(walkSolution.getLegs().size()).isEqualTo(1); assertThat(walkSolution.getNumChanges()).isEqualTo(-1); @@ -281,11 +289,11 @@ public void testSubnetworkRemoval() { // Go through all edges on removed foot subnetworks, and check that we can get to our destination station from there List responses = new ArrayList<>(); - AllEdgesIterator edges = graphHopperGtfs.getGraphHopperStorage().getAllEdges(); + AllEdgesIterator edges = graphHopperGtfs.getBaseGraph().getAllEdges(); while (edges.next()) { if (edges.get(footSub)) { Request ghRequest = new Request(Arrays.asList( - new GHPointLocation(new GHPoint(graphHopperGtfs.getGraphHopperStorage().getNodeAccess().getLat(edges.getBaseNode()), graphHopperGtfs.getGraphHopperStorage().getNodeAccess().getLon(edges.getBaseNode()))), + new GHPointLocation(new GHPoint(graphHopperGtfs.getBaseGraph().getNodeAccess().getLat(edges.getBaseNode()), graphHopperGtfs.getBaseGraph().getNodeAccess().getLon(edges.getBaseNode()))), new GHStationLocation("EMSI")), LocalDateTime.of(2007, 1, 1, 6, 40, 0).atZone(zoneId).toInstant()); ghRequest.setWalkSpeedKmH(50); // Yes, I can walk very fast, 50 km/h. Problem? diff --git a/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java b/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java index 72a67280f4f..6ac65eaa6b2 100644 --- a/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java +++ b/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java @@ -53,6 +53,7 @@ public static void init() { GraphHopperConfig ghConfig = new GraphHopperConfig(); ghConfig.putObject("gtfs.file", "files/sample-feed"); ghConfig.putObject("graph.location", GRAPH_LOC); + ghConfig.putObject("import.osm.ignored_highways", ""); ghConfig.setProfiles(Arrays.asList( new Profile("foot").setVehicle("foot").setWeighting("fastest"), new Profile("car").setVehicle("car").setWeighting("fastest"))); @@ -67,7 +68,7 @@ public static void init() { graphHopperGtfs.init(ghConfig); graphHopperGtfs.importOrLoad(); - graphHopperFactory = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getGraphHopperStorage(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()); + graphHopperFactory = new PtRouterImpl.Factory(ghConfig, new TranslationMap().doImport(), graphHopperGtfs.getBaseGraph(), graphHopperGtfs.getEncodingManager(), graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage()); } @AfterAll diff --git a/reader-gtfs/src/test/java/com/graphhopper/gtfs/analysis/AnalysisTest.java b/reader-gtfs/src/test/java/com/graphhopper/gtfs/analysis/AnalysisTest.java new file mode 100644 index 00000000000..ae7791be0e8 --- /dev/null +++ b/reader-gtfs/src/test/java/com/graphhopper/gtfs/analysis/AnalysisTest.java @@ -0,0 +1,79 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.gtfs.analysis; + +import com.graphhopper.GraphHopperConfig; +import com.graphhopper.config.Profile; +import com.graphhopper.gtfs.GraphHopperGtfs; +import com.graphhopper.gtfs.GtfsStorage; +import com.graphhopper.gtfs.PtGraph; +import com.graphhopper.util.Helper; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AnalysisTest { + + private static final String GRAPH_LOC = "target/AnotherAgencyIT"; + private static GraphHopperGtfs graphHopperGtfs; + + @BeforeAll + public static void init() { + GraphHopperConfig ghConfig = new GraphHopperConfig(); + ghConfig.putObject("graph.location", GRAPH_LOC); + ghConfig.putObject("datareader.file", "files/beatty.osm"); + ghConfig.putObject("gtfs.file", "files/sample-feed,files/another-sample-feed"); + ghConfig.putObject("import.osm.ignored_highways", ""); + ghConfig.setProfiles(Arrays.asList( + new Profile("foot").setVehicle("foot").setWeighting("fastest"), + new Profile("car").setVehicle("car").setWeighting("fastest"))); + Helper.removeDir(new File(GRAPH_LOC)); + graphHopperGtfs = new GraphHopperGtfs(ghConfig); + graphHopperGtfs.init(ghConfig); + graphHopperGtfs.importOrLoad(); + } + + @AfterAll + public static void close() { + graphHopperGtfs.close(); + } + + @Test + public void testStronglyConnectedComponentsOfStopGraph() { + PtGraph ptGraph = graphHopperGtfs.getPtGraph(); + List> stronglyConnectedComponentsOfStopGraph = Analysis.findStronglyConnectedComponentsOfStopGraph(ptGraph); + List largestComponent = stronglyConnectedComponentsOfStopGraph.get(0); + + assertThat(largestComponent) + .extracting("stopId") + .containsExactlyInAnyOrder("EMSI", "DADAN", "NADAV", "NANAA", "STAGECOACH", "AMV", "FUR_CREEK_RES", "BULLFROG", "BEATTY_AIRPORT", "AIRPORT"); + + List> singleElementComponents = stronglyConnectedComponentsOfStopGraph.subList(1, 4); + assertThat(singleElementComponents.stream().map(it -> it.get(0))) + .extracting("stopId") + .containsExactlyInAnyOrder("JUSTICE_COURT", "MUSEUM", "NEXT_TO_MUSEUM"); + } + +} diff --git a/tools/pom.xml b/tools/pom.xml index 4f0a2d0de34..93f66dcd770 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -44,10 +44,6 @@ com.graphhopper.external jackson-datatype-jts - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - org.slf4j slf4j-api diff --git a/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java b/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java index 2eaeecfd561..e6404c4b78e 100644 --- a/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java +++ b/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java @@ -87,7 +87,7 @@ private static void testPerformanceAutomaticNodeOrdering(String[] args) { final GraphHopper graphHopper = new GraphHopper(); String profile = "car_profile"; if (withTurnCosts) { - ghConfig.putObject("graph.flag_encoders", "car|turn_costs=true"); + ghConfig.putObject("graph.vehicles", "car|turn_costs=true"); ghConfig.setProfiles(Collections.singletonList( new Profile(profile).setVehicle("car").setWeighting("fastest").setTurnCosts(true).putHint(Parameters.Routing.U_TURN_COSTS, uTurnCosts) )); @@ -101,7 +101,7 @@ private static void testPerformanceAutomaticNodeOrdering(String[] args) { ghConfig.putObject("prepare.lm.landmarks", landmarks); } } else { - ghConfig.putObject("graph.flag_encoders", "car"); + ghConfig.putObject("graph.vehicles", "car"); ghConfig.setProfiles(Collections.singletonList( new Profile(profile).setVehicle("car").setWeighting("fastest").setTurnCosts(false) )); @@ -208,7 +208,7 @@ private static String getStatLine(TreeSet keys, Map resu private static void runCompareTest(final String algo, final GraphHopper graphHopper, final boolean withTurnCosts, final int uTurnCosts, long seed, final int iterations, final double threshold, final PMap results) { LOGGER.info("Running compare test for {}, using seed {}", algo, seed); - Graph g = graphHopper.getGraphHopperStorage(); + Graph g = graphHopper.getBaseGraph(); final int numNodes = g.getNodes(); final NodeAccess nodeAccess = g.getNodeAccess(); final Random random = new Random(seed); @@ -291,7 +291,7 @@ public int doCalc(boolean warmup, int run) { private static void runPerformanceTest(final String algo, final GraphHopper graphHopper, final boolean withTurnCosts, long seed, final int iterations, final PMap results) { - Graph g = graphHopper.getGraphHopperStorage(); + Graph g = graphHopper.getBaseGraph(); final int numNodes = g.getNodes(); final NodeAccess nodeAccess = g.getNodeAccess(); final Random random = new Random(seed); diff --git a/tools/src/main/java/com/graphhopper/tools/GraphSpeedMeasurement.java b/tools/src/main/java/com/graphhopper/tools/GraphSpeedMeasurement.java new file mode 100644 index 00000000000..c3fc6b98f57 --- /dev/null +++ b/tools/src/main/java/com/graphhopper/tools/GraphSpeedMeasurement.java @@ -0,0 +1,95 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.tools; + +import com.graphhopper.GraphHopper; +import com.graphhopper.GraphHopperConfig; +import com.graphhopper.routing.ev.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.weighting.custom.CustomProfile; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.util.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +public class GraphSpeedMeasurement { + + public static void main(String[] strs) { + PMap args = PMap.read(strs); + List result = new ArrayList<>(); + for (int speedBits = 7; speedBits <= 31; speedBits += 3) { + System.out.println("Running measurement for speedBits=" + speedBits); + GraphHopperConfig ghConfig = new GraphHopperConfig() + .putObject("datareader.file", args.getString("map", "map-matching/files/leipzig_germany.osm.pbf")) + .putObject("graph.location", args.getString("location", "graph-speed-measurement") + "-" + speedBits + "-gh") + .putObject("graph.dataaccess", args.getString("da", "RAM_STORE")) + .putObject("import.osm.ignored_highways", "") + .putObject("graph.vehicles", String.format("roads|speed_bits=%d,car|speed_bits=%d,bike|speed_bits=%d,foot|speed_bits=%d", speedBits, speedBits, speedBits, speedBits)) + .setProfiles(Arrays.asList( + new CustomProfile("car").setCustomModel(new CustomModel()).setVehicle("roads") + )); + GraphHopper hopper = new GraphHopper() + .init(ghConfig) + .importOrLoad(); + BaseGraph baseGraph = hopper.getBaseGraph(); + + EncodingManager em = hopper.getEncodingManager(); + List booleanEncodedValues = em.getEncodedValues().stream().filter(e -> e instanceof BooleanEncodedValue).map(e -> (BooleanEncodedValue) e).collect(Collectors.toList()); + List intEncodedValues = em.getEncodedValues().stream().filter(e -> e.getClass().equals(IntEncodedValueImpl.class)).map(e -> (IntEncodedValue) e).collect(Collectors.toList()); + List decimalEncodedValues = em.getEncodedValues().stream().filter(e -> e instanceof DecimalEncodedValue).map(e -> (DecimalEncodedValue) e).collect(Collectors.toList()); + List enumEncodedValues = em.getEncodedValues().stream().filter(e -> e.getClass().isAssignableFrom(EnumEncodedValue.class)).map(e -> (EnumEncodedValue) e).collect(Collectors.toList()); + + EdgeExplorer explorer = baseGraph.createEdgeExplorer(); + Random rnd = new Random(123); + + final int iterations = args.getInt("iters", 1_000_000); + // this parameter is quite interesting, because when we do multiple repeats per edge the differences between + // caching and not caching should become more clear. if we benefited from caching doing multiple repeats should + // not make much of a difference (thinking naively), while not caching should mean we need to do more work. + final int repeatsPerEdge = args.getInt("repeats_per_edge", 10); + MiniPerfTest t = new MiniPerfTest().setIterations(iterations) + .start((warmup, run) -> { + EdgeIterator iter = explorer.setBaseNode(rnd.nextInt(baseGraph.getNodes())); + double sum = 0; + while (iter.next()) { + for (int i = 0; i < repeatsPerEdge; i++) { + // note that reading **all** the EVs should be in favor of the caching solution, while cases + // with many encoded values where only a selected few are read should make the caching less + // important. but even in this scenario the caching provides no speedup apparently! + for (BooleanEncodedValue ev : booleanEncodedValues) sum += iter.get(ev) ? 1 : 0; + for (IntEncodedValue ev : intEncodedValues) sum += iter.get(ev) > 5 ? 1 : 0; + for (DecimalEncodedValue ev : decimalEncodedValues) sum += iter.get(ev) > 20 ? 1 : 0; + for (EnumEncodedValue ev : enumEncodedValues) sum += iter.get(ev).ordinal(); + } + } + return (int) sum; + }); + result.add(String.format("bits: %d, ints: %d, took: %.2fms, checksum: %d", speedBits, em.getIntsForFlags(), t.getSum(), t.getDummySum())); + System.out.println(result.get(result.size() - 1)); + } + System.out.println(); + System.out.println("### RESULT ###"); + for (String res : result) + System.out.println(res); + } +} diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java index e8573a4e2bb..e7429cd2339 100644 --- a/tools/src/main/java/com/graphhopper/tools/Measurement.java +++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java @@ -19,7 +19,6 @@ import com.carrotsearch.hppc.IntArrayList; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.graphhopper.*; import com.graphhopper.coll.GHBitSet; import com.graphhopper.coll.GHBitSetImpl; @@ -28,7 +27,10 @@ import com.graphhopper.config.Profile; import com.graphhopper.jackson.Jackson; import com.graphhopper.routing.ch.PrepareContractionHierarchies; +import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.ev.TurnCost; +import com.graphhopper.routing.ev.VehicleAccess; import com.graphhopper.routing.lm.LMConfig; import com.graphhopper.routing.lm.PrepareLandmarks; import com.graphhopper.routing.util.*; @@ -62,7 +64,6 @@ import static com.graphhopper.util.GHUtility.readCountries; import static com.graphhopper.util.Helper.*; import static com.graphhopper.util.Parameters.Algorithms.ALT_ROUTE; -import static com.graphhopper.util.Parameters.Routing.BLOCK_AREA; import static com.graphhopper.util.Parameters.Routing.U_TURN_COSTS; /** @@ -116,7 +117,6 @@ void start(PMap args) throws IOException { int count = args.getInt("measurement.count", 5000); put("measurement.name", args.getString("measurement.name", "no_name")); put("measurement.map", args.getString("datareader.file", "unknown")); - String blockAreaStr = args.getString("measurement.block_area", ""); final boolean useMeasurementTimeAsRefTime = args.getBool("measurement.use_measurement_time_as_ref_time", false); if (useMeasurementTimeAsRefTime && !useJson) { throw new IllegalArgumentException("Using measurement time as reference time only works with json files"); @@ -175,12 +175,10 @@ protected void importOSM() { hopper.importOrLoad(); - BaseGraph g = hopper.getGraphHopperStorage().getBaseGraph(); + BaseGraph g = hopper.getBaseGraph(); EncodingManager encodingManager = hopper.getEncodingManager(); - if (encodingManager.fetchEdgeEncoders().size() != 1) { - throw new IllegalArgumentException("There has to be exactly one encoder for each measurement"); - } - FlagEncoder encoder = encodingManager.fetchEdgeEncoders().get(0); + BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key(vehicle)); + boolean withTurnCosts = encodingManager.hasEncodedValue(TurnCost.key(vehicle)); StopWatch sw = new StopWatch().start(); try { @@ -188,7 +186,7 @@ protected void importOSM() { final boolean runSlow = args.getBool("measurement.run_slow_routing", true); printGraphDetails(g, vehicle); - measureGraphTraversal(g, encoder, count * 100); + measureGraphTraversal(g, accessEnc, count * 100); measureLocationIndex(g, hopper.getLocationIndex(), count); if (runSlow) { @@ -196,32 +194,35 @@ protected void importOSM() { boolean isLM = false; measureRouting(hopper, new QuerySettings("routing", count / 20, isCH, isLM). withInstructions()); - if (encoder.supportsTurnCosts()) + measureRouting(hopper, new QuerySettings("routing_alt", count / 500, isCH, isLM). + alternative()); + if (withTurnCosts) { measureRouting(hopper, new QuerySettings("routing_edge", count / 20, isCH, isLM). withInstructions().edgeBased()); - if (!blockAreaStr.isEmpty()) - measureRouting(hopper, new QuerySettings("routing_block_area", count / 20, isCH, isLM). - withInstructions().blockArea(blockAreaStr)); + // unfortunately alt routes are so slow that we cannot really afford many iterations + measureRouting(hopper, new QuerySettings("routing_edge_alt", count / 500, isCH, isLM). + edgeBased().alternative() + ); + } } if (hopper.getLMPreparationHandler().isEnabled()) { gcAndWait(); boolean isCH = false; boolean isLM = true; - Helper.parseList(args.getString("measurement.lm.active_counts", "[4,8,12,16]")).stream() + Helper.parseList(args.getString("measurement.lm.active_counts", "[4,8,12]")).stream() .mapToInt(Integer::parseInt).forEach(activeLMCount -> { - measureRouting(hopper, new QuerySettings("routingLM" + activeLMCount, count / 20, isCH, isLM). - withInstructions().activeLandmarks(activeLMCount)); - if (args.getBool("measurement.lm.edge_based", encoder.supportsTurnCosts())) { - measureRouting(hopper, new QuerySettings("routingLM" + activeLMCount + "_edge", count / 20, isCH, isLM). - withInstructions().activeLandmarks(activeLMCount).edgeBased()); - } - }); - - final int activeLMCount = 8; - if (!blockAreaStr.isEmpty()) - measureRouting(hopper, new QuerySettings("routingLM" + activeLMCount + "_block_area", count / 20, isCH, isLM). - withInstructions().activeLandmarks(activeLMCount).blockArea(blockAreaStr)); + measureRouting(hopper, new QuerySettings("routingLM" + activeLMCount, count / 20, isCH, isLM). + withInstructions().activeLandmarks(activeLMCount)); + measureRouting(hopper, new QuerySettings("routingLM" + activeLMCount + "_alt", count / 500, isCH, isLM). + activeLandmarks(activeLMCount).alternative()); + if (args.getBool("measurement.lm.edge_based", withTurnCosts)) { + measureRouting(hopper, new QuerySettings("routingLM" + activeLMCount + "_edge", count / 20, isCH, isLM). + withInstructions().activeLandmarks(activeLMCount).edgeBased()); + measureRouting(hopper, new QuerySettings("routingLM" + activeLMCount + "_alt_edge", count / 500, isCH, isLM). + activeLandmarks(activeLMCount).edgeBased().alternative()); + } + }); } if (hopper.getCHPreparationHandler().isEnabled()) { @@ -234,7 +235,7 @@ protected void importOSM() { gcAndWait(); measureRouting(hopper, new QuerySettings("routingCH", count, isCH, isLM). withInstructions().sod()); - measureRouting(hopper, new QuerySettings("routingCH_alt", count / 10, isCH, isLM). + measureRouting(hopper, new QuerySettings("routingCH_alt", count / 100, isCH, isLM). withInstructions().sod().alternative()); measureRouting(hopper, new QuerySettings("routingCH_with_hints", count, isCH, isLM). withInstructions().sod().withPointHints()); @@ -254,7 +255,7 @@ protected void importOSM() { if (edgeBasedCH != null) { measureRouting(hopper, new QuerySettings("routingCH_edge", count, isCH, isLM). edgeBased().withInstructions()); - measureRouting(hopper, new QuerySettings("routingCH_edge_alt", count / 10, isCH, isLM). + measureRouting(hopper, new QuerySettings("routingCH_edge_alt", count / 100, isCH, isLM). edgeBased().withInstructions().alternative()); measureRouting(hopper, new QuerySettings("routingCH_edge_no_instr", count, isCH, isLM). edgeBased()); @@ -296,13 +297,8 @@ protected void importOSM() { private GraphHopperConfig createConfigFromArgs(PMap args) { GraphHopperConfig ghConfig = new GraphHopperConfig(args); - String encodingManagerString = args.getString("graph.flag_encoders", "car"); - List tmpEncoders = EncodingManager.create(encodingManagerString).fetchEdgeEncoders(); - if (tmpEncoders.size() != 1) { - logger.warn("You configured multiple encoders, only the first one is used for the measurements"); - } - vehicle = tmpEncoders.get(0).toString(); - boolean turnCosts = tmpEncoders.get(0).supportsTurnCosts(); + vehicle = args.getString("measurement.vehicle", "car"); + boolean turnCosts = args.getBool("measurement.turn_costs", false); int uTurnCosts = args.getInt("measurement.u_turn_costs", 40); String weighting = args.getString("measurement.weighting", "fastest"); boolean useCHEdge = args.getBool("measurement.ch.edge", true); @@ -349,7 +345,6 @@ private static class QuerySettings { final boolean ch, lm; int activeLandmarks = -1; boolean withInstructions, withPointHints, sod, edgeBased, simplify, pathDetails, alternative; - String blockArea; int points = 2; QuerySettings(String prefix, int count, boolean isCH, boolean isLM) { @@ -403,11 +398,6 @@ QuerySettings alternative() { alternative = true; return this; } - - QuerySettings blockArea(String str) { - blockArea = str; - return this; - } } private void printGraphDetails(BaseGraph g, String vehicleStr) { @@ -436,10 +426,10 @@ private void measureLocationIndex(Graph g, final LocationIndex idx, int count) { print("location_index", miniPerf); } - private void measureGraphTraversal(final Graph graph, final FlagEncoder encoder, int count) { + private void measureGraphTraversal(final Graph graph, BooleanEncodedValue accessEnc, int count) { final Random rand = new Random(seed); - EdgeFilter outFilter = AccessFilter.outEdges(encoder.getAccessEnc()); + EdgeFilter outFilter = AccessFilter.outEdges(accessEnc); final EdgeExplorer outExplorer = graph.createEdgeExplorer(outFilter); MiniPerfTest miniPerf = new MiniPerfTest().setIterations(count).start((warmup, run) -> { int nodeId = rand.nextInt(maxNode); @@ -527,7 +517,7 @@ private void measureCountryAreaIndex(int count) { } private void measureRouting(final GraphHopper hopper, final QuerySettings querySettings) { - final Graph g = hopper.getGraphHopperStorage(); + final Graph g = hopper.getBaseGraph(); final AtomicLong maxDistance = new AtomicLong(0); final AtomicLong minDistance = new AtomicLong(Long.MAX_VALUE); final AtomicLong distSum = new AtomicLong(0); @@ -548,41 +538,25 @@ private void measureRouting(final GraphHopper hopper, final QuerySettings queryS MiniPerfTest miniPerf = new MiniPerfTest().setIterations(querySettings.count).start((warmup, run) -> { GHRequest req = new GHRequest(querySettings.points); IntArrayList nodes = new IntArrayList(querySettings.points); - // we try a few times to find points that do not lie within our blocked area - for (int i = 0; i < 5; i++) { - nodes.clear(); - List points = new ArrayList<>(); - List pointHints = new ArrayList<>(); - int tries = 0; - while (nodes.size() < querySettings.points) { - int node = rand.nextInt(maxNode); - if (++tries > g.getNodes()) - throw new RuntimeException("Could not find accessible points"); - // probe location. it could be a pedestrian area or an edge removed in the subnetwork removal process - if (GHUtility.count(edgeExplorer.setBaseNode(node)) == 0) - continue; - nodes.add(node); - points.add(new GHPoint(na.getLat(node), na.getLon(node))); - if (querySettings.withPointHints) { - // we add some point hint to make sure the name similarity filter has to do some actual work - pointHints.add("probably_not_found"); - } - } - req.setPoints(points); - req.setPointHints(pointHints); - if (querySettings.blockArea == null) - break; - try { - req.getHints().putObject(BLOCK_AREA, querySettings.blockArea); - // run this method to check if creating the blocked area is possible - GraphEdgeIdFinder.createBlockArea(hopper.getGraphHopperStorage(), hopper.getLocationIndex(), req.getPoints(), req.getHints(), edgeFilter); - break; - } catch (IllegalArgumentException ex) { - if (i >= 4) - throw new RuntimeException("Give up after 5 tries. Cannot find points outside of the block_area " - + querySettings.blockArea + " - too big block_area or map too small? Request:" + req); + List points = new ArrayList<>(); + List pointHints = new ArrayList<>(); + int tries = 0; + while (nodes.size() < querySettings.points) { + int node = rand.nextInt(maxNode); + if (++tries > g.getNodes()) + throw new RuntimeException("Could not find accessible points"); + // probe location. it could be a pedestrian area or an edge removed in the subnetwork removal process + if (GHUtility.count(edgeExplorer.setBaseNode(node)) == 0) + continue; + nodes.add(node); + points.add(new GHPoint(na.getLat(node), na.getLon(node))); + if (querySettings.withPointHints) { + // we add some point hint to make sure the name similarity filter has to do some actual work + pointHints.add("probably_not_found"); } } + req.setPoints(points); + req.setPointHints(pointHints); req.setProfile(profileName); req.getHints(). putObject(CH.DISABLE, !querySettings.ch). @@ -631,8 +605,12 @@ else if (!toLowerCase(rsp.getErrors().get(0).getMessage()).contains("not found") maxVisitedNodes.set(visitedNodes); } + rsp.getAll().forEach(p -> { + long dist = (long) p.getDistance(); + distSum.addAndGet(dist); + }); + long dist = (long) responsePath.getDistance(); - distSum.addAndGet(dist); GHPoint prev = req.getPoints().get(0); for (GHPoint point : req.getPoints()) { @@ -723,9 +701,9 @@ private void storeJson(String jsonLocation, boolean useMeasurementTimeAsRefTime) } private CustomModel loadCustomModel(String customModelLocation) { - ObjectMapper yamlOM = Jackson.initObjectMapper(new ObjectMapper(new YAMLFactory())); + ObjectMapper om = Jackson.initObjectMapper(new ObjectMapper()); try { - return yamlOM.readValue(new File(customModelLocation), CustomModel.class); + return om.readValue(Helper.readJSONFileWithoutComments(customModelLocation), CustomModel.class); } catch (Exception e) { throw new RuntimeException("Cannot load custom_model from " + customModelLocation, e); } diff --git a/tools/src/main/java/com/graphhopper/ui/DebugAStarBi.java b/tools/src/main/java/com/graphhopper/ui/DebugAStarBi.java index ef2cda5d000..9c11991b18d 100644 --- a/tools/src/main/java/com/graphhopper/ui/DebugAStarBi.java +++ b/tools/src/main/java/com/graphhopper/ui/DebugAStarBi.java @@ -45,7 +45,7 @@ public void setGraphics2D(Graphics2D g2) { @Override public void updateBestPath(double edgeWeight, SPTEntry entry, int origEdgeId, int traversalId, boolean reverse) { if (g2 != null) { - mg.plotNode(g2, traversalId, Color.YELLOW); + mg.plotNode(g2, entry.adjNode, Color.YELLOW); } super.updateBestPath(edgeWeight, entry, origEdgeId, traversalId, reverse); } diff --git a/tools/src/main/java/com/graphhopper/ui/GraphicsWrapper.java b/tools/src/main/java/com/graphhopper/ui/GraphicsWrapper.java index a0cc5f6cb41..d6916f68777 100644 --- a/tools/src/main/java/com/graphhopper/ui/GraphicsWrapper.java +++ b/tools/src/main/java/com/graphhopper/ui/GraphicsWrapper.java @@ -32,7 +32,7 @@ */ public class GraphicsWrapper { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final NodeAccess na; + private NodeAccess na; private double scaleX; private double scaleY; private double offsetX; @@ -47,6 +47,10 @@ public GraphicsWrapper(Graph g) { offsetX = -b.minLon; } + public void setNodeAccess(Graph graph) { + this.na = graph.getNodeAccess(); + } + public double getOffsetX() { return offsetX; } diff --git a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java index 2ff4eb1b5c9..06fb256a847 100644 --- a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java +++ b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java @@ -28,13 +28,16 @@ import com.graphhopper.routing.*; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.DecimalEncodedValue; +import com.graphhopper.routing.ev.VehicleAccess; +import com.graphhopper.routing.ev.VehicleSpeed; import com.graphhopper.routing.lm.LMRoutingAlgorithmFactory; import com.graphhopper.routing.lm.LandmarkStorage; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.querygraph.QueryRoutingCHGraph; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.routing.util.EdgeFilter; -import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.util.TraversalMode; +import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.RoutingCHGraph; @@ -73,14 +76,12 @@ public class MiniGraphUI { private final BaseGraph graph; private final NodeAccess na; private final MapLayer pathLayer; - private final FlagEncoder encoder; private final DecimalEncodedValue avSpeedEnc; private final BooleanEncodedValue accessEnc; private final boolean useCH; // for moving int currentPosX; int currentPosY; - private Path path; private LocationIndexTree index; private String latLon = ""; private GraphicsWrapper mg; @@ -88,18 +89,20 @@ public class MiniGraphUI { private LayeredPanel mainPanel; private MapLayer roadsLayer; private boolean fastPaint = false; + private boolean showQuadTree = false; private Snap fromRes; private Snap toRes; + private QueryGraph qGraph; public static void main(String[] strs) { PMap args = PMap.read(strs); args.putObject("datareader.file", args.getString("datareader.file", "core/files/monaco.osm.gz")); args.putObject("graph.location", args.getString("graph.location", "tools/target/mini-graph-ui-gh")); - args.putObject("graph.flag_encoders", args.getString("graph.flag_encoders", "car")); GraphHopperConfig ghConfig = new GraphHopperConfig(args); ghConfig.setProfiles(Arrays.asList( new Profile("profile") .setVehicle("car") + .setTurnCosts(true) .setWeighting("fastest") )); ghConfig.setCHProfiles(Arrays.asList( @@ -115,18 +118,16 @@ public static void main(String[] strs) { } public MiniGraphUI(GraphHopper hopper, boolean debug, boolean useCH) { - this.graph = hopper.getGraphHopperStorage().getBaseGraph(); + this.graph = hopper.getBaseGraph(); this.na = graph.getNodeAccess(); - encoder = hopper.getEncodingManager().fetchEdgeEncoders().get(0); - avSpeedEnc = encoder.getAverageSpeedEnc(); - accessEnc = encoder.getAccessEnc(); + String vehicle = hopper.getProfiles().get(0).getVehicle(); + accessEnc = hopper.getEncodingManager().getBooleanEncodedValue(VehicleAccess.key(vehicle)); + avSpeedEnc = hopper.getEncodingManager().getDecimalEncodedValue(VehicleSpeed.key(vehicle)); this.useCH = useCH; logger.info("locations:" + graph.getNodes() + ", debug:" + debug); mg = new GraphicsWrapper(graph); - // prepare node quadtree to 'enter' the graph. create a 313*313 grid => <3km -// this.index = new DebugLocation2IDQuadtree(roadGraph, mg); this.index = (LocationIndexTree) hopper.getLocationIndex(); infoPanel = new JPanel() { @Override @@ -154,7 +155,6 @@ protected void paintComponent(Graphics g) { @Override public void paintComponent(final Graphics2D g2) { clearGraphics(g2); - int locs = graph.getNodes(); Rectangle d = getBounds(); BBox b = mg.setBounds(0, d.width, 0, d.height); if (fastPaint) { @@ -162,18 +162,6 @@ public void paintComponent(final Graphics2D g2) { bitset.clear(); } -// g2.setColor(Color.BLUE); -// double fromLat = 42.56819, fromLon = 1.603231; -// mg.plotText(g2, fromLat, fromLon, "from"); -// Snap from = index.findClosest(fromLat, fromLon, EdgeFilter.ALL_EDGES); -// double toLat = 42.571034, toLon = 1.520662; -// mg.plotText(g2, toLat, toLon, "to"); -// Snap to = index.findClosest(toLat, toLon, EdgeFilter.ALL_EDGES); -// -// g2.setColor(Color.RED.brighter().brighter()); -// path = prepare.createAlgo().calcPath(from, to); -// System.out.println("now: " + path.toFlagEncodersAsString()); -// plotPath(path, g2, 1); g2.setColor(Color.black); Color[] speedColors = generateColors(15); @@ -238,27 +226,28 @@ public void paintComponent(final Graphics2D g2) { } } - index.query(graph.getBounds(), new LocationIndexTree.Visitor() { - @Override - public boolean isTileInfo() { - return true; - } + if (showQuadTree) + index.query(graph.getBounds(), new LocationIndexTree.Visitor() { + @Override + public boolean isTileInfo() { + return true; + } - @Override - public void onTile(BBox bbox, int depth) { - int width = Math.max(1, Math.min(4, 4 - depth)); - g2.setColor(Color.GRAY); - mg.plotEdge(g2, bbox.minLat, bbox.minLon, bbox.minLat, bbox.maxLon, width); - mg.plotEdge(g2, bbox.minLat, bbox.maxLon, bbox.maxLat, bbox.maxLon, width); - mg.plotEdge(g2, bbox.maxLat, bbox.maxLon, bbox.maxLat, bbox.minLon, width); - mg.plotEdge(g2, bbox.maxLat, bbox.minLon, bbox.minLat, bbox.minLon, width); - } + @Override + public void onTile(BBox bbox, int depth) { + int width = Math.max(1, Math.min(4, 4 - depth)); + g2.setColor(Color.GRAY); + mg.plotEdge(g2, bbox.minLat, bbox.minLon, bbox.minLat, bbox.maxLon, width); + mg.plotEdge(g2, bbox.minLat, bbox.maxLon, bbox.maxLat, bbox.maxLon, width); + mg.plotEdge(g2, bbox.maxLat, bbox.maxLon, bbox.maxLat, bbox.minLon, width); + mg.plotEdge(g2, bbox.maxLat, bbox.minLon, bbox.minLat, bbox.minLon, width); + } - @Override - public void onEdge(int edgeId) { - // mg.plotNode(g2, node, Color.BLUE); - } - }); + @Override + public void onEdge(int edgeId) { + // mg.plotNode(g2, node, Color.BLUE); + } + }); g2.setColor(Color.WHITE); g2.fillRect(0, 0, 1000, 20); @@ -274,12 +263,11 @@ public void onEdge(int edgeId) { mainPanel.addLayer(pathLayer = new DefaultMapLayer() { @Override public void paintComponent(final Graphics2D g2) { - if (fromRes == null || toRes == null) + if (qGraph == null) return; makeTransparent(g2); - QueryGraph qGraph = QueryGraph.create(graph, fromRes, toRes); - RoutingAlgorithm algo = createAlgo(hopper); + RoutingAlgorithm algo = createAlgo(hopper, qGraph); if (algo instanceof DebugAlgo) { ((DebugAlgo) algo).setGraphics2D(g2); } @@ -287,57 +275,44 @@ public void paintComponent(final Graphics2D g2) { StopWatch sw = new StopWatch().start(); logger.info("start searching with " + algo + " from:" + fromRes + " to:" + toRes); -// GHPoint qp = fromRes.getQueryPoint(); -// TIntHashSet set = index.findNetworkEntries(qp.lat, qp.lon, 1); -// TIntIterator nodeIter = set.iterator(); -// DistanceCalc distCalc = new DistancePlaneProjection(); -// System.out.println("set:" + set.size()); -// while (nodeIter.hasNext()) -// { -// int nodeId = nodeIter.next(); -// double lat = graph.getNodeAccess().getLat(nodeId); -// double lon = graph.getNodeAccess().getLon(nodeId); -// int dist = (int) Math.round(distCalc.calcDist(qp.lat, qp.lon, lat, lon)); -// mg.plotText(g2, lat, lon, nodeId + ": " + dist); -// mg.plotNode(g2, nodeId, Color.red); -// } Color red = Color.red.brighter(); g2.setColor(red); mg.plotNode(g2, qGraph.getNodeAccess(), fromRes.getClosestNode(), red, 10, ""); mg.plotNode(g2, qGraph.getNodeAccess(), toRes.getClosestNode(), red, 10, ""); g2.setColor(Color.blue.brighter().brighter()); - path = algo.calcPath(fromRes.getClosestNode(), toRes.getClosestNode()); + java.util.List paths = algo.calcPaths(fromRes.getClosestNode(), toRes.getClosestNode()); sw.stop(); // if directed edges - if (!path.isFound()) { + if (paths.isEmpty() || !paths.get(0).isFound()) { logger.warn("path not found! direction not valid?"); return; } - + Path best = paths.get(0); logger.info("found path in " + sw.getSeconds() + "s with nodes:" - + path.calcNodes().size() + ", millis: " + path.getTime() + + best.calcNodes().size() + ", millis: " + best.getTime() + ", visited nodes:" + algo.getVisitedNodes()); g2.setColor(red); - plotPath(path, g2, 4); + for (Path p : paths) + plotPath(p, g2, 3); } }); if (debug) { - // disable double buffering for debugging drawing - nice! when do we need DebugGraphics then? + // disable double buffering to see graphic changes while debugging. E.g. to set a break point in the + // algorithm its updateBest method and see the shortest path tree increasing everytime the program continues. RepaintManager repaintManager = RepaintManager.currentManager(mainPanel); repaintManager.setDoubleBufferingEnabled(false); mainPanel.setBuffering(false); } } - private RoutingAlgorithm createAlgo(GraphHopper hopper) { + private RoutingAlgorithm createAlgo(GraphHopper hopper, QueryGraph qGraph) { Profile profile = hopper.getProfiles().iterator().next(); if (useCH) { RoutingCHGraph chGraph = hopper.getCHGraphs().get(profile.getName()); logger.info("CH algo, profile: " + profile.getName()); - QueryGraph qGraph = QueryGraph.create(hopper.getGraphHopperStorage(), fromRes, toRes); QueryRoutingCHGraph queryRoutingCHGraph = new QueryRoutingCHGraph(chGraph, qGraph); return new CHDebugAlgo(queryRoutingCHGraph, mg); } else { @@ -353,13 +328,12 @@ private RoutingAlgorithm createAlgo(GraphHopper hopper) { return new DebugDijkstraBidirection(g, w, opts.getTraversalMode(), mg); } else if (algo instanceof Dijkstra) { return new DebugDijkstraSimple(g, w, opts.getTraversalMode(), mg); - } - return algo; + } else + return algo; }; - AlgorithmOptions algoOpts = new AlgorithmOptions().setAlgorithm(Algorithms.ASTAR_BI); - logger.info("algoOpts:" + algoOpts + ", weighting: " + landmarks.getWeighting() + ", profile: " + profile.getName()); - QueryGraph qGraph = QueryGraph.create(graph, fromRes, toRes); - return algoFactory.createAlgo(qGraph, landmarks.getWeighting(), algoOpts); + AlgorithmOptions algoOpts = new AlgorithmOptions().setAlgorithm(Algorithms.ASTAR_BI). + setTraversalMode(TraversalMode.EDGE_BASED); + return algoFactory.createAlgo(qGraph, new FastestWeighting(accessEnc, avSpeedEnc), algoOpts); } } @@ -394,19 +368,6 @@ public Color[] generateColors(int n) { return cols; } - // for debugging - private Path calcPath(RoutingAlgorithm algo) { -// int from = index.findID(50.042, 10.19); -// int to = index.findID(50.049, 10.23); -// -//// System.out.println("path " + from + "->" + to); -// return algo.calcPath(from, to); - // System.out.println(GraphUtility.getNodeInfo(graph, 60139, AccessFilter.allEdges(new CarFlagEncoder()).direction(false, true))); - // System.out.println(((GraphStorage) graph).debug(202947, 10)); -// GraphUtility.printInfo(graph, 106511, 10); - return algo.calcPath(162810, 35120); - } - void plotNodeName(Graphics2D g2, int node) { double lat = na.getLat(node); double lon = na.getLon(node); @@ -415,7 +376,7 @@ void plotNodeName(Graphics2D g2, int node) { private Path plotPath(Path tmpPath, Graphics2D g2, int w) { if (!tmpPath.isFound()) { - logger.info("nothing found " + w); + logger.info("cannot plot path as not found: " + tmpPath); return tmpPath; } @@ -505,7 +466,11 @@ public void mouseClicked(MouseEvent e) { // get from and to node id fromRes = index.findClosest(fromLat, fromLon, EdgeFilter.ALL_EDGES); toRes = index.findClosest(toLat, toLon, EdgeFilter.ALL_EDGES); - logger.info("found ids " + fromRes + " -> " + toRes + " in " + sw.stop().getSeconds() + "s"); + if (fromRes.isValid() && toRes.isValid()) { + qGraph = QueryGraph.create(graph, fromRes, toRes); + mg.setNodeAccess(qGraph); + logger.info("found ids " + fromRes + " -> " + toRes + " in " + sw.stop().getSeconds() + "s"); + } repaintPaths(); } @@ -549,22 +514,6 @@ public void mousePressed(MouseEvent e) { mainPanel.addMouseListener(ml); mainPanel.addMouseMotionListener(ml); - // just for fun -// mainPanel.getInputMap().put(KeyStroke.getKeyStroke("DELETE"), "removedNodes"); -// mainPanel.getActionMap().put("removedNodes", new AbstractAction() { -// @Override public void actionPerformed(ActionEvent e) { -// int counter = 0; -// for (CoordTrig coord : quadTreeNodes) { -// int ret = quadTree.remove(coord.lat, coord.lon); -// if (ret < 1) { -//// logger.info("cannot remove " + coord + " " + ret); -//// ret = quadTree.remove(coord.getLat(), coord.getLon()); -// } else -// counter += ret; -// } -// logger.info("Removed " + counter + " of " + quadTreeNodes.size() + " nodes"); -// } -// }); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(frameWidth + 10, frameHeight + 30); frame.setVisible(true); @@ -596,4 +545,4 @@ void repaintRoads() { mainPanel.repaint(); logger.info("roads painting took " + sw.stop().getSeconds() + " sec"); } -} +} \ No newline at end of file diff --git a/web-api/src/main/java/com/graphhopper/GHResponse.java b/web-api/src/main/java/com/graphhopper/GHResponse.java index 407b0222f87..ffd99461226 100644 --- a/web-api/src/main/java/com/graphhopper/GHResponse.java +++ b/web-api/src/main/java/com/graphhopper/GHResponse.java @@ -30,7 +30,7 @@ */ public class GHResponse { private final List errors = new ArrayList<>(4); - private final PMap hintsMap = new PMap(); + private PMap hintsMap = new PMap(); private final List responsePaths = new ArrayList<>(5); private String debugInfo = ""; @@ -140,6 +140,10 @@ public String toString() { return str; } + public void setHints(PMap hints) { + this.hintsMap = hints; + } + public PMap getHints() { return hintsMap; } diff --git a/web-api/src/main/java/com/graphhopper/ResponsePath.java b/web-api/src/main/java/com/graphhopper/ResponsePath.java index 881323b35b1..8638efc73c7 100644 --- a/web-api/src/main/java/com/graphhopper/ResponsePath.java +++ b/web-api/src/main/java/com/graphhopper/ResponsePath.java @@ -42,6 +42,7 @@ public class ResponsePath { private String debugInfo = ""; private InstructionList instructions; private PointList waypointList = PointList.EMPTY; + private List waypointIndices = new ArrayList<>(); private PointList pointList = PointList.EMPTY; private int numChanges; private final List legs = new ArrayList<>(5); @@ -119,11 +120,22 @@ public PointList getWaypoints() { /** * This method initializes this path with the snapped input points. */ - public void setWaypoints(PointList wpList) { + public ResponsePath setWaypoints(PointList wpList) { if (waypointList != PointList.EMPTY) throw new IllegalStateException("Cannot call setWaypoints twice"); this.waypointList = wpList; + return this; + } + + public List getWaypointIndices() { + check("getWaypointIndices"); + return waypointIndices; + } + + public ResponsePath setWaypointIndices(List waypointIndices) { + this.waypointIndices = waypointIndices; + return this; } /** @@ -254,35 +266,13 @@ public void addPathDetails(Map> details) { throw new IllegalStateException("Details have to be the same size"); } for (Map.Entry> detailEntry : details.entrySet()) { - if (this.pathDetails.containsKey(detailEntry.getKey())) { - List pd = this.pathDetails.get(detailEntry.getKey()); - merge(pd, detailEntry.getValue()); + String key = detailEntry.getKey(); + if (this.pathDetails.containsKey(key)) { + this.pathDetails.get(key).addAll(detailEntry.getValue()); } else { - this.pathDetails.put(detailEntry.getKey(), detailEntry.getValue()); - } - } - } - - /** - * Merges otherDetails into the pathDetails. - *

- * This method makes sure that Entry list around via points are merged correctly. - * See #1091 and the misplaced PathDetail after waypoints. - */ - public static void merge(List pathDetails, List otherDetails) { - // Make sure that the PathDetail list is merged correctly at via points - if (!pathDetails.isEmpty() && !otherDetails.isEmpty()) { - PathDetail lastDetail = pathDetails.get(pathDetails.size() - 1); - boolean extend = lastDetail.getValue() != null - ? lastDetail.getValue().equals(otherDetails.get(0).getValue()) - : otherDetails.get(0).getValue() != null; - if (extend) { - lastDetail.setLast(otherDetails.get(0).getLast()); - otherDetails.remove(0); + this.pathDetails.put(key, detailEntry.getValue()); } } - - pathDetails.addAll(otherDetails); } public Map> getPathDetails() { diff --git a/web-api/src/main/java/com/graphhopper/jackson/CustomModelAreasDeserializer.java b/web-api/src/main/java/com/graphhopper/jackson/CustomModelAreasDeserializer.java new file mode 100644 index 00000000000..c5e841f2450 --- /dev/null +++ b/web-api/src/main/java/com/graphhopper/jackson/CustomModelAreasDeserializer.java @@ -0,0 +1,47 @@ +package com.graphhopper.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.graphhopper.util.CustomModel; +import com.graphhopper.util.Helper; +import com.graphhopper.util.JsonFeature; +import com.graphhopper.util.JsonFeatureCollection; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +public class CustomModelAreasDeserializer extends JsonDeserializer { + + @Override + public JsonFeatureCollection deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + JsonNode treeNode = jp.readValueAsTree(); + JsonFeatureCollection collection = new JsonFeatureCollection(); + + if (treeNode.has("type") && "FeatureCollection".equals(treeNode.get("type").asText())) { + // Unfortunately the simpler code ala "jp.getCodec().treeToValue(treeNode, JsonFeatureCollection.class)" results in a StackErrorException + for (JsonNode node : treeNode.get("features")) { + JsonFeature feature = jp.getCodec().treeToValue(node, JsonFeature.class); + if (Helper.isEmpty(feature.getId())) + throw new IllegalArgumentException("The JsonFeature for the CustomModel area must contain \"id\""); + collection.getFeatures().add(feature); + } + } else { + Iterator> fields = treeNode.fields(); + while (fields.hasNext()) { + Map.Entry field = fields.next(); + JsonFeature feature = jp.getCodec().treeToValue(field.getValue(), JsonFeature.class); + feature.setId(field.getKey()); + collection.getFeatures().add(feature); + } + } + + // duplicate "id" check + Map index = CustomModel.getAreasAsMap(collection); + if (index.size() != collection.getFeatures().size()) // redundant but cannot hurt + throw new IllegalArgumentException("JsonFeatureCollection contains duplicate area"); + return collection; + } +} diff --git a/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java b/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java index fb8e8221abf..b6a6d3fc52d 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java +++ b/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java @@ -18,7 +18,6 @@ package com.graphhopper.jackson; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.graphhopper.GHRequest; import com.graphhopper.GHResponse; import com.graphhopper.ResponsePath; import com.graphhopper.json.Statement; diff --git a/web-api/src/main/java/com/graphhopper/jackson/InstructionListSerializer.java b/web-api/src/main/java/com/graphhopper/jackson/InstructionListSerializer.java index 4ae3353e833..df66995d043 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/InstructionListSerializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/InstructionListSerializer.java @@ -31,6 +31,8 @@ import com.graphhopper.util.Instruction; import com.graphhopper.util.InstructionList; +import static com.graphhopper.util.Parameters.Details.STREET_NAME; + public class InstructionListSerializer extends JsonSerializer { @Override public void serialize(InstructionList instructions, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { @@ -42,7 +44,7 @@ public void serialize(InstructionList instructions, JsonGenerator jsonGenerator, instrJson.put("text", Helper.firstBig(instruction.getTurnDescription(instructions.getTr()))); - instrJson.put("street_name", instruction.getName()); + instrJson.put(STREET_NAME, instruction.getName()); instrJson.put("time", instruction.getTime()); instrJson.put("distance", Helper.round(instruction.getDistance(), 3)); instrJson.put("sign", instruction.getSign()); diff --git a/web-api/src/main/java/com/graphhopper/jackson/PathDetailDeserializer.java b/web-api/src/main/java/com/graphhopper/jackson/PathDetailDeserializer.java index 7dc84ecda30..f40e345ed9d 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/PathDetailDeserializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/PathDetailDeserializer.java @@ -25,6 +25,7 @@ import com.graphhopper.util.details.PathDetail; import java.io.IOException; +import java.util.Map; public class PathDetailDeserializer extends JsonDeserializer { @@ -47,6 +48,10 @@ else if (val.canConvertToLong()) pd = new PathDetail(val.asLong()); else if (val.isTextual()) pd = new PathDetail(val.asText()); + else if (val.isObject()) + pd = new PathDetail(jp.getCodec().treeToValue(val, Map.class)); + else if (val.isNull()) + pd = new PathDetail(null); else throw new JsonParseException(jp, "Unsupported type of PathDetail value " + pathDetail.getNodeType().name()); diff --git a/web-api/src/main/java/com/graphhopper/jackson/PathDetailSerializer.java b/web-api/src/main/java/com/graphhopper/jackson/PathDetailSerializer.java index eb5b614ce98..282905005b9 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/PathDetailSerializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/PathDetailSerializer.java @@ -24,6 +24,7 @@ import com.graphhopper.util.details.PathDetail; import java.io.IOException; +import java.util.Map; public class PathDetailSerializer extends JsonSerializer { @@ -44,6 +45,8 @@ else if (value.getValue() instanceof Boolean) gen.writeBoolean((Boolean) value.getValue()); else if (value.getValue() instanceof String) gen.writeString((String) value.getValue()); + else if (value.getValue() instanceof Map) + gen.writeObject(value.getValue()); else if (value.getValue() == null) gen.writeNull(); else diff --git a/web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializer.java b/web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializer.java index 8dadf6f5c51..d8354190497 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/ResponsePathDeserializer.java @@ -83,7 +83,7 @@ public static ResponsePath createResponsePath(ObjectMapper objectMapper, JsonNod int viaCount = 1; for (JsonNode jsonObj : instrArr) { double instDist = jsonObj.get("distance").asDouble(); - String text = turnDescription ? jsonObj.get("text").asText() : jsonObj.get("street_name").asText(); + String text = turnDescription ? jsonObj.get("text").asText() : jsonObj.get(Parameters.Details.STREET_NAME).asText(); long instTime = jsonObj.get("time").asLong(); int sign = jsonObj.get("sign").asInt(); JsonNode iv = jsonObj.get("interval"); diff --git a/web-api/src/main/java/com/graphhopper/jackson/ResponsePathSerializer.java b/web-api/src/main/java/com/graphhopper/jackson/ResponsePathSerializer.java index 36337355e55..7c1021be73f 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/ResponsePathSerializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/ResponsePathSerializer.java @@ -86,7 +86,7 @@ private static void encodeNumber(StringBuilder sb, int num) { sb.append((char) (num)); } - public static ObjectNode jsonObject(GHResponse ghRsp, boolean enableInstructions, boolean calcPoints, boolean enableElevation, boolean pointsEncoded, float took) { + public static ObjectNode jsonObject(GHResponse ghRsp, boolean enableInstructions, boolean calcPoints, boolean enableElevation, boolean pointsEncoded, double took) { ObjectNode json = JsonNodeFactory.instance.objectNode(); json.putPOJO("hints", ghRsp.getHints().toMap()); final ObjectNode info = json.putObject("info"); diff --git a/web-api/src/main/java/com/graphhopper/jackson/StatementDeserializer.java b/web-api/src/main/java/com/graphhopper/jackson/StatementDeserializer.java index 33c14e6679d..5821d16d93f 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/StatementDeserializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/StatementDeserializer.java @@ -17,24 +17,22 @@ class StatementDeserializer extends JsonDeserializer { public Statement deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode treeNode = p.readValueAsTree(); Statement.Op jsonOp = null; - double value = Double.NaN; + String value = null; if (treeNode.size() != 2) throw new IllegalArgumentException("Statement expects two entries but was " + treeNode.size() + " for " + treeNode); for (Statement.Op op : Statement.Op.values()) { if (treeNode.has(op.getName())) { - if (!treeNode.get(op.getName()).isNumber()) - throw new IllegalArgumentException("Operations " + op.getName() + " expects a number but was " + treeNode.get(op.getName())); if (jsonOp != null) throw new IllegalArgumentException("Multiple operations are not allowed. Statement: " + treeNode); jsonOp = op; - value = treeNode.get(op.getName()).asDouble(); + value = treeNode.get(op.getName()).asText(); } } if (jsonOp == null) throw new IllegalArgumentException("Cannot find an operation in " + treeNode + ". Must be one of: " + Arrays.stream(Statement.Op.values()).map(Statement.Op::getName).collect(Collectors.joining(","))); - if (Double.isNaN(value)) - throw new IllegalArgumentException("Value of operation " + jsonOp.getName() + " is not a number"); + if (value == null) + throw new IllegalArgumentException("Cannot find a value in " + treeNode); if (treeNode.has(IF.getName())) return Statement.If(treeNode.get(IF.getName()).asText(), jsonOp, value); @@ -47,6 +45,6 @@ else if (treeNode.has(ELSE.getName())) { throw new IllegalArgumentException("else cannot have expression but was " + treeNode.get(ELSE.getName())); } - throw new IllegalArgumentException("Cannot find if, else_if or else for " + treeNode.toString()); + throw new IllegalArgumentException("Cannot find if, else_if or else for " + treeNode); } } diff --git a/web-api/src/main/java/com/graphhopper/jackson/StatementSerializer.java b/web-api/src/main/java/com/graphhopper/jackson/StatementSerializer.java index 1b6c5bc1680..cb1702ad704 100644 --- a/web-api/src/main/java/com/graphhopper/jackson/StatementSerializer.java +++ b/web-api/src/main/java/com/graphhopper/jackson/StatementSerializer.java @@ -29,7 +29,7 @@ class StatementSerializer extends JsonSerializer { public void serialize(Statement statement, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField(statement.getKeyword().getName(), statement.getCondition()); - jsonGenerator.writeNumberField(statement.getOperation().getName(), statement.getValue()); + jsonGenerator.writeStringField(statement.getOperation().getName(), statement.getValue()); jsonGenerator.writeEndObject(); } } diff --git a/web-api/src/main/java/com/graphhopper/json/MinMax.java b/web-api/src/main/java/com/graphhopper/json/MinMax.java new file mode 100644 index 00000000000..bcd5c7a691c --- /dev/null +++ b/web-api/src/main/java/com/graphhopper/json/MinMax.java @@ -0,0 +1,11 @@ +package com.graphhopper.json; + +public class MinMax { + public double min; + public double max; + + public MinMax(double min, double max) { + this.min = min; + this.max = max; + } +} diff --git a/web-api/src/main/java/com/graphhopper/json/Statement.java b/web-api/src/main/java/com/graphhopper/json/Statement.java index 4e780ec27ea..42e01b71b88 100644 --- a/web-api/src/main/java/com/graphhopper/json/Statement.java +++ b/web-api/src/main/java/com/graphhopper/json/Statement.java @@ -21,9 +21,9 @@ public class Statement { private final Keyword keyword; private final String condition; private final Op operation; - private final double value; + private final String value; - private Statement(Keyword keyword, String condition, Op operation, double value) { + private Statement(Keyword keyword, String condition, Op operation, String value) { this.keyword = keyword; this.condition = condition; this.value = value; @@ -42,21 +42,10 @@ public Op getOperation() { return operation; } - public double getValue() { + public String getValue() { return value; } - public double apply(double externValue) { - switch (operation) { - case MULTIPLY: - return value * externValue; - case LIMIT: - return Math.min(value, externValue); - default: - throw new IllegalArgumentException(); - } - } - public enum Keyword { IF("if"), ELSEIF("else_if"), ELSE("else"); @@ -84,7 +73,7 @@ public String getName() { return name; } - public String build(double value) { + public String build(String value) { switch (this) { case MULTIPLY: return "value *= " + value; @@ -94,6 +83,17 @@ public String build(double value) { throw new IllegalArgumentException(); } } + + public MinMax apply(MinMax minMax1, MinMax minMax2) { + switch (this) { + case MULTIPLY: + return new MinMax(minMax1.min * minMax2.min, minMax1.max * minMax2.max); + case LIMIT: + return new MinMax(Math.min(minMax1.min, minMax2.min), Math.min(minMax1.max, minMax2.max)); + default: + throw new IllegalArgumentException(); + } + } } @Override @@ -105,15 +105,15 @@ private String str(String str) { return "\"" + str + "\""; } - public static Statement If(String expression, Op op, double value) { + public static Statement If(String expression, Op op, String value) { return new Statement(Keyword.IF, expression, op, value); } - public static Statement ElseIf(String expression, Op op, double value) { + public static Statement ElseIf(String expression, Op op, String value) { return new Statement(Keyword.ELSEIF, expression, op, value); } - public static Statement Else(Op op, double value) { + public static Statement Else(Op op, String value) { return new Statement(Keyword.ELSE, null, op, value); } } diff --git a/web-api/src/main/java/com/graphhopper/util/CustomModel.java b/web-api/src/main/java/com/graphhopper/util/CustomModel.java index ab1a2dd8f79..6d49d157a8c 100644 --- a/web-api/src/main/java/com/graphhopper/util/CustomModel.java +++ b/web-api/src/main/java/com/graphhopper/util/CustomModel.java @@ -17,12 +17,12 @@ */ package com.graphhopper.util; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.graphhopper.jackson.CustomModelAreasDeserializer; import com.graphhopper.json.Statement; import java.util.*; - -import static com.graphhopper.json.Statement.Keyword.ELSE; -import static com.graphhopper.json.Statement.Keyword.IF; +import java.util.stream.Collectors; /** * This class is used in combination with CustomProfile. @@ -31,14 +31,13 @@ public class CustomModel { public static final String KEY = "custom_model"; - // e.g. 70 means that the time costs are 25€/hour and for the distance 0.5€/km (for trucks this is usually larger) - static double DEFAULT_DISTANCE_INFLUENCE = 70; + // 'Double' instead of 'double' is required to know if it was 0 or not specified in the request. private Double distanceInfluence; private double headingPenalty = Parameters.Routing.DEFAULT_HEADING_PENALTY; private boolean internal; private List speedStatements = new ArrayList<>(); private List priorityStatements = new ArrayList<>(); - private Map areas = new HashMap<>(); + private JsonFeatureCollection areas = new JsonFeatureCollection(); public CustomModel() { } @@ -46,12 +45,33 @@ public CustomModel() { public CustomModel(CustomModel toCopy) { this.headingPenalty = toCopy.headingPenalty; this.distanceInfluence = toCopy.distanceInfluence; - // do not copy "internal" + // do not copy "internal" boolean speedStatements = deepCopy(toCopy.getSpeed()); priorityStatements = deepCopy(toCopy.getPriority()); - areas.putAll(toCopy.getAreas()); + addAreas(toCopy.getAreas()); + } + + public static Map getAreasAsMap(JsonFeatureCollection areas) { + Map map = new HashMap<>(areas.getFeatures().size()); + areas.getFeatures().forEach(f -> { + if (map.put(f.getId(), f) != null) + throw new IllegalArgumentException("Cannot handle duplicate area " + f.getId()); + }); + return map; + } + + public void addAreas(JsonFeatureCollection externalAreas) { + Set indexed = areas.getFeatures().stream().map(JsonFeature::getId).collect(Collectors.toSet()); + for (JsonFeature ext : externalAreas.getFeatures()) { + if (!JsonFeature.isValidId("in_" + ext.getId())) + throw new IllegalArgumentException("The area '" + ext.getId() + "' has an invalid id. Only letters, numbers and underscore are allowed."); + if (indexed.contains(ext.getId())) + throw new IllegalArgumentException("area " + ext.getId() + " already exists"); + areas.getFeatures().add(ext); + indexed.add(ext.getId()); + } } /** @@ -106,22 +126,23 @@ public CustomModel addToPriority(Statement st) { return this; } - public CustomModel setAreas(Map areas) { + @JsonDeserialize(using = CustomModelAreasDeserializer.class) + public CustomModel setAreas(JsonFeatureCollection areas) { this.areas = areas; return this; } - public Map getAreas() { + public JsonFeatureCollection getAreas() { return areas; } - public CustomModel setDistanceInfluence(double distanceFactor) { + public CustomModel setDistanceInfluence(Double distanceFactor) { this.distanceInfluence = distanceFactor; return this; } - public double getDistanceInfluence() { - return distanceInfluence == null ? DEFAULT_DISTANCE_INFLUENCE : distanceInfluence; + public Double getDistanceInfluence() { + return distanceInfluence; } public CustomModel setHeadingPenalty(double headingPenalty) { @@ -144,81 +165,6 @@ private String createContentString() { + "|speedStatements=" + speedStatements + "|priorityStatements=" + priorityStatements + "|areas=" + areas; } - /** - * This method throws an exception when this CustomModel would decrease the edge weight compared to the specified - * baseModel as in such a case the optimality of A* with landmarks can no longer be guaranteed (as the preparation - * is based on baseModel). - */ - public void checkLMConstraints(CustomModel baseModel) { - if (isInternal()) - throw new IllegalArgumentException("CustomModel of query cannot be internal"); - if (distanceInfluence != null && distanceInfluence < baseModel.getDistanceInfluence()) - throw new IllegalArgumentException("CustomModel in query can only use " + - "distance_influence bigger or equal to " + baseModel.getDistanceInfluence() + - ", given: " + distanceInfluence); - - checkMultiplyValue(getPriority()); - double maxPrio = findMaxPriority(1); - if (maxPrio > 1) - throw new IllegalArgumentException("priority of CustomModel in query cannot be bigger than 1. Was: " + maxPrio); - - checkMultiplyValue(getSpeed()); - } - - private static void checkMultiplyValue(List list) { - for (Statement statement : list) { - if (statement.getOperation() == Statement.Op.MULTIPLY && statement.getValue() > 1) - throw new IllegalArgumentException("factor cannot be larger than 1 but was " + statement.getValue()); - } - } - - static double findMax(List statements, double max, String type) { - // we want to find the smallest value that cannot be exceeded by any edge. the 'blocks' of speed statements - // are applied one after the other. - List> blocks = splitIntoBlocks(statements); - for (List block : blocks) max = findMaxForBlock(block, max); - if (max <= 0) throw new IllegalArgumentException(type + " cannot be negative or 0 (was " + max + ")"); - return max; - } - - public double findMaxPriority(final double maxPriority) { - return findMax(getPriority(), maxPriority, "priority"); - } - - public double findMaxSpeed(final double maxSpeed) { - return findMax(getSpeed(), maxSpeed, "vehicle speed"); - } - - static double findMaxForBlock(List block, final double max) { - if (block.isEmpty() || !IF.equals(block.get(0).getKeyword())) - throw new IllegalArgumentException("Every block must start with an if-statement"); - if (block.get(0).getCondition().trim().equals("true")) - return block.get(0).apply(max); - - double blockMax = block.stream() - .mapToDouble(statement -> statement.apply(max)) - .max() - .orElse(max); - // if there is no 'else' statement it's like there is a 'neutral' branch that leaves the initial value as is - if (block.stream().noneMatch(st -> ELSE.equals(st.getKeyword()))) - blockMax = Math.max(blockMax, max); - return blockMax; - } - - /** - * Splits the specified list into several list of statements starting with if - */ - static List> splitIntoBlocks(List statements) { - List> result = new ArrayList<>(); - List block = null; - for (Statement st : statements) { - if (IF.equals(st.getKeyword())) result.add(block = new ArrayList<>()); - if (block == null) throw new IllegalArgumentException("Every block must start with an if-statement"); - block.add(st); - } - return result; - } - /** * A new CustomModel is created from the baseModel merged with the specified queryModel. Returns the baseModel if * queryModel is null. @@ -228,18 +174,12 @@ public static CustomModel merge(CustomModel baseModel, CustomModel queryModel) { // avoid changing the specified CustomModel via deep copy otherwise the server-side CustomModel would be // modified (same problem if queryModel would be used as target) CustomModel mergedCM = new CustomModel(baseModel); - // we only overwrite the distance influence if a non-default value was used - if (queryModel.distanceInfluence != null) - mergedCM.distanceInfluence = queryModel.distanceInfluence; + if (queryModel.getDistanceInfluence() != null) + mergedCM.distanceInfluence = queryModel.distanceInfluence; mergedCM.speedStatements.addAll(queryModel.getSpeed()); mergedCM.priorityStatements.addAll(queryModel.getPriority()); - - for (Map.Entry entry : queryModel.getAreas().entrySet()) { - if (mergedCM.areas.containsKey(entry.getKey())) - throw new IllegalArgumentException("area " + entry.getKey() + " already exists"); - mergedCM.areas.put(entry.getKey(), entry.getValue()); - } + mergedCM.addAreas(queryModel.getAreas()); return mergedCM; } diff --git a/web-api/src/main/java/com/graphhopper/util/Helper.java b/web-api/src/main/java/com/graphhopper/util/Helper.java index e03ba746f9f..f13ecdf8c25 100644 --- a/web-api/src/main/java/com/graphhopper/util/Helper.java +++ b/web-api/src/main/java/com/graphhopper/util/Helper.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; @@ -31,7 +32,7 @@ * @author Peter Karich */ public class Helper { - public static final Charset UTF_CS = Charset.forName("UTF-8"); + public static final Charset UTF_CS = StandardCharsets.UTF_8; public static final TimeZone UTC = TimeZone.getTimeZone("UTC"); public static final long MB = 1L << 20; // we keep the first seven decimal places of lat/lon coordinates. this corresponds to ~1cm precision ('pointing to waldo on a page') @@ -89,6 +90,18 @@ public static void saveProperties(Map map, Writer tmpWriter) thr } } + public static String readJSONFileWithoutComments(String file) throws IOException { + return Helper.readFile(file).stream(). + filter(line -> !line.trim().startsWith("//")). + reduce((s1, s2) -> s1 + "\n" + s2).orElse(""); + } + + public static String readJSONFileWithoutComments(InputStreamReader reader) throws IOException { + return Helper.readFile(reader).stream(). + filter(line -> !line.trim().startsWith("//")). + reduce((s1, s2) -> s1 + "\n" + s2).orElse(""); + } + public static List readFile(String file) throws IOException { return readFile(new InputStreamReader(new FileInputStream(file), UTF_CS)); } @@ -356,7 +369,7 @@ public static DateFormat createFormatter(String str) { * and returns the positive converted long. */ public static long toUnsignedLong(int x) { - return ((long) x) & 0xFFFFffffL; + return ((long) x) & 0xFFFF_FFFFL; } /** diff --git a/web-api/src/main/java/com/graphhopper/util/Instruction.java b/web-api/src/main/java/com/graphhopper/util/Instruction.java index 27e78f4ef2a..1e3ad721bcf 100644 --- a/web-api/src/main/java/com/graphhopper/util/Instruction.java +++ b/web-api/src/main/java/com/graphhopper/util/Instruction.java @@ -20,6 +20,8 @@ import java.util.HashMap; import java.util.Map; +import static com.graphhopper.util.Parameters.Details.*; + public class Instruction { public static final int UNKNOWN = -99; public static final int U_TURN_UNKNOWN = -98; @@ -45,7 +47,7 @@ public class Instruction { protected PointList points; protected boolean rawName; protected int sign; - protected String name; + protected String name = ""; protected double distance; protected long time; protected Map extraInfo = new HashMap<>(3); @@ -56,7 +58,7 @@ public class Instruction { */ public Instruction(int sign, String name, PointList pl) { this.sign = sign; - this.name = name; + if (name != null) this.name = name; this.points = pl; } @@ -84,7 +86,11 @@ public String getName() { } public void setName(String name) { - this.name = name; + if (name != null) this.name = name; + } + + String _getName() { + return getName().isEmpty() && extraInfo.get(STREET_REF) instanceof String ? (String) extraInfo.get(STREET_REF) : getName(); } public Map getExtraInfoJSON() { @@ -92,7 +98,8 @@ public Map getExtraInfoJSON() { } public void setExtraInfo(String key, Object value) { - extraInfo.put(key, value); + if (value != null && key != null) + extraInfo.put(key, value); } /** @@ -161,7 +168,7 @@ public String getTurnDescription(Translation tr) { return getName(); String str; - String streetName = getName(); + String streetName = _getName(); int indi = getSign(); if (indi == Instruction.CONTINUE_ON_STREET) { str = Helper.isEmpty(streetName) ? tr.tr("continue") : tr.tr("continue_onto", streetName); @@ -211,8 +218,16 @@ public String getTurnDescription(Translation tr) { if (dir == null) str = tr.tr("unknown", indi); else - str = Helper.isEmpty(streetName) ? dir : tr.tr("turn_onto", dir, streetName); + str = streetName.isEmpty() ? dir : tr.tr("turn_onto", dir, streetName); } + String dest = (String) extraInfo.get(STREET_DESTINATION); + String destRef = (String) extraInfo.get(STREET_DESTINATION_REF); + if (dest != null) { + if (destRef != null) + return tr.tr("toward_destination_with_ref", str, destRef, dest); + return tr.tr("toward_destination", str, dest); + } else if (destRef != null) + return tr.tr("toward_destination_ref_only", str, destRef); return str; } } diff --git a/web-api/src/main/java/com/graphhopper/util/JsonFeature.java b/web-api/src/main/java/com/graphhopper/util/JsonFeature.java index e747ff32d34..9615827656d 100644 --- a/web-api/src/main/java/com/graphhopper/util/JsonFeature.java +++ b/web-api/src/main/java/com/graphhopper/util/JsonFeature.java @@ -20,8 +20,12 @@ import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; +import javax.lang.model.SourceVersion; import java.util.Map; +import static java.lang.Character.isDigit; +import static java.lang.Character.isLetter; + /** * This class defines a properties where a geometry is associated. Typically read from GeoJSON but also from in-memory is possible. * @@ -89,4 +93,22 @@ public void setProperties(Map properties) { public String toString() { return "id:" + getId(); } + + public static boolean isValidId(String name) { + if (name.length() <= 3 || !name.startsWith("in_") || SourceVersion.isKeyword(name)) return false; + + int underscoreCount = 0; + for (int i = 1; i < name.length(); i++) { + char c = name.charAt(i); + if (c == '_') { + if (underscoreCount > 0) return false; + underscoreCount++; + } else if (!isLetter(c) && !isDigit(c)) { + return false; + } else { + underscoreCount = 0; + } + } + return true; + } } diff --git a/web-api/src/main/java/com/graphhopper/util/JsonFeatureCollection.java b/web-api/src/main/java/com/graphhopper/util/JsonFeatureCollection.java index f0957d751dd..b54e1fa6cae 100644 --- a/web-api/src/main/java/com/graphhopper/util/JsonFeatureCollection.java +++ b/web-api/src/main/java/com/graphhopper/util/JsonFeatureCollection.java @@ -34,4 +34,9 @@ public String getType() { public List getFeatures() { return features; } + + @Override + public String toString() { + return features.toString(); + } } diff --git a/web-api/src/main/java/com/graphhopper/util/PMap.java b/web-api/src/main/java/com/graphhopper/util/PMap.java index 639d67371cc..83ef579eaf0 100644 --- a/web-api/src/main/java/com/graphhopper/util/PMap.java +++ b/web-api/src/main/java/com/graphhopper/util/PMap.java @@ -46,8 +46,7 @@ public PMap(PMap map) { } public PMap(String propertiesString) { - // five chosen as arbitrary initial capacity - this.map = new LinkedHashMap<>(5); + this.map = new LinkedHashMap<>(); for (String s : propertiesString.split("\\|")) { s = s.trim(); @@ -104,9 +103,8 @@ public PMap put(String key, String str) { return this; } - public PMap remove(String key) { - map.remove(key); - return this; + public Object remove(String key) { + return map.remove(key); } public boolean has(String key) { diff --git a/web-api/src/main/java/com/graphhopper/util/Parameters.java b/web-api/src/main/java/com/graphhopper/util/Parameters.java index 9d675d16b14..6fdf686706d 100644 --- a/web-api/src/main/java/com/graphhopper/util/Parameters.java +++ b/web-api/src/main/java/com/graphhopper/util/Parameters.java @@ -131,11 +131,6 @@ public static final class Routing { */ public static final double DEFAULT_HEADING_PENALTY = 300; public static final String HEADING_PENALTY = "heading_penalty"; - /** - * block road access via a point in the format lat,lon or an area defined by a circle lat,lon,radius or - * a rectangle lat1,lon1,lat2,lon2 - */ - public static final String BLOCK_AREA = "block_area"; } /** @@ -201,13 +196,23 @@ public static final class Details { public static final String PATH_DETAILS = "details"; - public static final String AVERAGE_SPEED = "average_speed"; + // these details are directly accessing the KVStorage for edges and the names have to be identical public static final String STREET_NAME = "street_name"; + public static final String STREET_REF = "street_ref"; + public static final String STREET_DESTINATION = "street_destination"; + public static final String STREET_DESTINATION_REF = "street_destination_ref"; + + public static final String AVERAGE_SPEED = "average_speed"; public static final String EDGE_ID = "edge_id"; public static final String EDGE_KEY = "edge_key"; public static final String TIME = "time"; public static final String WEIGHT = "weight"; public static final String DISTANCE = "distance"; + public static final String INTERSECTION = "intersection"; + + public static final String LEG_TIME = "leg_time"; + public static final String LEG_DISTANCE = "leg_distance"; + public static final String LEG_WEIGHT = "leg_weight"; } } diff --git a/web-api/src/main/java/com/graphhopper/util/RoundaboutInstruction.java b/web-api/src/main/java/com/graphhopper/util/RoundaboutInstruction.java index 493836d7ba8..486e40cec73 100644 --- a/web-api/src/main/java/com/graphhopper/util/RoundaboutInstruction.java +++ b/web-api/src/main/java/com/graphhopper/util/RoundaboutInstruction.java @@ -114,7 +114,7 @@ public String getTurnDescription(Translation tr) { return getName(); String str; - String streetName = getName(); + String streetName = _getName(); int indi = getSign(); if (indi == Instruction.USE_ROUNDABOUT) { if (!exited) { diff --git a/web-api/src/main/java/com/graphhopper/util/ViaInstruction.java b/web-api/src/main/java/com/graphhopper/util/ViaInstruction.java index d521876703b..d6adc440aee 100644 --- a/web-api/src/main/java/com/graphhopper/util/ViaInstruction.java +++ b/web-api/src/main/java/com/graphhopper/util/ViaInstruction.java @@ -55,6 +55,6 @@ public String getTurnDescription(Translation tr) { if (rawName) return getName(); - return tr.tr("stopover", viaPosition); + return tr.tr("stopover", getViaCount()); } } diff --git a/web-api/src/test/java/com/graphhopper/jackson/JsonFeatureCollectionTest.java b/web-api/src/test/java/com/graphhopper/jackson/JsonFeatureCollectionTest.java index 37252c43b20..d745c3b1c4c 100644 --- a/web-api/src/test/java/com/graphhopper/jackson/JsonFeatureCollectionTest.java +++ b/web-api/src/test/java/com/graphhopper/jackson/JsonFeatureCollectionTest.java @@ -28,11 +28,12 @@ import org.locationtech.jts.geom.LineString; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; /** * @author Peter Karich @@ -115,4 +116,14 @@ public void testDeserialization() throws IOException { assertEquals("a", ((Map) f3.getProperty("prop1")).get("test")); } + @Test + public void validAreaID() { + for (String str : Arrays.asList("in_bla", "in_BLA")) { + assertTrue(JsonFeature.isValidId(str), str); + } + + for (String str : Arrays.asList("in_", "test")) { + assertFalse(JsonFeature.isValidId(str), str); + } + } } diff --git a/web-api/src/test/java/com/graphhopper/jackson/PathDetailDeserializerTest.java b/web-api/src/test/java/com/graphhopper/jackson/PathDetailDeserializerTest.java new file mode 100644 index 00000000000..7cf953db2b1 --- /dev/null +++ b/web-api/src/test/java/com/graphhopper/jackson/PathDetailDeserializerTest.java @@ -0,0 +1,20 @@ +package com.graphhopper.jackson; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.graphhopper.GHResponse; + +class PathDetailDeserializerTest { + + @Test + public void else_null_detail_average_speed() throws JsonProcessingException { + final ObjectMapper objectMapper = Jackson.newObjectMapper(); + final String result = "{\"hints\":{\"visited_nodes.sum\":90,\"visited_nodes.average\":90},\"info\":{\"copyrights\":[\"GraphHopper\",\"OpenStreetMap contributors\"],\"took\":1},\"paths\":[{\"distance\":9863.287,\"weight\":888.630785,\"time\":740658,\"transfers\":0,\"points_encoded\":false,\"bbox\":[-58.476658,-34.650384,-58.429018,-34.628717],\"points\":{\"type\":\"LineString\",\"coordinates\":[[-58.465985,-34.650384],[-58.465985,-34.650384],[-58.46555,-34.649904],[-58.465416,-34.649686],[-58.464935,-34.649033],[-58.464199,-34.648446],[-58.464014,-34.648262],[-58.463675,-34.647783],[-58.462575,-34.645549],[-58.462426,-34.645311],[-58.462031,-34.644079],[-58.461681,-34.643299],[-58.461561,-34.643099],[-58.461258,-34.642675],[-58.460623,-34.642013],[-58.460372,-34.641799],[-58.458082,-34.640102],[-58.456072,-34.638467],[-58.45555,-34.638104],[-58.453825,-34.637045],[-58.453518,-34.636894],[-58.453287,-34.636799],[-58.452854,-34.636647],[-58.452399,-34.636541],[-58.451783,-34.636477],[-58.451437,-34.636461],[-58.45096,-34.636455],[-58.449346,-34.636492],[-58.448718,-34.636475],[-58.448065,-34.636381],[-58.447576,-34.636252],[-58.447071,-34.636088],[-58.446624,-34.635893],[-58.445336,-34.635105],[-58.443767,-34.633999],[-58.441687,-34.632678],[-58.440299,-34.631866],[-58.439234,-34.631214],[-58.43877,-34.630985],[-58.437805,-34.630657],[-58.436974,-34.630468],[-58.434133,-34.630075],[-58.432415,-34.629923],[-58.430902,-34.629722],[-58.430437,-34.62966],[-58.430367,-34.629977],[-58.429018,-34.629782],[-58.429325,-34.628717],[-58.430635,-34.628917],[-58.430561,-34.62922],[-58.432879,-34.629618],[-58.434549,-34.629933],[-58.436946,-34.630326],[-58.437878,-34.630532],[-58.438762,-34.630822],[-58.439319,-34.63109],[-58.440885,-34.632045],[-58.442483,-34.632958],[-58.443484,-34.633603],[-58.445463,-34.634973],[-58.446519,-34.635643],[-58.446771,-34.635791],[-58.447069,-34.635926],[-58.447557,-34.636096],[-58.448078,-34.636254],[-58.44873,-34.636348],[-58.451367,-34.636297],[-58.452221,-34.636358],[-58.452712,-34.636448],[-58.453267,-34.636623],[-58.453552,-34.63673],[-58.453854,-34.636872],[-58.454214,-34.637075],[-58.455065,-34.637593],[-58.456306,-34.638427],[-58.457424,-34.639367],[-58.45823,-34.640018],[-58.460244,-34.641438],[-58.460783,-34.641844],[-58.461038,-34.642077],[-58.461295,-34.642353],[-58.461474,-34.64258],[-58.461901,-34.643196],[-58.462073,-34.643528],[-58.462269,-34.644017],[-58.462687,-34.645211],[-58.463101,-34.645982],[-58.463506,-34.646562],[-58.463782,-34.646898],[-58.464636,-34.647832],[-58.465174,-34.648395],[-58.4654,-34.648587],[-58.465807,-34.648882],[-58.466103,-34.649053],[-58.466435,-34.6492],[-58.466725,-34.649293],[-58.467056,-34.649351],[-58.467634,-34.649388],[-58.469992,-34.64935],[-58.47232,-34.64918],[-58.473264,-34.649091],[-58.474569,-34.648922],[-58.475036,-34.648843],[-58.475346,-34.648771],[-58.475799,-34.648596],[-58.476658,-34.648084]]},\"instructions\":[{\"distance\":3923.699,\"heading\":90,\"sign\":0,\"interval\":[0,41],\"text\":\"Continue na Autopista Teniente General Luis Dellepiane\",\"time\":228978,\"street_name\":\"Autopista Teniente General Luis Dellepiane\"},{\"distance\":341.449,\"sign\":7,\"interval\":[41,44],\"text\":\"Mantenha-se à direita\",\"time\":52395,\"street_name\":\"\"},{\"distance\":35.813,\"sign\":2,\"interval\":[44,45],\"text\":\"Vire à direita na Viel\",\"time\":7162,\"street_name\":\"Viel\"},{\"distance\":125.343,\"sign\":-2,\"interval\":[45,46],\"text\":\"Vire à esquerda na Tejedor\",\"time\":25068,\"street_name\":\"Tejedor\"},{\"distance\":121.839,\"sign\":-2,\"interval\":[46,47],\"text\":\"Vire à esquerda na Doblas\",\"time\":31329,\"street_name\":\"Doblas\"},{\"distance\":121.976,\"sign\":-2,\"interval\":[47,48],\"text\":\"Vire à esquerda na Zuviría\",\"time\":24395,\"street_name\":\"Zuviría\"},{\"distance\":34.367,\"sign\":-2,\"interval\":[48,49],\"text\":\"Vire à esquerda na Viel\",\"time\":6873,\"street_name\":\"Viel\"},{\"distance\":3623.263,\"sign\":2,\"interval\":[49,85],\"text\":\"Vire à direita\",\"time\":234503,\"street_name\":\"\"},{\"distance\":1535.538,\"sign\":7,\"interval\":[85,105],\"text\":\"Mantenha-se à direita\",\"time\":129955,\"street_name\":\"\"},{\"distance\":0,\"sign\":4,\"last_heading\":305.9709217557622,\"interval\":[105,105],\"text\":\"Destino alcançado!\",\"time\":0,\"street_name\":\"\"}],\"legs\":[],\"details\":{\"average_speed\":[[0,1,null],[1,2,16],[2,7,78],[7,41,64],[41,43,26],[43,44,14],[44,46,18],[46,47,14],[47,49,18],[49,51,26],[51,85,64],[85,96,40],[96,103,48],[103,105,32]]},\"ascend\":40.208499908447266,\"descend\":36.19799995422363,\"snapped_waypoints\":{\"type\":\"LineString\",\"coordinates\":[[-58.465985,-34.650384],[-58.476658,-34.648084]]}}]}"; + final GHResponse ghResponse = objectMapper.readValue(result, GHResponse.class); + assertNotNull(ghResponse); + } +} diff --git a/web-api/src/test/java/com/graphhopper/jackson/StatementDeserializerTest.java b/web-api/src/test/java/com/graphhopper/jackson/StatementDeserializerTest.java new file mode 100644 index 00000000000..ef930cca70a --- /dev/null +++ b/web-api/src/test/java/com/graphhopper/jackson/StatementDeserializerTest.java @@ -0,0 +1,60 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jackson; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.graphhopper.json.Statement; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class StatementDeserializerTest { + + @Test + void conditionAsBoolean_expressionAsNumber() throws JsonProcessingException { + SimpleModule module = new SimpleModule(); + module.addDeserializer(Statement.class, new StatementDeserializer()); + ObjectMapper objectMapper = new ObjectMapper().registerModule(module); + // true instead of "true" or 100 instead of "100" also work because they are parsed to strings + // We probably need to accept numbers instead of strings for legacy support, but maybe we should reject true/false + Statement statement = objectMapper.readValue("{\"if\":true,\"limit_to\":100}", Statement.class); + assertEquals(Statement.Keyword.IF, statement.getKeyword()); + assertEquals("true", statement.getCondition()); + assertEquals(Statement.Op.LIMIT, statement.getOperation()); + assertEquals("100", statement.getValue()); + } + + @Test + void else_null() throws JsonProcessingException { + SimpleModule module = new SimpleModule(); + module.addDeserializer(Statement.class, new StatementDeserializer()); + ObjectMapper objectMapper = new ObjectMapper().registerModule(module); + // There is no error for `"else": null` currently, even though there is no real reason to support this. + // The value will actually be null, but the way we use it at the moment this is not a problem. + Statement statement = objectMapper.readValue("{\"else\":null,\"limit_to\":\"abc\"}", Statement.class); + assertEquals(Statement.Keyword.ELSE, statement.getKeyword()); + assertNull(statement.getCondition()); + assertEquals(Statement.Op.LIMIT, statement.getOperation()); + assertEquals("abc", statement.getValue()); + } + +} \ No newline at end of file diff --git a/web-api/src/test/java/com/graphhopper/util/CustomModelTest.java b/web-api/src/test/java/com/graphhopper/util/CustomModelTest.java index 8e12c970be2..89269202821 100644 --- a/web-api/src/test/java/com/graphhopper/util/CustomModelTest.java +++ b/web-api/src/test/java/com/graphhopper/util/CustomModelTest.java @@ -21,105 +21,23 @@ import com.graphhopper.json.Statement; import org.junit.jupiter.api.Test; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; -import java.util.List; -import static com.graphhopper.json.Statement.*; -import static com.graphhopper.json.Statement.Op.LIMIT; +import static com.graphhopper.json.Statement.ElseIf; +import static com.graphhopper.json.Statement.If; import static com.graphhopper.json.Statement.Op.MULTIPLY; -import static com.graphhopper.util.CustomModel.findMax; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; public class CustomModelTest { - @Test - public void testCheck() { - CustomModel queryModel = new CustomModel(); - queryModel.addToPriority(If("max_width < 3", MULTIPLY, 10)); - assertEquals(1, CustomModel.merge(queryModel, new CustomModel()).getPriority().size()); - // priority bigger than 1 is not ok for CustomModel of query - assertThrows(IllegalArgumentException.class, () -> queryModel.checkLMConstraints(new CustomModel())); - } - - @Test - public void testFindMax() { - List statements = new ArrayList<>(); - statements.add(If("true", LIMIT, 100)); - assertEquals(100, findMax(statements, 120, "speed")); - - statements.add(Else(LIMIT, 20)); - assertEquals(100, findMax(statements, 120, "speed")); - - statements = new ArrayList<>(); - statements.add(If("road_environment == BRIDGE", LIMIT, 85)); - statements.add(Else(LIMIT, 100)); - assertEquals(100, findMax(statements, 120, "speed")); - - // find bigger speed than stored max_speed in server-side custom_models - double storedMaxSpeed = 30; - statements = new ArrayList<>(); - statements.add(If("true", MULTIPLY, 2)); - statements.add(If("true", LIMIT, 35)); - assertEquals(35, findMax(statements, 30, "speed")); - } - - @Test - public void findMax_limitAndMultiply() { - List statements = Arrays.asList( - If("road_class == TERTIARY", LIMIT, 90), - ElseIf("road_class == SECONDARY", MULTIPLY, 1.0), - ElseIf("road_class == PRIMARY", LIMIT, 30), - Else(LIMIT, 3) - ); - assertEquals(140, findMax(statements, 140, "speed")); - } - - @Test - public void testFindMaxPriority() { - List statements = new ArrayList<>(); - statements.add(If("true", MULTIPLY, 2)); - assertEquals(2, findMax(statements, 1, "priority")); - - statements = new ArrayList<>(); - statements.add(If("true", MULTIPLY, 0.5)); - assertEquals(0.5, findMax(statements, 1, "priority")); - } - - @Test - public void findMax_multipleBlocks() { - List statements = Arrays.asList( - If("road_class == TERTIARY", MULTIPLY, 0.2), - ElseIf("road_class == SECONDARY", LIMIT, 25), - If("road_environment == TUNNEL", LIMIT, 60), - ElseIf("road_environment == BRIDGE", LIMIT, 50), - Else(MULTIPLY, 0.8) - ); - assertEquals(120, findMax(statements, 150, "speed")); - assertEquals(80, findMax(statements, 100, "speed")); - assertEquals(60, findMax(statements, 60, "speed")); - - statements = Arrays.asList( - If("road_class == TERTIARY", MULTIPLY, 0.2), - ElseIf("road_class == SECONDARY", LIMIT, 25), - Else(LIMIT, 40), - If("road_environment == TUNNEL", MULTIPLY, 0.8), - ElseIf("road_environment == BRIDGE", LIMIT, 30) - ); - assertEquals(40, findMax(statements, 150, "speed")); - assertEquals(40, findMax(statements, 40, "speed")); - } - @Test public void testMergeComparisonKeys() { CustomModel truck = new CustomModel(); - truck.addToPriority(If("max_width < 3", MULTIPLY, 0)); + truck.addToPriority(If("max_width < 3", MULTIPLY, "0")); CustomModel car = new CustomModel(); - car.addToPriority(If("max_width<2", MULTIPLY, 0)); + car.addToPriority(If("max_width<2", MULTIPLY, "0")); CustomModel bike = new CustomModel(); - bike.addToPriority(If("max_weight<0.02", MULTIPLY, 0)); + bike.addToPriority(If("max_weight<0.02", MULTIPLY, "0")); assertEquals(2, CustomModel.merge(bike, car).getPriority().size()); assertEquals(1, bike.getPriority().size()); @@ -129,10 +47,10 @@ public void testMergeComparisonKeys() { @Test public void testMergeElse() { CustomModel truck = new CustomModel(); - truck.addToPriority(If("max_width < 3", MULTIPLY, 0)); + truck.addToPriority(If("max_width < 3", MULTIPLY, "0")); CustomModel car = new CustomModel(); - car.addToPriority(If("max_width < 2", MULTIPLY, 0)); + car.addToPriority(If("max_width < 2", MULTIPLY, "0")); CustomModel merged = CustomModel.merge(truck, car); assertEquals(2, merged.getPriority().size()); @@ -143,15 +61,15 @@ public void testMergeElse() { public void testMergeEmptyModel() { CustomModel emptyCar = new CustomModel(); CustomModel car = new CustomModel(); - car.addToPriority(If("road_class==primary", MULTIPLY, 0.5)); - car.addToPriority(ElseIf("road_class==tertiary", MULTIPLY, 0.8)); + car.addToPriority(If("road_class==primary", MULTIPLY, "0.5")); + car.addToPriority(ElseIf("road_class==tertiary", MULTIPLY, "0.8")); Iterator iter = CustomModel.merge(emptyCar, car).getPriority().iterator(); - assertEquals(0.5, iter.next().getValue()); - assertEquals(0.8, iter.next().getValue()); + assertEquals("0.5", iter.next().getValue()); + assertEquals("0.8", iter.next().getValue()); iter = CustomModel.merge(car, emptyCar).getPriority().iterator(); - assertEquals(0.5, iter.next().getValue()); - assertEquals(0.8, iter.next().getValue()); + assertEquals("0.5", iter.next().getValue()); + assertEquals("0.8", iter.next().getValue()); } } \ No newline at end of file diff --git a/web-api/src/test/java/com/graphhopper/util/HelperTest.java b/web-api/src/test/java/com/graphhopper/util/HelperTest.java index e3ce76bac9b..49f56c84d27 100644 --- a/web-api/src/test/java/com/graphhopper/util/HelperTest.java +++ b/web-api/src/test/java/com/graphhopper/util/HelperTest.java @@ -19,10 +19,12 @@ import org.junit.jupiter.api.Test; +import java.util.Arrays; import java.util.Locale; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static com.graphhopper.util.Helper.UTF_CS; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author Peter Karich @@ -104,4 +106,23 @@ public void testUnderscoreToCamelCase() { assertEquals("testCaseTBD", Helper.underScoreToCamelCase("test_case_t_b_d")); assertEquals("TestCase_", Helper.underScoreToCamelCase("_test_case_")); } + + @Test + public void testIssue2609() { + String s = ""; + for (int i = 0; i < 128; i++) { + s += "ä"; + } + + // all chars are 2 bytes so at 255 we cut the char into an invalid character and this is probably automatically + // corrected leading to a longer string (or do chars have special marker bits to indicate their byte length?) + assertEquals(257, new String(s.getBytes(UTF_CS), 0, 255, UTF_CS).getBytes(UTF_CS).length); + + // see this in action: + byte[] bytes = "a".getBytes(UTF_CS); + assertEquals(1, new String(bytes, 0, 1, UTF_CS).getBytes(UTF_CS).length); + // force incorrect char: + bytes[0] = -25; + assertEquals(3, new String(bytes, 0, 1, UTF_CS).getBytes(UTF_CS).length); + } } diff --git a/web-bundle/.jshintignore b/web-bundle/.jshintignore deleted file mode 100644 index 257834843d2..00000000000 --- a/web-bundle/.jshintignore +++ /dev/null @@ -1,2 +0,0 @@ -src/main/resources/com/graphhopper/maps/js/lib/*.js -src/main/resources/com/graphhopper/maps/js/main.js diff --git a/web-bundle/package.json b/web-bundle/package.json deleted file mode 100644 index e4dcf01a7c3..00000000000 --- a/web-bundle/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "graphhopper-js-ui", - "version": "1.0.0", - "description": "A Route Planner Frontend for GraphHopper", - "author": "GraphHopper Community", - "license": "Apache-2.0", - "main": "main.js", - "scripts": { - "watch": "watchify src/main/resources/com/graphhopper/maps/js/main-template.js -o src/main/resources/com/graphhopper/maps/js/main.js --debug --verbose", - "bundle": "browserify src/main/resources/com/graphhopper/maps/js/main-template.js -o src/main/resources/com/graphhopper/maps/js/main.js", - "bundleDebug": "browserify src/main/resources/com/graphhopper/maps/js/main-template.js --debug -o src/main/resources/com/graphhopper/maps/js/main.js", - "bundleProduction": "browserify -g uglifyify src/main/resources/com/graphhopper/maps/js/main-template.js -o src/main/resources/com/graphhopper/maps/js/main.js", - "test": "JASMINE_CONFIG_PATH=src/test/resources/com/graphhopper/maps/spec/jasmine.json jasmine", - "lint": "jshint src/main/resources/com/graphhopper/maps/js/", - "serve": "live-server --port=8991 --mount=/:src/main/resources/com/graphhopper/maps/ --wait=1000" - }, - "browserify": { - "transform": [ - "browserify-swap" - ] - }, - "browserify-swap": { - "production": { - "config/options.js$": "./src/main/resources/com/graphhopper/maps/js/config/options_prod.js" - } - }, - "dependencies": { - "custom-model-editor": "github:graphhopper/custom-model-editor#693a44097271ed84f603f46efbfcfc56ab97b608", - "flatpickr": "4.4.6", - "jquery": "3.5.0", - "leaflet": "1.5.1", - "leaflet-contextmenu": "1.4.0", - "leaflet-loading": "0.1.24", - "leaflet.heightgraph": "1.4.0", - "leaflet.vectorgrid": "1.3.0", - "moment": "2.29.2" - }, - "devDependencies": { - "browserify": "16.2.0", - "browserify-swap": "0.2.2", - "jasmine": "3.1.0", - "jshint": "^2.10.2", - "live-server": "^1.2.1", - "uglifyify": "5.0.2", - "watchify": "^3.11.1" - } -} diff --git a/web-bundle/pom.xml b/web-bundle/pom.xml index 7fa0efc4281..bf3928fa73d 100644 --- a/web-bundle/pom.xml +++ b/web-bundle/pom.xml @@ -62,8 +62,9 @@ - com.wdtinc - mapbox-vector-tile + org.locationtech.jts + jts-core + 1.19.0 com.fasterxml.jackson.jaxrs @@ -127,7 +128,7 @@ com.github.eirslett frontend-maven-plugin - 1.10.0 + 1.12.1 install node and npm @@ -135,37 +136,66 @@ install-node-and-npm - v12.3.1 - 6.14.5 + v16.17.0 + 8.15.0 - npm install + download graphhopper maps + generate-resources npm - install + + + pack --pack-destination=${basedir}/target + @graphhopper/graphhopper-maps-bundle@${graphhopper-maps.version} + + + + + maven-antrun-plugin + - npm run bundleProduction + + create target directory + initialize + + + + + - npm + run - generate-resources + + + unzip maps bundle + process-resources - run bundleProduction - - development - + + + + + + + + + + + run + - diff --git a/web-bundle/src/main/java/com/graphhopper/http/GHRequestTransformer.java b/web-bundle/src/main/java/com/graphhopper/http/GHRequestTransformer.java new file mode 100644 index 00000000000..9730e2fdbb9 --- /dev/null +++ b/web-bundle/src/main/java/com/graphhopper/http/GHRequestTransformer.java @@ -0,0 +1,25 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.http; + +import com.graphhopper.GHRequest; + +public interface GHRequestTransformer { + GHRequest transformRequest(GHRequest request); +} diff --git a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java index 764da6de617..05991a1cb7c 100644 --- a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java +++ b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java @@ -28,11 +28,12 @@ import com.graphhopper.isochrone.algorithm.JTSTriangulator; import com.graphhopper.isochrone.algorithm.Triangulator; import com.graphhopper.jackson.Jackson; +import com.graphhopper.matching.MapMatching; import com.graphhopper.resources.*; -import com.graphhopper.routing.ProfileResolver; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.index.LocationIndex; +import com.graphhopper.util.PMap; import com.graphhopper.util.TranslationMap; import com.graphhopper.util.details.PathDetailsBuilderFactory; import io.dropwizard.ConfiguredBundle; @@ -61,18 +62,18 @@ public void dispose(TranslationMap instance) { } } - static class GraphHopperStorageFactory implements Factory { + static class BaseGraphFactory implements Factory { @Inject GraphHopper graphHopper; @Override - public GraphHopperStorage provide() { - return graphHopper.getGraphHopperStorage(); + public BaseGraph provide() { + return graphHopper.getBaseGraph(); } @Override - public void dispose(GraphHopperStorage instance) { + public void dispose(BaseGraph instance) { } } @@ -126,25 +127,31 @@ public void dispose(LocationIndex instance) { } static class ProfileResolverFactory implements Factory { - @Inject GraphHopper graphHopper; @Override public ProfileResolver provide() { - return new ProfileResolver(graphHopper.getEncodingManager(), - graphHopper.getProfiles(), - graphHopper.getCHPreparationHandler().getCHProfiles(), - graphHopper.getLMPreparationHandler().getLMProfiles() - ); + return new ProfileResolver(graphHopper.getProfiles()); } @Override - public void dispose(ProfileResolver profileResolver) { + public void dispose(ProfileResolver instance) { } } + static class GHRequestTransformerFactory implements Factory { + @Override + public GHRequestTransformer provide() { + return req -> req; + } + + @Override + public void dispose(GHRequestTransformer instance) { + } + } + static class PathDetailsBuilderFactoryFactory implements Factory { @Inject @@ -161,6 +168,27 @@ public void dispose(PathDetailsBuilderFactory profileResolver) { } } + static class MapMatchingRouterFactoryFactory implements Factory { + + @Inject + GraphHopper graphHopper; + + @Override + public MapMatchingResource.MapMatchingRouterFactory provide() { + return new MapMatchingResource.MapMatchingRouterFactory() { + @Override + public MapMatching.Router createMapMatchingRouter(PMap hints) { + return MapMatching.routerFromGraphHopper(graphHopper, hints); + } + }; + } + + @Override + public void dispose(MapMatchingResource.MapMatchingRouterFactory mapMatchingRouterFactory) { + + } + } + static class HasElevation implements Factory { @Inject @@ -236,13 +264,15 @@ protected void configure() { bind(graphHopper).to(GraphHopper.class); bind(new JTSTriangulator(graphHopper.getRouterConfig())).to(Triangulator.class); + bindFactory(MapMatchingRouterFactoryFactory.class).to(MapMatchingResource.MapMatchingRouterFactory.class); bindFactory(PathDetailsBuilderFactoryFactory.class).to(PathDetailsBuilderFactory.class); bindFactory(ProfileResolverFactory.class).to(ProfileResolver.class); + bindFactory(GHRequestTransformerFactory.class).to(GHRequestTransformer.class); bindFactory(HasElevation.class).to(Boolean.class).named("hasElevation"); bindFactory(LocationIndexFactory.class).to(LocationIndex.class); bindFactory(TranslationMapFactory.class).to(TranslationMap.class); bindFactory(EncodingManagerFactory.class).to(EncodingManager.class); - bindFactory(GraphHopperStorageFactory.class).to(GraphHopperStorage.class); + bindFactory(BaseGraphFactory.class).to(BaseGraph.class); bindFactory(GtfsStorageFactory.class).to(GtfsStorage.class); } }); diff --git a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java index acb7fa806b8..045b056dfe2 100644 --- a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java +++ b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java @@ -18,8 +18,8 @@ package com.graphhopper.http; +import com.bedatadriven.jackson.datatype.jts.JtsModule; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.graphhopper.GraphHopper; import com.graphhopper.GraphHopperConfig; import com.graphhopper.config.Profile; @@ -27,12 +27,19 @@ import com.graphhopper.jackson.Jackson; import com.graphhopper.routing.weighting.custom.CustomProfile; import com.graphhopper.routing.weighting.custom.CustomWeighting; -import com.graphhopper.util.CustomModel; +import com.graphhopper.util.*; import io.dropwizard.lifecycle.Managed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedReader; import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; @@ -49,15 +56,34 @@ public GraphHopperManaged(GraphHopperConfig configuration) { graphHopper = new GraphHopper(); } - String customModelFolder = configuration.getString("custom_model_folder", ""); - List newProfiles = resolveCustomModelFiles(customModelFolder, configuration.getProfiles()); + String customAreasDirectory = configuration.getString("custom_areas.directory", ""); + JsonFeatureCollection globalAreas = resolveCustomAreas(customAreasDirectory); + String customModelFolder = configuration.getString("custom_models.directory", configuration.getString("custom_model_folder", "")); + List newProfiles = resolveCustomModelFiles(customModelFolder, configuration.getProfiles(), globalAreas); configuration.setProfiles(newProfiles); graphHopper.init(configuration); } - public static List resolveCustomModelFiles(String customModelFolder, List profiles) { - ObjectMapper yamlOM = Jackson.initObjectMapper(new ObjectMapper(new YAMLFactory())); + public static JsonFeatureCollection resolveCustomAreas(String customAreasDirectory) { + JsonFeatureCollection globalAreas = new JsonFeatureCollection(); + if (!customAreasDirectory.isEmpty()) { + ObjectMapper mapper = new ObjectMapper().registerModule(new JtsModule()); + try (DirectoryStream stream = Files.newDirectoryStream(Paths.get(customAreasDirectory), "*.{geojson,json}")) { + for (Path customAreaFile : stream) { + try (BufferedReader reader = Files.newBufferedReader(customAreaFile, StandardCharsets.UTF_8)) { + globalAreas.getFeatures().addAll(mapper.readValue(reader, JsonFeatureCollection.class).getFeatures()); + } + } + logger.info("Will make " + globalAreas.getFeatures().size() + " areas available to all custom profiles. Found in " + customAreasDirectory); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return globalAreas; + } + + public static List resolveCustomModelFiles(String customModelFolder, List profiles, JsonFeatureCollection globalAreas) { ObjectMapper jsonOM = Jackson.newObjectMapper(); List newProfiles = new ArrayList<>(); for (Profile profile : profiles) { @@ -66,34 +92,43 @@ public static List resolveCustomModelFiles(String customModelFolder, Li continue; } Object cm = profile.getHints().getObject("custom_model", null); + CustomModel customModel; if (cm != null) { try { // custom_model can be an object tree (read from config) or an object (e.g. from tests) - CustomModel customModel = jsonOM.readValue(jsonOM.writeValueAsBytes(cm), CustomModel.class); + customModel = jsonOM.readValue(jsonOM.writeValueAsBytes(cm), CustomModel.class); newProfiles.add(new CustomProfile(profile).setCustomModel(customModel)); - continue; } catch (Exception ex) { - throw new RuntimeException("Cannot load custom_model from " + cm + " for profile " + profile.getName(), ex); + throw new RuntimeException("Cannot load custom_model from " + cm + " for profile " + profile.getName() + + ". If you are trying to load from a file, use 'custom_model_file' instead.", ex); } - } - String customModelFileName = profile.getHints().getString("custom_model_file", ""); - if (customModelFileName.isEmpty()) - throw new IllegalArgumentException("Missing 'custom_model' or 'custom_model_file' field in profile '" - + profile.getName() + "'. To use default specify custom_model_file: empty"); - if ("empty".equals(customModelFileName)) - newProfiles.add(new CustomProfile(profile).setCustomModel(new CustomModel())); - else { - if (customModelFileName.contains(File.separator)) - throw new IllegalArgumentException("Use custom_model_folder for the custom_model_file parent"); - // Somehow dropwizard makes it very hard to find out the folder of config.yml -> use an extra parameter for the folder - File file = Paths.get(customModelFolder).resolve(customModelFileName).toFile(); - try { - CustomModel customModel = (customModelFileName.endsWith(".json") ? jsonOM : yamlOM).readValue(file, CustomModel.class); - newProfiles.add(new CustomProfile(profile).setCustomModel(customModel)); - } catch (Exception ex) { - throw new RuntimeException("Cannot load custom_model from location " + customModelFileName + " for profile " + profile.getName(), ex); + } else { + String customModelFileName = profile.getHints().getString("custom_model_file", ""); + if (customModelFileName.isEmpty()) + throw new IllegalArgumentException("Missing 'custom_model' or 'custom_model_file' field in profile '" + + profile.getName() + "'. To use default specify custom_model_file: empty"); + if ("empty".equals(customModelFileName)) + newProfiles.add(new CustomProfile(profile).setCustomModel(customModel = new CustomModel())); + else { + if (customModelFileName.contains(File.separator)) + throw new IllegalArgumentException("Use custom_models.directory for the custom_model_file parent"); + if (!customModelFileName.endsWith(".json")) + throw new IllegalArgumentException("Yaml is no longer supported, see #2672. Use JSON with optional comments //"); + try { + // Somehow dropwizard makes it very hard to find out the folder of config.yml -> use an extra parameter for the folder + String string = Helper.readJSONFileWithoutComments(Paths.get(customModelFolder). + resolve(customModelFileName).toFile().getAbsolutePath()); + customModel = jsonOM.readValue(string, CustomModel.class); + newProfiles.add(new CustomProfile(profile).setCustomModel(customModel)); + } catch (Exception ex) { + throw new RuntimeException("Cannot load custom_model from location " + customModelFileName + " for profile " + profile.getName(), ex); + } } } + + // we can fill in all areas here as in the created template we include only the areas that are used in + // statements (see CustomModelParser) + customModel.addAreas(globalAreas); } return newProfiles; } @@ -105,7 +140,7 @@ public void start() { graphHopper.getGraphHopperLocation(), graphHopper.getOSMFile(), graphHopper.getEncodingManager().toEncodedValuesAsString(), graphHopper.getEncodingManager().getIntsForFlags(), - graphHopper.getGraphHopperStorage().toDetailsString()); + graphHopper.getBaseGraph().toDetailsString()); } public GraphHopper getGraphHopper() { diff --git a/web-bundle/src/main/java/com/graphhopper/http/IllegalArgumentExceptionMapper.java b/web-bundle/src/main/java/com/graphhopper/http/IllegalArgumentExceptionMapper.java index 75fdd8ddc8d..6999801d264 100644 --- a/web-bundle/src/main/java/com/graphhopper/http/IllegalArgumentExceptionMapper.java +++ b/web-bundle/src/main/java/com/graphhopper/http/IllegalArgumentExceptionMapper.java @@ -15,7 +15,7 @@ public class IllegalArgumentExceptionMapper implements ExceptionMapper profilesByName; + + public ProfileResolver(List profiles) { + profilesByName = new LinkedHashMap<>(profiles.size()); + profiles.forEach(p -> { + if (profilesByName.put(p.getName(), p) != null) + throw new IllegalArgumentException("Profiles must have distinct names"); + }); + } + + public String resolveProfile(PMap hints) { + String profileName = hints.getString("profile", ""); + if (profileName.isEmpty()) + throw new IllegalArgumentException("profile parameter required"); + errorIfLegacyParameters(hints); + String profile = doResolveProfile(profileName, hints); + if (profile == null) + throw new IllegalArgumentException("The requested profile '" + profileName + "' does not exist.\nAvailable profiles: " + profilesByName.keySet()); + return profile; + } + + protected String doResolveProfile(String profileName, PMap hints) { + Profile profile = profilesByName.get(profileName); + return profile == null ? null : profile.getName(); + } + + public static void errorIfLegacyParameters(PMap hints) { + if (hints.has("weighting")) + throw new IllegalArgumentException("The 'weighting' parameter is no longer supported." + + " You used 'weighting=" + hints.getString("weighting", "") + "'"); + if (hints.has("vehicle")) + throw new IllegalArgumentException("The 'vehicle' parameter is no longer supported." + + " You used 'vehicle=" + hints.getString("vehicle", "") + "'"); + if (hints.has("edge_based")) + throw new IllegalArgumentException("The 'edge_based' parameter is no longer supported." + + " You used 'edge_based=" + hints.getBool("edge_based", false) + "'"); + if (hints.has("turn_costs")) + throw new IllegalArgumentException("The 'turn_costs' parameter is no longer supported." + + " You used 'turn_costs=" + hints.getBool("turn_costs", false) + "'"); + } + +} diff --git a/web-bundle/src/main/java/com/graphhopper/http/RealtimeFeedLoadingCache.java b/web-bundle/src/main/java/com/graphhopper/http/RealtimeFeedLoadingCache.java index b555192b2b6..e563dc135f1 100644 --- a/web-bundle/src/main/java/com/graphhopper/http/RealtimeFeedLoadingCache.java +++ b/web-bundle/src/main/java/com/graphhopper/http/RealtimeFeedLoadingCache.java @@ -28,7 +28,8 @@ import com.graphhopper.gtfs.GtfsStorage; import com.graphhopper.gtfs.RealtimeFeed; import com.graphhopper.gtfs.Transfers; -import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.storage.BaseGraph; import io.dropwizard.lifecycle.Managed; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; @@ -47,7 +48,8 @@ public class RealtimeFeedLoadingCache implements Factory, Managed { private final HttpClient httpClient; - private final GraphHopperStorage graphHopperStorage; + private final BaseGraph baseGraph; + private final EncodingManager encodingManager; private final GtfsStorage gtfsStorage; private final RealtimeBundleConfiguration bundleConfiguration; private ExecutorService executor; @@ -55,8 +57,9 @@ public class RealtimeFeedLoadingCache implements Factory, Managed private Map transfers; @Inject - RealtimeFeedLoadingCache(GraphHopperStorage graphHopperStorage, GtfsStorage gtfsStorage, HttpClient httpClient, RealtimeBundleConfiguration bundleConfiguration) { - this.graphHopperStorage = graphHopperStorage; + RealtimeFeedLoadingCache(BaseGraph baseGraph, EncodingManager encodingManager, GtfsStorage gtfsStorage, HttpClient httpClient, RealtimeBundleConfiguration bundleConfiguration) { + this.baseGraph = baseGraph; + this.encodingManager = encodingManager; this.gtfsStorage = gtfsStorage; this.bundleConfiguration = bundleConfiguration; this.httpClient = httpClient; @@ -115,7 +118,7 @@ private RealtimeFeed fetchFeedsAndCreateGraph() { throw new RuntimeException(e); } } - return RealtimeFeed.fromProtobuf(graphHopperStorage, gtfsStorage, this.transfers, feedMessageMap); + return RealtimeFeed.fromProtobuf(gtfsStorage, this.transfers, feedMessageMap); } } diff --git a/web-bundle/src/main/java/com/graphhopper/http/health/GraphHopperHealthCheck.java b/web-bundle/src/main/java/com/graphhopper/http/health/GraphHopperHealthCheck.java index 63c3db0bda7..5abfa8a07d1 100644 --- a/web-bundle/src/main/java/com/graphhopper/http/health/GraphHopperHealthCheck.java +++ b/web-bundle/src/main/java/com/graphhopper/http/health/GraphHopperHealthCheck.java @@ -31,8 +31,8 @@ public GraphHopperHealthCheck(GraphHopper graphHopper) { @Override protected Result check() { - if (!graphHopper.getGraphHopperStorage().getBounds().isValid()) { - return Result.unhealthy("GraphHopperStorage has invalid bounds."); + if (!graphHopper.getBaseGraph().getBounds().isValid()) { + return Result.unhealthy("BaseGraph has invalid bounds."); } if (!graphHopper.getFullyLoaded()) { return Result.unhealthy("GraphHopper is not fully loaded."); diff --git a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java index 053b6809b32..448881366f5 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java @@ -21,7 +21,9 @@ import com.graphhopper.GraphHopperConfig; import com.graphhopper.config.Profile; import com.graphhopper.routing.ev.*; -import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.storage.BaseGraph; +import com.graphhopper.storage.StorableProperties; import com.graphhopper.util.Constants; import org.locationtech.jts.geom.Envelope; @@ -31,7 +33,10 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; /** * @author Peter Karich @@ -41,13 +46,17 @@ public class InfoResource { private final GraphHopperConfig config; - private final GraphHopperStorage storage; + private final BaseGraph baseGraph; + private final EncodingManager encodingManager; + private final StorableProperties properties; private final boolean hasElevation; @Inject public InfoResource(GraphHopperConfig config, GraphHopper graphHopper, @Named("hasElevation") Boolean hasElevation) { this.config = config; - this.storage = graphHopper.getGraphHopperStorage(); + this.baseGraph = graphHopper.getBaseGraph(); + this.encodingManager = graphHopper.getEncodingManager(); + this.properties = graphHopper.getProperties(); this.hasElevation = hasElevation; } @@ -78,7 +87,7 @@ public ProfileData(String name, String vehicle) { @GET public Info getInfo() { final Info info = new Info(); - info.bbox = new Envelope(storage.getBounds().minLon, storage.getBounds().maxLon, storage.getBounds().minLat, storage.getBounds().maxLat); + info.bbox = new Envelope(baseGraph.getBounds().minLon, baseGraph.getBounds().maxLon, baseGraph.getBounds().minLat, baseGraph.getBounds().maxLat); for (Profile p : config.getProfiles()) { Info.ProfileData profileData = new Info.ProfileData(p.getName(), p.getVehicle()); info.profiles.add(profileData); @@ -87,15 +96,14 @@ public Info getInfo() { info.profiles.add(new Info.ProfileData("pt", "pt")); info.elevation = hasElevation; - List encoderNames = Arrays.asList(storage.getEncodingManager().toString().split(",")); - info.supported_vehicles = new ArrayList<>(encoderNames); + info.supported_vehicles = encodingManager.getVehicles(); if (config.has("gtfs.file")) { info.supported_vehicles.add("pt"); } - info.import_date = storage.getProperties().get("datareader.import.date"); - info.data_date = storage.getProperties().get("datareader.data.date"); + info.import_date = properties.get("datareader.import.date"); + info.data_date = properties.get("datareader.data.date"); - List evList = storage.getEncodingManager().getEncodedValues(); + List evList = encodingManager.getEncodedValues(); info.encoded_values = new LinkedHashMap<>(); for (EncodedValue encodedValue : evList) { List possibleValueList = new ArrayList<>(); diff --git a/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java b/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java index b2910476f88..27254e416e5 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java @@ -5,26 +5,21 @@ import com.graphhopper.GraphHopper; import com.graphhopper.config.Profile; import com.graphhopper.http.GHPointParam; +import com.graphhopper.http.ProfileResolver; import com.graphhopper.isochrone.algorithm.ContourBuilder; import com.graphhopper.isochrone.algorithm.ShortestPathTree; import com.graphhopper.isochrone.algorithm.Triangulator; import com.graphhopper.jackson.ResponsePathSerializer; -import com.graphhopper.routing.ProfileResolver; import com.graphhopper.routing.ev.BooleanEncodedValue; import com.graphhopper.routing.ev.Subnetwork; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.DefaultSnapFilter; -import com.graphhopper.routing.util.FiniteWeightFilter; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.BlockAreaWeighting; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.storage.Graph; -import com.graphhopper.storage.GraphEdgeIdFinder; +import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; import com.graphhopper.util.*; -import io.dropwizard.jersey.params.IntParam; -import io.dropwizard.jersey.params.LongParam; import org.hibernate.validator.constraints.Range; import org.locationtech.jts.geom.*; import org.slf4j.Logger; @@ -38,12 +33,12 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.function.ToDoubleFunction; import static com.graphhopper.resources.IsochroneResource.ResponseType.geojson; -import static com.graphhopper.resources.RouteResource.errorIfLegacyParameters; import static com.graphhopper.resources.RouteResource.removeLegacyParameters; import static com.graphhopper.routing.util.TraversalMode.EDGE_BASED; import static com.graphhopper.routing.util.TraversalMode.NODE_BASED; @@ -71,12 +66,12 @@ public enum ResponseType {json, geojson} public Response doGet( @Context UriInfo uriInfo, @QueryParam("profile") String profileName, - @QueryParam("buckets") @Range(min = 1, max = 20) @DefaultValue("1") IntParam nBuckets, + @QueryParam("buckets") @Range(min = 1, max = 20) @DefaultValue("1") OptionalInt nBuckets, @QueryParam("reverse_flow") @DefaultValue("false") boolean reverseFlow, @QueryParam("point") @NotNull GHPointParam point, - @QueryParam("time_limit") @DefaultValue("600") LongParam timeLimitInSeconds, - @QueryParam("distance_limit") @DefaultValue("-1") LongParam distanceLimitInMeter, - @QueryParam("weight_limit") @DefaultValue("-1") LongParam weightLimit, + @QueryParam("time_limit") @DefaultValue("600") OptionalLong timeLimitInSeconds, + @QueryParam("distance_limit") @DefaultValue("-1") OptionalLong distanceLimitInMeter, + @QueryParam("weight_limit") @DefaultValue("-1") OptionalLong weightLimit, @QueryParam("type") @DefaultValue("json") ResponseType respType, @QueryParam("tolerance") @DefaultValue("0") double toleranceInMeter, @QueryParam("full_geometry") @DefaultValue("false") boolean fullGeometry) { @@ -85,24 +80,19 @@ public Response doGet( RouteResource.initHints(hintsMap, uriInfo.getQueryParameters()); hintsMap.putObject(Parameters.CH.DISABLE, true); hintsMap.putObject(Parameters.Landmark.DISABLE, true); - if (Helper.isEmpty(profileName)) { - profileName = profileResolver.resolveProfile(hintsMap).getName(); - removeLegacyParameters(hintsMap); - } - errorIfLegacyParameters(hintsMap); + + PMap profileResolverHints = new PMap(hintsMap); + profileResolverHints.putObject("profile", profileName); + profileName = profileResolver.resolveProfile(profileResolverHints); + removeLegacyParameters(hintsMap); Profile profile = graphHopper.getProfile(profileName); if (profile == null) throw new IllegalArgumentException("The requested profile '" + profileName + "' does not exist"); LocationIndex locationIndex = graphHopper.getLocationIndex(); - Graph graph = graphHopper.getGraphHopperStorage(); + BaseGraph graph = graphHopper.getBaseGraph(); Weighting weighting = graphHopper.createWeighting(profile, hintsMap); BooleanEncodedValue inSubnetworkEnc = graphHopper.getEncodingManager().getBooleanEncodedValue(Subnetwork.key(profileName)); - if (hintsMap.has(Parameters.Routing.BLOCK_AREA)) { - GraphEdgeIdFinder.BlockArea blockArea = GraphEdgeIdFinder.createBlockArea(graph, locationIndex, - Collections.singletonList(point.get()), hintsMap, new FiniteWeightFilter(weighting)); - weighting = new BlockAreaWeighting(weighting, blockArea); - } Snap snap = locationIndex.findClosest(point.get().lat, point.get().lon, new DefaultSnapFilter(weighting, inSubnetworkEnc)); if (!snap.isValid()) throw new IllegalArgumentException("Point not found:" + point); @@ -111,31 +101,26 @@ public Response doGet( ShortestPathTree shortestPathTree = new ShortestPathTree(queryGraph, queryGraph.wrapWeighting(weighting), reverseFlow, traversalMode); double limit; - if (weightLimit.get() > 0) { - limit = weightLimit.get(); + ToDoubleFunction fz; + if (weightLimit.orElseThrow(() -> new IllegalArgumentException("query param weight_limit is not a number.")) > 0) { + limit = weightLimit.getAsLong(); shortestPathTree.setWeightLimit(limit + Math.max(limit * 0.14, 2_000)); - } else if (distanceLimitInMeter.get() > 0) { - limit = distanceLimitInMeter.get(); + fz = l -> l.weight; + } else if (distanceLimitInMeter.orElseThrow(() -> new IllegalArgumentException("query param distance_limit is not a number.")) > 0) { + limit = distanceLimitInMeter.getAsLong(); shortestPathTree.setDistanceLimit(limit + Math.max(limit * 0.14, 2_000)); + fz = l -> l.distance; } else { - limit = timeLimitInSeconds.get() * 1000; + limit = timeLimitInSeconds.orElseThrow(() -> new IllegalArgumentException("query param time_limit is not a number.")) * 1000d; shortestPathTree.setTimeLimit(limit + Math.max(limit * 0.14, 200_000)); + fz = l -> l.time; } ArrayList zs = new ArrayList<>(); - double delta = limit / nBuckets.get(); - for (int i = 0; i < nBuckets.get(); i++) { + double delta = limit / nBuckets.orElseThrow(() -> new IllegalArgumentException("query param buckets is not a number.")); + for (int i = 0; i < nBuckets.getAsInt(); i++) { zs.add((i + 1) * delta); } - ToDoubleFunction fz; - if (weightLimit.get() > 0) { - fz = l -> l.weight; - } else if (distanceLimitInMeter.get() > 0) { - fz = l -> l.distance; - } else { - fz = l -> l.time; - } - Triangulator.Result result = triangulator.triangulate(snap, queryGraph, shortestPathTree, fz, degreesFromMeters(toleranceInMeter)); ContourBuilder contourBuilder = new ContourBuilder(result.triangulation); diff --git a/web-bundle/src/main/java/com/graphhopper/resources/MVTResource.java b/web-bundle/src/main/java/com/graphhopper/resources/MVTResource.java index d74d2a3e9bb..2e816951887 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/MVTResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/MVTResource.java @@ -5,20 +5,17 @@ import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.index.LocationIndexTree; -import com.graphhopper.util.*; +import com.graphhopper.util.EdgeIteratorState; +import com.graphhopper.util.FetchMode; +import com.graphhopper.util.PointList; +import com.graphhopper.util.StopWatch; import com.graphhopper.util.shapes.BBox; -import com.wdtinc.mapbox_vector_tile.VectorTile; -import com.wdtinc.mapbox_vector_tile.adapt.jts.IGeometryFilter; -import com.wdtinc.mapbox_vector_tile.adapt.jts.JtsAdapter; -import com.wdtinc.mapbox_vector_tile.adapt.jts.TileGeomResult; -import com.wdtinc.mapbox_vector_tile.adapt.jts.UserDataKeyValueMapConverter; -import com.wdtinc.mapbox_vector_tile.build.MvtLayerBuild; -import com.wdtinc.mapbox_vector_tile.build.MvtLayerParams; -import com.wdtinc.mapbox_vector_tile.build.MvtLayerProps; +import no.ecc.vectortile.VectorTileEncoder; import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.util.AffineTransformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,8 +26,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import java.util.HashMap; -import java.util.List; +import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -57,11 +53,11 @@ public Response doGetXyz( @PathParam("z") int zInfo, @PathParam("x") int xInfo, @PathParam("y") int yInfo, - @QueryParam(Parameters.Details.PATH_DETAILS) List pathDetails) { + @QueryParam("render_all") @DefaultValue("false") Boolean renderAll) { if (zInfo <= 9) { - VectorTile.Tile.Builder mvtBuilder = VectorTile.Tile.newBuilder(); - return Response.fromResponse(Response.ok(mvtBuilder.build().toByteArray(), PBF).build()) + byte[] bytes = new VectorTileEncoder().encode(); + return Response.fromResponse(Response.ok(bytes, PBF).build()) .header("X-GH-Took", "0") .build(); } @@ -70,85 +66,92 @@ public Response doGetXyz( Coordinate nw = num2deg(xInfo, yInfo, zInfo); Coordinate se = num2deg(xInfo + 1, yInfo + 1, zInfo); LocationIndexTree locationIndex = (LocationIndexTree) graphHopper.getLocationIndex(); - final NodeAccess na = graphHopper.getGraphHopperStorage().getNodeAccess(); + final NodeAccess na = graphHopper.getBaseGraph().getNodeAccess(); BBox bbox = new BBox(nw.x, se.x, se.y, nw.y); if (!bbox.isValid()) throw new IllegalStateException("Invalid bbox " + bbox); final GeometryFactory geometryFactory = new GeometryFactory(); - VectorTile.Tile.Builder mvtBuilder = VectorTile.Tile.newBuilder(); - final IGeometryFilter acceptAllGeomFilter = geometry -> true; - final Envelope tileEnvelope = new Envelope(se, nw); - final MvtLayerParams layerParams = new MvtLayerParams(256, 4096); - final UserDataKeyValueMapConverter converter = new UserDataKeyValueMapConverter(); if (!encodingManager.hasEncodedValue(RoadClass.KEY)) throw new IllegalStateException("You need to configure GraphHopper to store road_class, e.g. graph.encoded_values: road_class,max_speed,... "); final EnumEncodedValue roadClassEnc = encodingManager.getEnumEncodedValue(RoadClass.KEY, RoadClass.class); final AtomicInteger edgeCounter = new AtomicInteger(0); - // in toFeatures addTags of the converter is called and layerProps is filled with keys&values => those need to be stored in the layerBuilder - // otherwise the decoding won't be successful and "undefined":"undefined" instead of "speed": 30 is the result - final MvtLayerProps layerProps = new MvtLayerProps(); - final VectorTile.Tile.Layer.Builder layerBuilder = MvtLayerBuild.newLayerBuilder("roads", layerParams); + // 256x256 pixels per MVT. here we transform from the global coordinate system to the local one of the tile. + AffineTransformation affineTransformation = new AffineTransformation(); + affineTransformation.translate(-nw.x, -se.y); + affineTransformation.scale( + 256.0 / (se.x - nw.x), + -256.0 / (nw.y - se.y) + ); + affineTransformation.translate(0, 256); + + // if performance of the vector tile encoding becomes an issue it might be worth to get rid of the simplification + // and clipping in the no.ecc code? https://github.com/graphhopper/graphhopper/commit/0f96c2deddb24efa97109e35e0c05f1c91221f59#r90830001 + VectorTileEncoder vectorTileEncoder = new VectorTileEncoder(); locationIndex.query(bbox, edgeId -> { - EdgeIteratorState edge = graphHopper.getGraphHopperStorage().getEdgeIteratorStateForKey(edgeId * 2); + EdgeIteratorState edge = graphHopper.getBaseGraph().getEdgeIteratorStateForKey(edgeId * 2); LineString lineString; - RoadClass rc = edge.get(roadClassEnc); - if (zInfo >= 14) { + if (renderAll) { PointList pl = edge.fetchWayGeometry(FetchMode.ALL); lineString = pl.toLineString(false); - } else if (rc == RoadClass.MOTORWAY - || zInfo > 10 && (rc == RoadClass.PRIMARY || rc == RoadClass.TRUNK) - || zInfo > 11 && (rc == RoadClass.SECONDARY) - || zInfo > 12) { - double lat = na.getLat(edge.getBaseNode()); - double lon = na.getLon(edge.getBaseNode()); - double toLat = na.getLat(edge.getAdjNode()); - double toLon = na.getLon(edge.getAdjNode()); - lineString = geometryFactory.createLineString(new Coordinate[]{new Coordinate(lon, lat), new Coordinate(toLon, toLat)}); } else { - // skip edge for certain zoom - return; + RoadClass rc = edge.get(roadClassEnc); + if (zInfo >= 14) { + PointList pl = edge.fetchWayGeometry(FetchMode.ALL); + lineString = pl.toLineString(false); + } else if (rc == RoadClass.MOTORWAY + || zInfo > 10 && (rc == RoadClass.PRIMARY || rc == RoadClass.TRUNK) + || zInfo > 11 && (rc == RoadClass.SECONDARY) + || zInfo > 12) { + double lat = na.getLat(edge.getBaseNode()); + double lon = na.getLon(edge.getBaseNode()); + double toLat = na.getLat(edge.getAdjNode()); + double toLon = na.getLon(edge.getAdjNode()); + lineString = geometryFactory.createLineString(new Coordinate[]{new Coordinate(lon, lat), new Coordinate(toLon, toLat)}); + } else { + // skip edge for certain zoom + return; + } } edgeCounter.incrementAndGet(); - Map map = new HashMap<>(2); - map.put("name", edge.getName()); - for (String str : pathDetails) { - // how to indicate an erroneous parameter? - if (str.contains(",") || !encodingManager.hasEncodedValue(str)) - continue; - - EncodedValue ev = encodingManager.getEncodedValue(str, EncodedValue.class); + Map map = new LinkedHashMap<>(); + edge.getKeyValues().forEach( + entry -> map.put(entry.key, entry.value) + ); + map.put("edge_id", edge.getEdge()); + map.put("edge_key", edge.getEdgeKey()); + map.put("base_node", edge.getBaseNode()); + map.put("adj_node", edge.getAdjNode()); + map.put("distance", edge.getDistance()); + encodingManager.getEncodedValues().forEach(ev -> { if (ev instanceof EnumEncodedValue) - map.put(ev.getName(), edge.get((EnumEncodedValue) ev).toString()); + map.put(ev.getName(), edge.get((EnumEncodedValue) ev).toString() + (ev.isStoreTwoDirections() ? " | " + edge.getReverse((EnumEncodedValue) ev).toString() : "")); else if (ev instanceof DecimalEncodedValue) - map.put(ev.getName(), edge.get((DecimalEncodedValue) ev)); + map.put(ev.getName(), edge.get((DecimalEncodedValue) ev) + (ev.isStoreTwoDirections() ? " | " + edge.getReverse((DecimalEncodedValue) ev) : "")); else if (ev instanceof BooleanEncodedValue) - map.put(ev.getName(), edge.get((BooleanEncodedValue) ev)); + map.put(ev.getName(), edge.get((BooleanEncodedValue) ev) + (ev.isStoreTwoDirections() ? " | " + edge.getReverse((BooleanEncodedValue) ev) : "")); else if (ev instanceof IntEncodedValue) - map.put(ev.getName(), edge.get((IntEncodedValue) ev)); - } - + map.put(ev.getName(), edge.get((IntEncodedValue) ev) + (ev.isStoreTwoDirections() ? " | " + edge.getReverse((IntEncodedValue) ev) : "")); + }); lineString.setUserData(map); - // doing some AffineTransformation - TileGeomResult tileGeom = JtsAdapter.createTileGeom(lineString, tileEnvelope, geometryFactory, layerParams, acceptAllGeomFilter); - List features = JtsAdapter.toFeatures(tileGeom.mvtGeoms, layerProps, converter); - layerBuilder.addAllFeatures(features); + Geometry g = affineTransformation.transform(lineString); + vectorTileEncoder.addFeature("roads", map, g, edge.getEdge()); }); - MvtLayerBuild.writeProps(layerBuilder, layerProps); - mvtBuilder.addLayers(layerBuilder.build()); - byte[] bytes = mvtBuilder.build().toByteArray(); + + byte[] bytes = vectorTileEncoder.encode(); totalSW.stop(); - logger.debug("took: " + totalSW.getSeconds() + ", edges:" + edgeCounter.get()); + logger.debug("took: " + totalSW.getMillis() + "ms, edges:" + edgeCounter.get()); return Response.ok(bytes, PBF).header("X-GH-Took", "" + totalSW.getSeconds() * 1000) .build(); } Coordinate num2deg(int xInfo, int yInfo, int zoom) { + // inverse web mercator projection double n = Math.pow(2, zoom); double lonDeg = xInfo / n * 360.0 - 180.0; // unfortunately latitude numbers goes from north to south diff --git a/web-bundle/src/main/java/com/graphhopper/resources/MapMatchingResource.java b/web-bundle/src/main/java/com/graphhopper/resources/MapMatchingResource.java index ebaa70cae03..dacb5445ba0 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/MapMatchingResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/MapMatchingResource.java @@ -18,6 +18,7 @@ package com.graphhopper.resources; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -25,20 +26,25 @@ import com.graphhopper.GraphHopper; import com.graphhopper.ResponsePath; import com.graphhopper.gpx.GpxConversions; +import com.graphhopper.http.ProfileResolver; import com.graphhopper.jackson.Gpx; +import com.graphhopper.jackson.Jackson; import com.graphhopper.jackson.ResponsePathSerializer; import com.graphhopper.matching.*; -import com.graphhopper.routing.ProfileResolver; +import com.graphhopper.storage.index.LocationIndexTree; import com.graphhopper.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; import javax.ws.rs.*; -import javax.ws.rs.core.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import java.util.*; +import static com.graphhopper.resources.RouteResource.removeLegacyParameters; import static com.graphhopper.util.Parameters.Details.PATH_DETAILS; import static com.graphhopper.util.Parameters.Routing.*; @@ -50,17 +56,24 @@ @javax.ws.rs.Path("match") public class MapMatchingResource { + public interface MapMatchingRouterFactory { + public MapMatching.Router createMapMatchingRouter(PMap hints); + } + private static final Logger logger = LoggerFactory.getLogger(MapMatchingResource.class); private final GraphHopper graphHopper; private final ProfileResolver profileResolver; private final TranslationMap trMap; + private final MapMatchingRouterFactory mapMatchingRouterFactory; + private final ObjectMapper objectMapper = Jackson.newObjectMapper(); @Inject - public MapMatchingResource(GraphHopper graphHopper, ProfileResolver profileResolver, TranslationMap trMap) { + public MapMatchingResource(GraphHopper graphHopper, ProfileResolver profileResolver, TranslationMap trMap, MapMatchingRouterFactory mapMatchingRouterFactory) { this.graphHopper = graphHopper; this.profileResolver = profileResolver; this.trMap = trMap; + this.mapMatchingRouterFactory = mapMatchingRouterFactory; } @POST @@ -68,7 +81,6 @@ public MapMatchingResource(GraphHopper graphHopper, ProfileResolver profileResol @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, "application/gpx+xml"}) public Response match( Gpx gpx, - @Context HttpServletRequest request, @Context UriInfo uriInfo, @QueryParam(WAY_POINT_MAX_DISTANCE) @DefaultValue("1") double minPathPrecision, @QueryParam("type") @DefaultValue("json") String outType, @@ -82,8 +94,7 @@ public Response match( @QueryParam("gpx.route") @DefaultValue("true") boolean withRoute, @QueryParam("gpx.track") @DefaultValue("true") boolean withTrack, @QueryParam("traversal_keys") @DefaultValue("false") boolean enableTraversalKeys, - @QueryParam("gps_accuracy") @DefaultValue("40") double gpsAccuracy, - @QueryParam(MAX_VISITED_NODES) @DefaultValue("3000") int maxVisitedNodes) { + @QueryParam("gps_accuracy") @DefaultValue("40") double gpsAccuracy) { boolean writeGPX = "gpx".equalsIgnoreCase(outType); if (gpx.trk.isEmpty()) { @@ -97,44 +108,42 @@ public Response match( StopWatch sw = new StopWatch().start(); - PMap hints = createHintsMap(uriInfo.getQueryParameters()); - // add values that are not in hints because they were explicitly listed in query params - hints.putObject(MAX_VISITED_NODES, maxVisitedNodes); - String weightingVehicleLogStr = "weighting: " + hints.getString("weighting", "") + ", vehicle: " + hints.getString("vehicle", ""); - if (Helper.isEmpty(profile)) { - // resolve profile and remove legacy vehicle/weighting parameters - // we need to explicitly disable CH here because map matching does not use it - PMap pMap = new PMap(hints).putObject(Parameters.CH.DISABLE, true); - profile = profileResolver.resolveProfile(pMap).getName(); - removeLegacyParameters(hints); - } + PMap hints = new PMap(); + RouteResource.initHints(hints, uriInfo.getQueryParameters()); + + // resolve profile and remove legacy vehicle/weighting parameters + // we need to explicitly disable CH here because map matching does not use it + PMap profileResolverHints = new PMap(hints); + profileResolverHints.putObject("profile", profile); + profileResolverHints.putObject(Parameters.CH.DISABLE, true); + profile = profileResolver.resolveProfile(profileResolverHints); hints.putObject("profile", profile); - errorIfLegacyParameters(hints); + removeLegacyParameters(hints); - MapMatching matching = new MapMatching(graphHopper, hints); + MapMatching matching = new MapMatching(graphHopper.getBaseGraph(), (LocationIndexTree) graphHopper.getLocationIndex(), mapMatchingRouterFactory.createMapMatchingRouter(hints)); matching.setMeasurementErrorSigma(gpsAccuracy); List measurements = GpxConversions.getEntries(gpx.trk.get(0)); MatchResult matchResult = matching.match(measurements); - // TODO: Request logging and timing should perhaps be done somewhere outside - float took = sw.stop().getSeconds(); - String infoStr = request.getRemoteAddr() + " " + request.getLocale() + " " + request.getHeader("User-Agent"); - String logStr = request.getQueryString() + ", " + infoStr + ", took:" + took + "s, entries:" + measurements.size() + - ", profile: " + profile + ", " + weightingVehicleLogStr; - logger.info(logStr); + sw.stop(); + logger.info(objectMapper.createObjectNode() + .put("duration", sw.getNanos()) + .put("profile", profile) + .put("observations", measurements.size()) + .putPOJO("mapmatching", matching.getStatistics()).toString()); if ("extended_json".equals(outType)) { return Response.ok(convertToTree(matchResult, enableElevation, pointsEncoded)). - header("X-GH-Took", "" + Math.round(took * 1000)). + header("X-GH-Took", "" + Math.round(sw.getMillisDouble())). build(); } else { Translation tr = trMap.getWithFallBack(Helper.getLocale(localeStr)); - DouglasPeucker peucker = new DouglasPeucker().setMaxDistance(minPathPrecision); + RamerDouglasPeucker simplifyAlgo = new RamerDouglasPeucker().setMaxDistance(minPathPrecision); PathMerger pathMerger = new PathMerger(matchResult.getGraph(), matchResult.getWeighting()). setEnableInstructions(instructions). setPathDetailsBuilders(graphHopper.getPathDetailsBuilderFactory(), pathDetails). - setDouglasPeucker(peucker). + setRamerDouglasPeucker(simplifyAlgo). setSimplifyResponse(minPathPrecision > 0); ResponsePath responsePath = pathMerger.doWork(PointList.EMPTY, Collections.singletonList(matchResult.getMergedPath()), graphHopper.getEncodingManager(), tr); @@ -150,10 +159,10 @@ public Response match( .map(Date::getTime) .orElse(System.currentTimeMillis()); return Response.ok(GpxConversions.createGPX(rsp.getBest().getInstructions(), gpx.trk.get(0).name != null ? gpx.trk.get(0).name : "", time, enableElevation, withRoute, withTrack, false, Constants.VERSION, tr), "application/gpx+xml"). - header("X-GH-Took", "" + Math.round(took * 1000)). + header("X-GH-Took", "" + Math.round(sw.getMillisDouble())). build(); } else { - ObjectNode map = ResponsePathSerializer.jsonObject(rsp, instructions, calcPoints, enableElevation, pointsEncoded, took); + ObjectNode map = ResponsePathSerializer.jsonObject(rsp, instructions, calcPoints, enableElevation, pointsEncoded, sw.getMillisDouble()); Map matchStatistics = new HashMap<>(); matchStatistics.put("distance", matchResult.getMatchLength()); @@ -166,43 +175,17 @@ public Response match( for (EdgeMatch em : matchResult.getEdgeMatches()) { EdgeIteratorState edge = em.getEdgeState(); // encode edges as traversal keys which includes orientation, decode simply by multiplying with 0.5 - traversalKeylist.add(GHUtility.createEdgeKey(edge.getBaseNode(), edge.getAdjNode(), edge.getEdge(), false)); + traversalKeylist.add(edge.getEdgeKey()); } map.putPOJO("traversal_keys", traversalKeylist); } return Response.ok(map). - header("X-GH-Took", "" + Math.round(took * 1000)). + header("X-GH-Took", "" + Math.round(sw.getMillisDouble())). build(); } } } - private void removeLegacyParameters(PMap hints) { - hints.remove("vehicle"); - hints.remove("weighting"); - } - - private static void errorIfLegacyParameters(PMap hints) { - if (hints.has("weighting")) - throw new IllegalArgumentException("Since you are using the 'profile' parameter, do not use the 'weighting' parameter." + - " You used 'weighting=" + hints.getString("weighting", "") + "'"); - if (hints.has("vehicle")) - throw new IllegalArgumentException("Since you are using the 'profile' parameter, do not use the 'vehicle' parameter." + - " You used 'vehicle=" + hints.getString("vehicle", "") + "'"); - } - - private PMap createHintsMap(MultivaluedMap queryParameters) { - PMap m = new PMap(); - for (Map.Entry> e : queryParameters.entrySet()) { - if (e.getValue().size() == 1) { - m.putObject(Helper.camelCaseToUnderScore(e.getKey()), Helper.toObject(e.getValue().get(0))); - } else { - // TODO ugly: ignore multi parameters like point to avoid exception. See RouteResource.initHints - } - } - return m; - } - public static JsonNode convertToTree(MatchResult result, boolean elevation, boolean pointsEncoded) { ObjectNode root = JsonNodeFactory.instance.objectNode(); ObjectNode diary = root.putObject("diary"); diff --git a/web-bundle/src/main/java/com/graphhopper/resources/PtIsochroneResource.java b/web-bundle/src/main/java/com/graphhopper/resources/PtIsochroneResource.java index bab5927f0ca..ec079d52ec0 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/PtIsochroneResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/PtIsochroneResource.java @@ -25,13 +25,12 @@ import com.graphhopper.isochrone.algorithm.ContourBuilder; import com.graphhopper.isochrone.algorithm.ReadableTriangulation; import com.graphhopper.jackson.ResponsePathSerializer; -import com.graphhopper.routing.ev.Subnetwork; +import com.graphhopper.routing.ev.*; import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.weighting.FastestWeighting; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.util.EdgeIteratorState; @@ -59,14 +58,14 @@ public class PtIsochroneResource { private final GtfsStorage gtfsStorage; private final EncodingManager encodingManager; - private final GraphHopperStorage graphHopperStorage; + private final BaseGraph baseGraph; private final LocationIndex locationIndex; @Inject - public PtIsochroneResource(GtfsStorage gtfsStorage, EncodingManager encodingManager, GraphHopperStorage graphHopperStorage, LocationIndex locationIndex) { + public PtIsochroneResource(GtfsStorage gtfsStorage, EncodingManager encodingManager, BaseGraph baseGraph, LocationIndex locationIndex) { this.gtfsStorage = gtfsStorage; this.encodingManager = encodingManager; - this.graphHopperStorage = graphHopperStorage; + this.baseGraph = baseGraph; this.locationIndex = locationIndex; } @@ -94,11 +93,12 @@ public Response doGet( double targetZ = seconds * 1000; GeometryFactory geometryFactory = new GeometryFactory(); - final FlagEncoder footEncoder = encodingManager.getEncoder("foot"); - final Weighting weighting = new FastestWeighting(footEncoder); - DefaultSnapFilter snapFilter = new DefaultSnapFilter(weighting, graphHopperStorage.getEncodingManager().getBooleanEncodedValue(Subnetwork.key("foot"))); + BooleanEncodedValue accessEnc = encodingManager.getBooleanEncodedValue(VehicleAccess.key("foot")); + DecimalEncodedValue speedEnc = encodingManager.getDecimalEncodedValue(VehicleSpeed.key("foot")); + final Weighting weighting = new FastestWeighting(accessEnc, speedEnc); + DefaultSnapFilter snapFilter = new DefaultSnapFilter(weighting, encodingManager.getBooleanEncodedValue(Subnetwork.key("foot"))); - PtLocationSnapper.Result snapResult = new PtLocationSnapper(graphHopperStorage, locationIndex, gtfsStorage).snapAll(Arrays.asList(location), Arrays.asList(snapFilter)); + PtLocationSnapper.Result snapResult = new PtLocationSnapper(baseGraph, locationIndex, gtfsStorage).snapAll(Arrays.asList(location), Arrays.asList(snapFilter)); GraphExplorer graphExplorer = new GraphExplorer(snapResult.queryGraph, gtfsStorage.getPtGraph(), weighting, gtfsStorage, RealtimeFeed.empty(), reverseFlow, false, false, 5.0, reverseFlow, blockedRouteTypes); MultiCriteriaLabelSetting router = new MultiCriteriaLabelSetting(graphExplorer, reverseFlow, false, false, 0, Collections.emptyList()); diff --git a/web-bundle/src/main/java/com/graphhopper/resources/PtMVTResource.java b/web-bundle/src/main/java/com/graphhopper/resources/PtMVTResource.java index 78ed7a276b7..2b392702ebf 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/PtMVTResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/PtMVTResource.java @@ -8,14 +8,9 @@ import com.graphhopper.matching.MatchResult; import com.graphhopper.util.Parameters; import com.graphhopper.util.shapes.BBox; -import com.wdtinc.mapbox_vector_tile.VectorTile; -import com.wdtinc.mapbox_vector_tile.adapt.jts.JtsAdapter; -import com.wdtinc.mapbox_vector_tile.adapt.jts.TileGeomResult; -import com.wdtinc.mapbox_vector_tile.adapt.jts.UserDataKeyValueMapConverter; -import com.wdtinc.mapbox_vector_tile.build.MvtLayerBuild; -import com.wdtinc.mapbox_vector_tile.build.MvtLayerParams; -import com.wdtinc.mapbox_vector_tile.build.MvtLayerProps; +import no.ecc.vectortile.VectorTileEncoder; import org.locationtech.jts.geom.*; +import org.locationtech.jts.geom.util.AffineTransformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,7 +63,15 @@ public Response doGetXyz( if (!bbox.isValid()) throw new IllegalStateException("Invalid bbox " + bbox); - List features = new ArrayList<>(); + VectorTileEncoder vectorTileEncoder = new VectorTileEncoder(); + // 256x256 pixels per MVT. here we transform from the global coordinate system to the local one of the tile. + AffineTransformation affineTransformation = new AffineTransformation(); + affineTransformation.translate(-nw.x, -se.y); + affineTransformation.scale( + 256.0 / (se.x - nw.x), + -256.0 / (nw.y - se.y) + ); + affineTransformation.translate(0, 256); gtfsStorage.getStopIndex().query(bbox, edgeId -> { for (PtGraph.PtEdge ptEdge : gtfsStorage.getPtGraph().backEdgesAround(edgeId)) { if (ptEdge.getType() == GtfsStorage.EdgeType.EXIT_PT) { @@ -79,22 +82,12 @@ public Response doGetXyz( properties.put("stop_id", fromPlatformDescriptor.stop_id); Point feature = geometryFactory.createPoint(new Coordinate(stop.stop_lon, stop.stop_lat)); feature.setUserData(properties); - features.add(feature); + Geometry g = affineTransformation.transform(feature); + vectorTileEncoder.addFeature("stops", properties, g); } } }); - VectorTile.Tile.Builder mvtBuilder = VectorTile.Tile.newBuilder(); - mvtBuilder.addLayers(createLayer(new Envelope(se, nw), new MvtLayerParams(256, 4096), features, "stops")); - return Response.ok(mvtBuilder.build().toByteArray(), PBF).build(); - } - - private VectorTile.Tile.Layer createLayer(Envelope tileEnvelope, MvtLayerParams layerParams, List locationReferences, String layerName) { - final VectorTile.Tile.Layer.Builder roadsLayerBuilder = MvtLayerBuild.newLayerBuilder(layerName, layerParams); - TileGeomResult tileGeom = JtsAdapter.createTileGeom(locationReferences, tileEnvelope, geometryFactory, layerParams, geometry -> true); - final MvtLayerProps roadsLayerProps = new MvtLayerProps(); - roadsLayerBuilder.addAllFeatures(JtsAdapter.toFeatures(tileGeom.mvtGeoms, roadsLayerProps, new UserDataKeyValueMapConverter())); - MvtLayerBuild.writeProps(roadsLayerBuilder, roadsLayerProps); - return roadsLayerBuilder.build(); + return Response.ok(vectorTileEncoder.encode(), PBF).build(); } Coordinate num2deg(int xInfo, int yInfo, int zoom) { diff --git a/web-bundle/src/main/java/com/graphhopper/resources/RouteResource.java b/web-bundle/src/main/java/com/graphhopper/resources/RouteResource.java index d280486ad9b..76debaa25c0 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/RouteResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/RouteResource.java @@ -22,9 +22,10 @@ import com.graphhopper.GraphHopper; import com.graphhopper.gpx.GpxConversions; import com.graphhopper.http.GHPointParam; +import com.graphhopper.http.GHRequestTransformer; +import com.graphhopper.http.ProfileResolver; import com.graphhopper.jackson.MultiException; import com.graphhopper.jackson.ResponsePathSerializer; -import com.graphhopper.routing.ProfileResolver; import com.graphhopper.util.*; import com.graphhopper.util.shapes.GHPoint; import io.dropwizard.jersey.params.AbstractParam; @@ -58,12 +59,14 @@ public class RouteResource { private final GraphHopper graphHopper; private final ProfileResolver profileResolver; + private final GHRequestTransformer ghRequestTransformer; private final Boolean hasElevation; @Inject - public RouteResource(GraphHopper graphHopper, ProfileResolver profileResolver, @Named("hasElevation") Boolean hasElevation) { + public RouteResource(GraphHopper graphHopper, ProfileResolver profileResolver, GHRequestTransformer ghRequestTransformer, @Named("hasElevation") Boolean hasElevation) { this.graphHopper = graphHopper; this.profileResolver = profileResolver; + this.ghRequestTransformer = ghRequestTransformer; this.hasElevation = hasElevation; } @@ -93,22 +96,19 @@ public Response doGet( @QueryParam("gpx.waypoints") @DefaultValue("false") boolean withWayPoints, @QueryParam("gpx.trackname") @DefaultValue("GraphHopper Track") String trackName, @QueryParam("gpx.millis") String timeString) { + StopWatch sw = new StopWatch().start(); List points = pointParams.stream().map(AbstractParam::get).collect(toList()); boolean writeGPX = "gpx".equalsIgnoreCase(type); instructions = writeGPX || instructions; if (enableElevation && !hasElevation) throw new IllegalArgumentException("Elevation not supported!"); - StopWatch sw = new StopWatch().start(); GHRequest request = new GHRequest(); initHints(request.getHints(), uriInfo.getQueryParameters()); - String weightingVehicleLogStr = "weighting: " + request.getHints().getString("weighting", "") + ", vehicle: " + request.getHints().getString("vehicle", ""); - if (Helper.isEmpty(profileName)) { - enableEdgeBasedIfThereAreCurbsides(curbsides, request); - profileName = profileResolver.resolveProfile(request.getHints()).getName(); - removeLegacyParameters(request.getHints()); - } - errorIfLegacyParameters(request.getHints()); + + if (minPathElevationPrecision != null) + request.getHints().putObject(ELEVATION_WAY_POINT_MAX_DISTANCE, minPathElevationPrecision); + request.setPoints(points). setProfile(profileName). setAlgorithm(algoStr). @@ -123,20 +123,24 @@ public Response doGet( putObject(INSTRUCTIONS, instructions). putObject(WAY_POINT_MAX_DISTANCE, minPathPrecision); - if (minPathElevationPrecision != null) { - request.getHints().putObject(ELEVATION_WAY_POINT_MAX_DISTANCE, minPathElevationPrecision); - } + request = ghRequestTransformer.transformRequest(request); + + PMap profileResolverHints = new PMap(request.getHints()); + profileResolverHints.putObject("profile", profileName); + profileResolverHints.putObject("has_curbsides", !curbsides.isEmpty()); + profileName = profileResolver.resolveProfile(profileResolverHints); + removeLegacyParameters(request.getHints()); + request.setProfile(profileName); GHResponse ghResponse = graphHopper.route(request); - long took = sw.stop().getNanos() / 1_000_000; - String infoStr = httpReq.getRemoteAddr() + " " + httpReq.getLocale() + " " + httpReq.getHeader("User-Agent"); - String logStr = httpReq.getQueryString() + " " + infoStr + " " + points + ", took: " - + String.format("%.1f", (double) took) + "ms, algo: " + algoStr + ", profile: " + profileName + ", " + weightingVehicleLogStr; + double took = sw.stop().getMillisDouble(); + String logStr = (httpReq.getRemoteAddr() + " " + httpReq.getLocale() + " " + httpReq.getHeader("User-Agent")) + " " + points + ", took: " + String.format("%.1f", took) + "ms, algo: " + algoStr + ", profile: " + profileName; if (ghResponse.hasErrors()) { - logger.error(logStr + ", errors:" + ghResponse.getErrors()); - throw new MultiException(ghResponse.getErrors()); + MultiException ex = new MultiException(ghResponse.getErrors()); + logger.error(logStr, ex); + throw ex; } else { logger.info(logStr + ", alternatives: " + ghResponse.getAll().size() + ", distance0: " + ghResponse.getBest().getDistance() @@ -161,39 +165,34 @@ public Response doGet( @Produces(MediaType.APPLICATION_JSON) public Response doPost(@NotNull GHRequest request, @Context HttpServletRequest httpReq) { StopWatch sw = new StopWatch().start(); - if (request.getCustomModel() == null) { - if (Helper.isEmpty(request.getProfile())) { - // legacy parameter resolution (only used when there is no custom model) - enableEdgeBasedIfThereAreCurbsides(request.getCurbsides(), request); - request.setProfile(profileResolver.resolveProfile(request.getHints()).getName()); - removeLegacyParameters(request.getHints()); - } - } else { - if (Helper.isEmpty(request.getProfile())) - // throw a dedicated exception here, otherwise a missing profile is still caught in Router - throw new IllegalArgumentException("The 'profile' parameter is required when you use the `custom_model` parameter"); - } - errorIfLegacyParameters(request.getHints()); + request = ghRequestTransformer.transformRequest(request); + + if (Helper.isEmpty(request.getProfile()) && request.getCustomModel() != null) + // throw a dedicated exception here, otherwise a missing profile is still caught in Router + throw new IllegalArgumentException("The 'profile' parameter is required when you use the `custom_model` parameter"); + + PMap profileResolverHints = new PMap(request.getHints()); + profileResolverHints.putObject("profile", request.getProfile()); + profileResolverHints.putObject("has_curbsides", !request.getCurbsides().isEmpty()); + request.setProfile(profileResolver.resolveProfile(profileResolverHints)); + removeLegacyParameters(request.getHints()); + GHResponse ghResponse = graphHopper.route(request); boolean instructions = request.getHints().getBool(INSTRUCTIONS, true); boolean enableElevation = request.getHints().getBool("elevation", false); boolean calcPoints = request.getHints().getBool(CALC_POINTS, true); boolean pointsEncoded = request.getHints().getBool("points_encoded", true); - long took = sw.stop().getNanos() / 1_000_000; + double took = sw.stop().getMillisDouble(); String infoStr = httpReq.getRemoteAddr() + " " + httpReq.getLocale() + " " + httpReq.getHeader("User-Agent"); - String queryString = httpReq.getQueryString() == null ? "" : (httpReq.getQueryString() + " "); - // todo: vehicle/weighting will always be empty at this point... - String weightingVehicleLogStr = "weighting: " + request.getHints().getString("weighting", "") - + ", vehicle: " + request.getHints().getString("vehicle", ""); - String logStr = queryString + infoStr + " " + request.getPoints().size() + ", took: " - + String.format("%.1f", (double) took) + " ms, algo: " + request.getAlgorithm() + ", profile: " + request.getProfile() - + ", " + weightingVehicleLogStr + String logStr = infoStr + " " + request.getPoints().size() + ", took: " + + String.format("%.1f", took) + " ms, algo: " + request.getAlgorithm() + ", profile: " + request.getProfile() + ", custom_model: " + request.getCustomModel(); if (ghResponse.hasErrors()) { - logger.error(logStr + ", errors:" + ghResponse.getErrors()); - throw new MultiException(ghResponse.getErrors()); + MultiException ex = new MultiException(ghResponse.getErrors()); + logger.error(logStr, ex); + throw ex; } else { logger.info(logStr + ", alternatives: " + ghResponse.getAll().size() + ", distance0: " + ghResponse.getBest().getDistance() @@ -208,31 +207,6 @@ public Response doPost(@NotNull GHRequest request, @Context HttpServletRequest h } } - private void enableEdgeBasedIfThereAreCurbsides(List curbsides, GHRequest request) { - if (!curbsides.isEmpty()) { - if (!request.getHints().getBool(TURN_COSTS, true)) - throw new IllegalArgumentException("Disabling '" + TURN_COSTS + "' when using '" + CURBSIDE + "' is not allowed"); - if (!request.getHints().getBool(EDGE_BASED, true)) - throw new IllegalArgumentException("Disabling '" + EDGE_BASED + "' when using '" + CURBSIDE + "' is not allowed"); - request.getHints().putObject(EDGE_BASED, true); - } - } - - public static void errorIfLegacyParameters(PMap hints) { - if (hints.has("weighting")) - throw new IllegalArgumentException("Since you are using the 'profile' parameter, do not use the 'weighting' parameter." + - " You used 'weighting=" + hints.getString("weighting", "") + "'"); - if (hints.has("vehicle")) - throw new IllegalArgumentException("Since you are using the 'profile' parameter, do not use the 'vehicle' parameter." + - " You used 'vehicle=" + hints.getString("vehicle", "") + "'"); - if (hints.has("edge_based")) - throw new IllegalArgumentException("Since you are using the 'profile' parameter, do not use the 'edge_based' parameter." + - " You used 'edge_based=" + hints.getBool("edge_based", false) + "'"); - if (hints.has("turn_costs")) - throw new IllegalArgumentException("Since you are using the 'profile' parameter, do not use the 'turn_costs' parameter." + - " You used 'turn_costs=" + hints.getBool("turn_costs", false) + "'"); - } - public static void removeLegacyParameters(PMap hints) { // these parameters should only be used to resolve the profile, but should not be passed to GraphHopper hints.remove("weighting"); diff --git a/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java b/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java index fe2630c25bf..a7a3cc947d2 100644 --- a/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java +++ b/web-bundle/src/main/java/com/graphhopper/resources/SPTResource.java @@ -3,24 +3,20 @@ import com.graphhopper.GraphHopper; import com.graphhopper.config.Profile; import com.graphhopper.http.GHPointParam; +import com.graphhopper.http.ProfileResolver; import com.graphhopper.isochrone.algorithm.ShortestPathTree; -import com.graphhopper.routing.ProfileResolver; import com.graphhopper.routing.ev.*; import com.graphhopper.routing.querygraph.QueryGraph; import com.graphhopper.routing.util.DefaultSnapFilter; import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FiniteWeightFilter; import com.graphhopper.routing.util.TraversalMode; -import com.graphhopper.routing.weighting.BlockAreaWeighting; import com.graphhopper.routing.weighting.Weighting; -import com.graphhopper.storage.Graph; -import com.graphhopper.storage.GraphEdgeIdFinder; +import com.graphhopper.storage.BaseGraph; import com.graphhopper.storage.NodeAccess; import com.graphhopper.storage.index.LocationIndex; import com.graphhopper.storage.index.Snap; import com.graphhopper.util.*; import com.graphhopper.util.shapes.GHPoint; -import io.dropwizard.jersey.params.LongParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,10 +33,10 @@ import java.io.Writer; import java.util.*; -import static com.graphhopper.resources.RouteResource.errorIfLegacyParameters; import static com.graphhopper.resources.RouteResource.removeLegacyParameters; import static com.graphhopper.routing.util.TraversalMode.EDGE_BASED; import static com.graphhopper.routing.util.TraversalMode.NODE_BASED; +import static com.graphhopper.util.Parameters.Details.STREET_NAME; /** * This resource provides the entire shortest path tree as response. In a simple CSV format discussed at #1577. @@ -79,31 +75,26 @@ public Response doGet( @QueryParam("reverse_flow") @DefaultValue("false") boolean reverseFlow, @QueryParam("point") @NotNull GHPointParam point, @QueryParam("columns") String columnsParam, - @QueryParam("time_limit") @DefaultValue("600") LongParam timeLimitInSeconds, - @QueryParam("distance_limit") @DefaultValue("-1") LongParam distanceInMeter) { + @QueryParam("time_limit") @DefaultValue("600") OptionalLong timeLimitInSeconds, + @QueryParam("distance_limit") @DefaultValue("-1") OptionalLong distanceInMeter) { StopWatch sw = new StopWatch().start(); PMap hintsMap = new PMap(); RouteResource.initHints(hintsMap, uriInfo.getQueryParameters()); hintsMap.putObject(Parameters.CH.DISABLE, true); hintsMap.putObject(Parameters.Landmark.DISABLE, true); - if (Helper.isEmpty(profileName)) { - profileName = profileResolver.resolveProfile(hintsMap).getName(); - removeLegacyParameters(hintsMap); - } - errorIfLegacyParameters(hintsMap); + PMap profileResolverHints = new PMap(hintsMap); + profileResolverHints.putObject("profile", profileName); + profileName = profileResolver.resolveProfile(profileResolverHints); + removeLegacyParameters(hintsMap); + Profile profile = graphHopper.getProfile(profileName); if (profile == null) throw new IllegalArgumentException("The requested profile '" + profileName + "' does not exist"); LocationIndex locationIndex = graphHopper.getLocationIndex(); - Graph graph = graphHopper.getGraphHopperStorage(); + BaseGraph graph = graphHopper.getBaseGraph(); Weighting weighting = graphHopper.createWeighting(profile, hintsMap); BooleanEncodedValue inSubnetworkEnc = graphHopper.getEncodingManager().getBooleanEncodedValue(Subnetwork.key(profileName)); - if (hintsMap.has(Parameters.Routing.BLOCK_AREA)) { - GraphEdgeIdFinder.BlockArea blockArea = GraphEdgeIdFinder.createBlockArea(graph, locationIndex, - Collections.singletonList(point.get()), hintsMap, new FiniteWeightFilter(weighting)); - weighting = new BlockAreaWeighting(weighting, blockArea); - } Snap snap = locationIndex.findClosest(point.get().lat, point.get().lon, new DefaultSnapFilter(weighting, inSubnetworkEnc)); if (!snap.isValid()) throw new IllegalArgumentException("Point not found:" + point); @@ -112,10 +103,10 @@ public Response doGet( TraversalMode traversalMode = profile.isTurnCosts() ? EDGE_BASED : NODE_BASED; ShortestPathTree shortestPathTree = new ShortestPathTree(queryGraph, queryGraph.wrapWeighting(weighting), reverseFlow, traversalMode); - if (distanceInMeter.get() > 0) { - shortestPathTree.setDistanceLimit(distanceInMeter.get()); + if (distanceInMeter.orElseThrow(() -> new IllegalArgumentException("query param distance_limit is not a number.")) > 0) { + shortestPathTree.setDistanceLimit(distanceInMeter.getAsLong()); } else { - double limit = timeLimitInSeconds.get() * 1000; + double limit = timeLimitInSeconds.orElseThrow(() -> new IllegalArgumentException("query param time_limit is not a number.")) * 1000d; shortestPathTree.setTimeLimit(limit); } @@ -199,7 +190,7 @@ public Response doGet( if (edge == null) continue; - if (col.equals(Parameters.Details.STREET_NAME)) { + if (col.equals(STREET_NAME)) { sb.append(edge.getName().replaceAll(",", "")); continue; } diff --git a/web-bundle/src/main/java/no/ecc/vectortile/Command.java b/web-bundle/src/main/java/no/ecc/vectortile/Command.java new file mode 100644 index 00000000000..6ef8b704180 --- /dev/null +++ b/web-bundle/src/main/java/no/ecc/vectortile/Command.java @@ -0,0 +1,42 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package no.ecc.vectortile; + +final class Command { + + /** + * MoveTo: 1. (2 parameters follow) + */ + static final int MoveTo = 1; + + /** + * LineTo: 2. (2 parameters follow) + */ + static final int LineTo = 2; + + /** + * ClosePath: 7. (no parameters follow) + */ + static final int ClosePath = 7; + + private Command() { + + } + +} diff --git a/web-bundle/src/main/java/no/ecc/vectortile/Filter.java b/web-bundle/src/main/java/no/ecc/vectortile/Filter.java new file mode 100644 index 00000000000..455557967f3 --- /dev/null +++ b/web-bundle/src/main/java/no/ecc/vectortile/Filter.java @@ -0,0 +1,57 @@ +package no.ecc.vectortile; + +import java.util.Set; + +/** + * A filter which can be passed to a VectorTile decoder to optimize performance by only decoding layers of interest. + */ +public abstract class Filter { + + public abstract boolean include(String layerName); + + public static final Filter ALL = new Filter() { + + @Override + public boolean include(String layerName) { + return true; + } + + }; + + /** + * A filter that only lets a single named layer be decoded. + */ + public static final class Single extends Filter { + + private final String layerName; + + public Single(String layerName) { + this.layerName = layerName; + } + + @Override + public boolean include(String layerName) { + return this.layerName.equals(layerName); + } + + } + + /** + * A filter that only allows the named layers to be decoded. + */ + public static final class Any extends Filter { + + private final Set layerNames; + + public Any(Set layerNames) { + this.layerNames = layerNames; + } + + @Override + public boolean include(String layerName) { + return this.layerNames.contains(layerName); + } + + } + +} diff --git a/web-bundle/src/main/java/no/ecc/vectortile/VectorTileDecoder.java b/web-bundle/src/main/java/no/ecc/vectortile/VectorTileDecoder.java new file mode 100644 index 00000000000..152d11bd024 --- /dev/null +++ b/web-bundle/src/main/java/no/ecc/vectortile/VectorTileDecoder.java @@ -0,0 +1,425 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package no.ecc.vectortile; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.locationtech.jts.algorithm.Area; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Polygon; + +import vector_tile.VectorTile; +import vector_tile.VectorTile.Tile.GeomType; +import vector_tile.VectorTile.Tile.Layer; + +public class VectorTileDecoder { + + private boolean autoScale = true; + + /** + * Get the autoScale setting. + * + * @return autoScale + */ + public boolean isAutoScale() { + return autoScale; + } + + /** + * Set the autoScale setting. + * + * @param autoScale + * when true, the encoder automatically scale and return all coordinates in the 0..255 range. + * when false, the encoder returns all coordinates in the 0..extent-1 range as they are encoded. + * + */ + public void setAutoScale(boolean autoScale) { + this.autoScale = autoScale; + } + + public FeatureIterable decode(byte[] data) throws IOException { + return decode(data, Filter.ALL); + } + + public FeatureIterable decode(byte[] data, String layerName) throws IOException { + return decode(data, new Filter.Single(layerName)); + } + + public FeatureIterable decode(byte[] data, Set layerNames) throws IOException { + return decode(data, new Filter.Any(layerNames)); + } + + public FeatureIterable decode(byte[] data, Filter filter) throws IOException { + VectorTile.Tile tile = VectorTile.Tile.parseFrom(data); + return new FeatureIterable(tile, filter, autoScale); + } + + static int zigZagDecode(int n) { + return ((n >> 1) ^ (-(n & 1))); + } + + static Geometry decodeGeometry(GeometryFactory gf, GeomType geomType, List commands, double scale) { + int x = 0; + int y = 0; + + List> coordsList = new ArrayList>(); + List coords = null; + + int geometryCount = commands.size(); + int length = 0; + int command = 0; + int i = 0; + while (i < geometryCount) { + + if (length <= 0) { + length = commands.get(i++).intValue(); + command = length & ((1 << 3) - 1); + length = length >> 3; + } + + if (length > 0) { + + if (command == Command.MoveTo) { + coords = new ArrayList(); + coordsList.add(coords); + } + + if (command == Command.ClosePath) { + if (geomType != VectorTile.Tile.GeomType.POINT && !coords.isEmpty()) { + coords.add(new Coordinate(coords.get(0))); + } + length--; + continue; + } + + int dx = commands.get(i++).intValue(); + int dy = commands.get(i++).intValue(); + + length--; + + dx = zigZagDecode(dx); + dy = zigZagDecode(dy); + + x = x + dx; + y = y + dy; + + Coordinate coord = new Coordinate(x / scale, y / scale); + coords.add(coord); + } + + } + + Geometry geometry = null; + + switch (geomType) { + case LINESTRING: + List lineStrings = new ArrayList(); + for (List cs : coordsList) { + if (cs.size() <= 1) { + continue; + } + lineStrings.add(gf.createLineString(cs.toArray(new Coordinate[cs.size()]))); + } + if (lineStrings.size() == 1) { + geometry = lineStrings.get(0); + } else if (lineStrings.size() > 1) { + geometry = gf.createMultiLineString(lineStrings.toArray(new LineString[lineStrings.size()])); + } + break; + case POINT: + List allCoords = new ArrayList(); + for (List cs : coordsList) { + allCoords.addAll(cs); + } + if (allCoords.size() == 1) { + geometry = gf.createPoint(allCoords.get(0)); + } else if (allCoords.size() > 1) { + geometry = gf.createMultiPointFromCoords(allCoords.toArray(new Coordinate[allCoords.size()])); + } + break; + case POLYGON: + List> polygonRings = new ArrayList>(); + List ringsForCurrentPolygon = null; + Boolean ccw = null; + for (List cs : coordsList) { + Coordinate[] ringCoords = cs.toArray(new Coordinate[cs.size()]); + double area = Area.ofRingSigned(ringCoords); + if (area == 0) { + continue; + } + boolean thisCcw = area < 0; + if (ccw == null) { + ccw = thisCcw; + } + LinearRing ring = gf.createLinearRing(ringCoords); + if (ccw == thisCcw) { + if (ringsForCurrentPolygon != null) { + polygonRings.add(ringsForCurrentPolygon); + } + ringsForCurrentPolygon = new ArrayList<>(); + } + ringsForCurrentPolygon.add(ring); + } + if (ringsForCurrentPolygon != null) { + polygonRings.add(ringsForCurrentPolygon); + } + + List polygons = new ArrayList(); + for (List rings : polygonRings) { + LinearRing shell = rings.get(0); + LinearRing[] holes = rings.subList(1, rings.size()).toArray(new LinearRing[rings.size() - 1]); + polygons.add(gf.createPolygon(shell, holes)); + } + if (polygons.size() == 1) { + geometry = polygons.get(0); + } + if (polygons.size() > 1) { + geometry = gf.createMultiPolygon(GeometryFactory.toPolygonArray(polygons)); + } + break; + case UNKNOWN: + break; + default: + break; + } + + if (geometry == null) { + geometry = gf.createGeometryCollection(new Geometry[0]); + } + + return geometry; + } + + public static final class FeatureIterable implements Iterable { + + private final VectorTile.Tile tile; + private final Filter filter; + private boolean autoScale; + + public FeatureIterable(VectorTile.Tile tile, Filter filter, boolean autoScale) { + this.tile = tile; + this.filter = filter; + this.autoScale = autoScale; + } + + public Iterator iterator() { + return new FeatureIterator(tile, filter, autoScale); + } + + public List asList() { + List features = new ArrayList(); + for (Feature feature : this) { + features.add(feature); + } + return features; + } + + public Collection getLayerNames() { + Set layerNames = new HashSet(); + for (VectorTile.Tile.Layer layer : tile.getLayersList()) { + layerNames.add(layer.getName()); + } + return Collections.unmodifiableSet(layerNames); + } + + } + + private static final class FeatureIterator implements Iterator { + + private final GeometryFactory gf = new GeometryFactory(); + + private final Filter filter; + + private final Iterator layerIterator; + + private Iterator featureIterator; + + private int extent; + private String layerName; + private double scale; + private boolean autoScale; + + private final List keys = new ArrayList(); + private final List values = new ArrayList(); + + private Feature next; + + public FeatureIterator(VectorTile.Tile tile, Filter filter, boolean autoScale) { + layerIterator = tile.getLayersList().iterator(); + this.filter = filter; + this.autoScale = autoScale; + } + + public boolean hasNext() { + findNext(); + return next != null; + } + + public Feature next() { + findNext(); + if (next == null) { + throw new NoSuchElementException(); + } + Feature n = next; + next = null; + return n; + } + + private void findNext() { + + if (next != null) { + return; + } + + while (true) { + + if (featureIterator == null || !featureIterator.hasNext()) { + if (!layerIterator.hasNext()) { + next = null; + break; + } + + Layer layer = layerIterator.next(); + if (!filter.include(layer.getName())) { + continue; + } + + parseLayer(layer); + continue; + } + + next = parseFeature(featureIterator.next()); + break; + + } + + } + + private void parseLayer(VectorTile.Tile.Layer layer) { + + layerName = layer.getName(); + extent = layer.getExtent(); + scale = autoScale ? extent / 256.0 : 1.0; + + keys.clear(); + keys.addAll(layer.getKeysList()); + values.clear(); + + for (VectorTile.Tile.Value value : layer.getValuesList()) { + if (value.hasBoolValue()) { + values.add(value.getBoolValue()); + } else if (value.hasDoubleValue()) { + values.add(value.getDoubleValue()); + } else if (value.hasFloatValue()) { + values.add(value.getFloatValue()); + } else if (value.hasIntValue()) { + values.add(value.getIntValue()); + } else if (value.hasSintValue()) { + values.add(value.getSintValue()); + } else if (value.hasUintValue()) { + values.add(value.getUintValue()); + } else if (value.hasStringValue()) { + values.add(value.getStringValue()); + } else { + values.add(null); + } + + } + + featureIterator = layer.getFeaturesList().iterator(); + } + + private Feature parseFeature(VectorTile.Tile.Feature feature) { + + int tagsCount = feature.getTagsCount(); + Map attributes = new HashMap(tagsCount / 2); + int tagIdx = 0; + while (tagIdx < feature.getTagsCount()) { + String key = keys.get(feature.getTags(tagIdx++)); + Object value = values.get(feature.getTags(tagIdx++)); + attributes.put(key, value); + } + + Geometry geometry = decodeGeometry(gf, feature.getType(), feature.getGeometryList(), scale); + if (geometry == null) { + geometry = gf.createGeometryCollection(new Geometry[0]); + } + + return new Feature(layerName, extent, geometry, Collections.unmodifiableMap(attributes), feature.getId()); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + public static final class Feature { + + private final String layerName; + private final int extent; + private final long id; + private final Geometry geometry; + private final Map attributes; + + public Feature(String layerName, int extent, Geometry geometry, Map attributes, long id) { + this.layerName = layerName; + this.extent = extent; + this.geometry = geometry; + this.attributes = attributes; + this.id = id; + } + + public String getLayerName() { + return layerName; + } + + public long getId() { + return id; + } + + public int getExtent() { + return extent; + } + + public Geometry getGeometry() { + return geometry; + } + + public Map getAttributes() { + return attributes; + } + + } + +} diff --git a/web-bundle/src/main/java/no/ecc/vectortile/VectorTileEncoder.java b/web-bundle/src/main/java/no/ecc/vectortile/VectorTileEncoder.java new file mode 100644 index 00000000000..63464b273fe --- /dev/null +++ b/web-bundle/src/main/java/no/ecc/vectortile/VectorTileEncoder.java @@ -0,0 +1,644 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package no.ecc.vectortile; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.locationtech.jts.algorithm.Area; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.MultiLineString; +import org.locationtech.jts.geom.MultiPoint; +import org.locationtech.jts.geom.MultiPolygon; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.TopologyException; +import org.locationtech.jts.geom.prep.PreparedGeometry; +import org.locationtech.jts.geom.prep.PreparedGeometryFactory; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jts.simplify.DouglasPeuckerSimplifier; +import org.locationtech.jts.simplify.TopologyPreservingSimplifier; + +import vector_tile.VectorTile; +import vector_tile.VectorTile.Tile.GeomType; + +/** + * This is a copy of https://github.com/ElectronicChartCentre/java-vector-tile/commit/15e2e9b127729a00c52ced3a11fd1e9a45b462b1 + * We use this copy because we want to avoid the non-standard no.ecc Maven repository + */ +public class VectorTileEncoder { + + private final Map layers = new LinkedHashMap(); + + private final int extent; + + private final double minimumLength; + + private final double minimumArea; + + protected final Geometry clipGeometry; + + protected final Envelope clipEnvelope; + + protected final PreparedGeometry clipGeometryPrepared; + + private final boolean autoScale; + + private long autoincrement; + + private final boolean autoincrementIds; + + private final double simplificationDistanceTolerance; + + private final GeometryFactory gf = new GeometryFactory(); + + /** + * Create a {@link VectorTileEncoder} with the default extent of 4096 and + * clip buffer of 8. + */ + public VectorTileEncoder() { + this(4096, 8, true); + } + + /** + * Create a {@link VectorTileEncoder} with the given extent and a clip + * buffer of 8. + * + * @param extent a int to specify vector tile extent. 4096 is a good value. + */ + public VectorTileEncoder(int extent) { + this(extent, 8, true); + } + + public VectorTileEncoder(int extent, int clipBuffer, boolean autoScale) { + this(extent, clipBuffer, autoScale, false); + } + + public VectorTileEncoder(int extent, int clipBuffer, boolean autoScale, boolean autoincrementIds) { + this(extent, clipBuffer, autoScale, autoincrementIds, -1.0); + } + + /** + * Create a {@link VectorTileEncoder} with the given extent value. + *

+ * The extent value control how detailed the coordinates are encoded in the + * vector tile. 4096 is a good default, 256 can be used to reduce density. + *

+ * The clip buffer value control how large the clipping area is outside of the + * tile for geometries. 0 means that the clipping is done at the tile border. 8 + * is a good default. + * + * @param extent + * a int with extent value. 4096 is a good value. + * @param clipBuffer + * a int with clip buffer size for geometries. 8 is a good value. + * @param autoScale + * when true, the encoder expects coordinates in the 0..255 range and + * will scale them automatically to the 0..extent-1 range before + * encoding. when false, the encoder expects coordinates in the + * 0..extent-1 range. + * @param autoincrementIds + * when true the vector tile feature id is auto incremented when using + * {@link #addFeature(String, Map, Geometry)} + * @param simplificationDistanceTolerance + * a positive double representing the distance tolerance to be used + * for non-points before (optional) scaling and encoding. A value + * <=0 will prevent simplifying geometry. 0.1 seems to be a good + * value when {@code autoScale} is turned on. + */ + public VectorTileEncoder(int extent, int clipBuffer, boolean autoScale, boolean autoincrementIds, double simplificationDistanceTolerance) { + this.extent = extent; + this.autoScale = autoScale; + this.minimumLength = autoScale ? (256.0 / extent) : 1.0; + this.minimumArea = this.minimumLength * this.minimumLength; + this.autoincrementIds = autoincrementIds; + this.autoincrement = 1; + this.simplificationDistanceTolerance = simplificationDistanceTolerance; + + final int size = autoScale ? 256 : extent; + clipGeometry = createTileEnvelope(clipBuffer, size); + clipEnvelope = clipGeometry.getEnvelopeInternal(); + clipGeometryPrepared = PreparedGeometryFactory.prepare(clipGeometry); + } + + private static Geometry createTileEnvelope(int buffer, int size) { + Coordinate[] coords = new Coordinate[5]; + coords[0] = new Coordinate(0 - buffer, size + buffer); + coords[1] = new Coordinate(size + buffer, size + buffer); + coords[2] = new Coordinate(size + buffer, 0 - buffer); + coords[3] = new Coordinate(0 - buffer, 0 - buffer); + coords[4] = coords[0]; + return new GeometryFactory().createPolygon(coords); + } + + public void addFeature(String layerName, Map attributes, Geometry geometry) { + this.addFeature(layerName, attributes, geometry, this.autoincrementIds ? this.autoincrement++ : -1); + } + + /** + * Add a feature with layer name (typically feature type name), some attributes + * and a Geometry. The Geometry must be in "pixel" space 0,0 upper left and + * 256,256 lower right. + *

+ * For optimization, geometries will be clipped and simplified. Features with + * geometries outside of the tile will be skipped. + * + * @param layerName a {@link String} with the vector tile layer name. + * @param attributes a {@link Map} with the vector tile feature attributes. + * @param geometry a {@link Geometry} for the vector tile feature. + * @param id a long with the vector tile feature id field. + */ + public void addFeature(String layerName, Map attributes, Geometry geometry, long id) { + + // skip small Polygon/LineString. + if (geometry instanceof MultiPolygon && geometry.getArea() < minimumArea) { + return; + } + if (geometry instanceof Polygon && geometry.getArea() < minimumArea) { + return; + } + if (geometry instanceof LineString && geometry.getLength() < minimumLength) { + return; + } + + // special handling of GeometryCollection. subclasses are not handled here. + if (geometry.getClass().equals(GeometryCollection.class)) { + for (int i = 0; i < geometry.getNumGeometries(); i++) { + Geometry subGeometry = geometry.getGeometryN(i); + // keeping the id. any better suggestion? + addFeature(layerName, attributes, subGeometry, id); + } + return; + } + + // About to simplify and clip. Looks like simplification before clipping is + // faster than clipping before simplification + + // simplify non-points + if (simplificationDistanceTolerance > 0.0 && !(geometry instanceof Point)) { + if (geometry instanceof LineString || geometry instanceof MultiLineString) { + geometry = DouglasPeuckerSimplifier.simplify(geometry, simplificationDistanceTolerance); + } else if (geometry instanceof Polygon || geometry instanceof MultiPolygon) { + Geometry simplified = DouglasPeuckerSimplifier.simplify(geometry, simplificationDistanceTolerance); + // extra check to prevent polygon converted to line + if (simplified instanceof Polygon || simplified instanceof MultiPolygon) { + geometry = simplified; + } else { + geometry = TopologyPreservingSimplifier.simplify(geometry, simplificationDistanceTolerance); + } + } else { + geometry = TopologyPreservingSimplifier.simplify(geometry, simplificationDistanceTolerance); + } + } + + // clip geometry + if (geometry instanceof Point) { + if (!clipCovers(geometry)) { + return; + } + } else { + geometry = clipGeometry(geometry); + } + + // no need to add empty geometry + if (geometry == null || geometry.isEmpty()) { + return; + } + + Layer layer = layers.get(layerName); + if (layer == null) { + layer = new Layer(); + layers.put(layerName, layer); + } + + Feature feature = new Feature(); + feature.geometry = geometry; + feature.id = id; + this.autoincrement = Math.max(this.autoincrement, id + 1); + + for (Map.Entry e : attributes.entrySet()) { + // skip attribute without value + if (e.getValue() == null) { + continue; + } + feature.tags.add(layer.key(e.getKey())); + feature.tags.add(layer.value(e.getValue())); + } + + layer.features.add(feature); + } + + /** + * A short circuit clip to the tile extent (tile boundary + buffer) for + * points to improve performance. This method can be overridden to change + * clipping behavior. See also {@link #clipGeometry(Geometry)}. + * + * @param geom a {@link Geometry} to check for "covers" + * @return a boolean true when the current clip geometry covers the given geom. + */ + protected boolean clipCovers(Geometry geom) { + if (geom instanceof Point) { + Point p = (Point) geom; + return clipGeometry.getEnvelopeInternal().covers(p.getCoordinate()); + } + return clipEnvelope.covers(geom.getEnvelopeInternal()); + } + + /** + * Clip geometry according to buffer given at construct time. This method + * can be overridden to change clipping behavior. See also + * {@link #clipCovers(Geometry)}. + * + * @param geometry a {@link Geometry} to check for intersection with the current clip geometry + * @return a boolean true when current clip geometry intersects with the given geometry. + */ + protected Geometry clipGeometry(Geometry geometry) { + try { + if (clipEnvelope.contains(geometry.getEnvelopeInternal())) { + return geometry; + } + + Geometry original = geometry; + geometry = clipGeometry.intersection(original); + + // some times a intersection is returned as an empty geometry. + // going via wkt fixes the problem. + if (geometry.isEmpty() && clipGeometryPrepared.intersects(original)) { + Geometry originalViaWkt = new WKTReader().read(original.toText()); + geometry = clipGeometry.intersection(originalViaWkt); + } + + return geometry; + } catch (TopologyException e) { + // could not intersect. original geometry will be used instead. + return geometry; + } catch (ParseException e1) { + // could not encode/decode WKT. original geometry will be used + // instead. + return geometry; + } + } + + /** + * @return a byte array with the vector tile + */ + public byte[] encode() { + + VectorTile.Tile.Builder tile = VectorTile.Tile.newBuilder(); + + for (Map.Entry e : layers.entrySet()) { + String layerName = e.getKey(); + Layer layer = e.getValue(); + + VectorTile.Tile.Layer.Builder tileLayer = VectorTile.Tile.Layer.newBuilder(); + + tileLayer.setVersion(2); + tileLayer.setName(layerName); + + tileLayer.addAllKeys(layer.keys()); + + for (Object value : layer.values()) { + VectorTile.Tile.Value.Builder tileValue = VectorTile.Tile.Value.newBuilder(); + if (value instanceof String) { + tileValue.setStringValue((String) value); + } else if (value instanceof Integer) { + tileValue.setSintValue(((Integer) value).intValue()); + } else if (value instanceof Long) { + tileValue.setSintValue(((Long) value).longValue()); + } else if (value instanceof Float) { + tileValue.setFloatValue(((Float) value).floatValue()); + } else if (value instanceof Double) { + tileValue.setDoubleValue(((Double) value).doubleValue()); + } else if (value instanceof BigDecimal) { + tileValue.setStringValue(value.toString()); + } else if (value instanceof Number) { + tileValue.setDoubleValue(((Number) value).doubleValue()); + } else if (value instanceof Boolean) { + tileValue.setBoolValue(((Boolean) value).booleanValue()); + } else { + tileValue.setStringValue(value.toString()); + } + tileLayer.addValues(tileValue.build()); + } + + tileLayer.setExtent(extent); + + for (Feature feature : layer.features) { + + Geometry geometry = feature.geometry; + + VectorTile.Tile.Feature.Builder featureBuilder = VectorTile.Tile.Feature.newBuilder(); + + featureBuilder.addAllTags(feature.tags); + if (feature.id >= 0) { + featureBuilder.setId(feature.id); + } + + GeomType geomType = toGeomType(geometry); + x = 0; + y = 0; + List commands = commands(geometry); + + // skip features with no geometry commands + if (commands.isEmpty()) { + continue; + } + + // Extra step to parse and check validity and try to repair. Probably expensive. + if (simplificationDistanceTolerance > 0.0 && geomType == GeomType.POLYGON) { + double scale = autoScale ? (extent / 256.0) : 1.0; + Geometry decodedGeometry = VectorTileDecoder.decodeGeometry(gf, geomType, commands, scale); + if (!isValid(decodedGeometry)) { + // Invalid. Try more simplification and without preserving topology. + geometry = DouglasPeuckerSimplifier.simplify(geometry, simplificationDistanceTolerance * 2.0); + if (geometry.isEmpty()) { + continue; + } + geomType = toGeomType(geometry); + x = 0; + y = 0; + commands = commands(geometry); + } + } + + featureBuilder.setType(geomType); + featureBuilder.addAllGeometry(commands); + + tileLayer.addFeatures(featureBuilder.build()); + } + + tile.addLayers(tileLayer.build()); + + } + + return tile.build().toByteArray(); + } + + private static final boolean isValid(Geometry geometry) { + try { + return geometry.isValid(); + } catch (RuntimeException e) { + return false; + } + } + + static VectorTile.Tile.GeomType toGeomType(Geometry geometry) { + if (geometry instanceof Point) { + return VectorTile.Tile.GeomType.POINT; + } + if (geometry instanceof MultiPoint) { + return VectorTile.Tile.GeomType.POINT; + } + if (geometry instanceof LineString) { + return VectorTile.Tile.GeomType.LINESTRING; + } + if (geometry instanceof MultiLineString) { + return VectorTile.Tile.GeomType.LINESTRING; + } + if (geometry instanceof Polygon) { + return VectorTile.Tile.GeomType.POLYGON; + } + if (geometry instanceof MultiPolygon) { + return VectorTile.Tile.GeomType.POLYGON; + } + return VectorTile.Tile.GeomType.UNKNOWN; + } + + static boolean shouldClosePath(Geometry geometry) { + return (geometry instanceof Polygon) || (geometry instanceof LinearRing); + } + + List commands(Geometry geometry) { + + if (geometry instanceof MultiLineString) { + return commands((MultiLineString) geometry); + } + if (geometry instanceof Polygon) { + return commands((Polygon) geometry); + } + if (geometry instanceof MultiPolygon) { + return commands((MultiPolygon) geometry); + } + + return commands(geometry.getCoordinates(), shouldClosePath(geometry), geometry instanceof MultiPoint); + } + + List commands(MultiLineString mls) { + List commands = new ArrayList(); + for (int i = 0; i < mls.getNumGeometries(); i++) { + final List geomCommands = + commands(mls.getGeometryN(i).getCoordinates(), false); + if (geomCommands.size() > 3) { + // if the geometry consists of all identical points (after Math.round()) commands + // returns a single move_to command, which is not valid according to the vector tile + // specifications. + // (https://github.com/mapbox/vector-tile-spec/tree/master/2.1#4343-linestring-geometry-type) + commands.addAll(geomCommands); + } + } + return commands; + } + + List commands(MultiPolygon mp) { + List commands = new ArrayList(); + for (int i = 0; i < mp.getNumGeometries(); i++) { + Polygon polygon = (Polygon) mp.getGeometryN(i); + commands.addAll(commands(polygon)); + } + return commands; + } + + List commands(Polygon polygon) { + List commands = new ArrayList(); + + // According to the vector tile specification, the exterior ring of a polygon + // must be in clockwise order, while the interior ring in counter-clockwise order. + // In the tile coordinate system, Y axis is positive down. + // + // However, in geographic coordinate system, Y axis is positive up. + // Therefore, we must reverse the coordinates. + // So, the code below will make sure that exterior ring is in counter-clockwise order + // and interior ring in clockwise order. + LineString exteriorRing = polygon.getExteriorRing(); + if (Area.ofRingSigned(exteriorRing.getCoordinates()) > 0) { + exteriorRing = exteriorRing.reverse(); + } + commands.addAll(commands(exteriorRing.getCoordinates(), true)); + + for (int i = 0; i < polygon.getNumInteriorRing(); i++) { + LineString interiorRing = polygon.getInteriorRingN(i); + if (Area.ofRingSigned(interiorRing.getCoordinates()) < 0) { + interiorRing = interiorRing.reverse(); + } + commands.addAll(commands(interiorRing.getCoordinates(), true)); + } + return commands; + } + + private int x = 0; + private int y = 0; + + /** + * // // // Ex.: MoveTo(3, 6), LineTo(8, 12), LineTo(20, 34), ClosePath // + * Encoded as: [ 9 3 6 18 5 6 12 22 15 ] // == command type 7 (ClosePath), + * length 1 // ===== relative LineTo(+12, +22) == LineTo(20, 34) // === + * relative LineTo(+5, +6) == LineTo(8, 12) // == [00010 010] = command type + * 2 (LineTo), length 2 // === relative MoveTo(+3, +6) // == [00001 001] = + * command type 1 (MoveTo), length 1 // Commands are encoded as uint32 + * varints, vertex parameters are // encoded as sint32 varints (zigzag). + * Vertex parameters are // also encoded as deltas to the previous position. + * The original // position is (0,0) + * + * @param cs + * @return + */ + List commands(Coordinate[] cs, boolean closePathAtEnd) { + return commands(cs, closePathAtEnd, false); + } + + List commands(Coordinate[] cs, boolean closePathAtEnd, boolean multiPoint) { + + if (cs.length == 0) { + return Collections.emptyList(); + } + + List r = new ArrayList(); + + int lineToIndex = 0; + int lineToLength = 0; + + double scale = autoScale ? (extent / 256.0) : 1.0; + + for (int i = 0; i < cs.length; i++) { + Coordinate c = cs[i]; + + if (i == 0) { + r.add(commandAndLength(Command.MoveTo, multiPoint ? cs.length : 1)); + } + + int _x = (int) Math.round(c.x * scale); + int _y = (int) Math.round(c.y * scale); + + // prevent point equal to the previous + if (i > 0 && _x == x && _y == y) { + lineToLength--; + continue; + } + + // prevent double closing + if (closePathAtEnd && cs.length > 1 && i == (cs.length - 1) && cs[0].equals(c)) { + lineToLength--; + continue; + } + + // delta, then zigzag + r.add(zigZagEncode(_x - x)); + r.add(zigZagEncode(_y - y)); + + x = _x; + y = _y; + + if (i == 0 && cs.length > 1 && !multiPoint) { + // can length be too long? + lineToIndex = r.size(); + lineToLength = cs.length - 1; + r.add(commandAndLength(Command.LineTo, lineToLength)); + } + + } + + // update LineTo length + if (lineToIndex > 0) { + if (lineToLength == 0) { + // remove empty LineTo + r.remove(lineToIndex); + } else { + // update LineTo with new length + r.set(lineToIndex, commandAndLength(Command.LineTo, lineToLength)); + } + } + + if (closePathAtEnd) { + r.add(commandAndLength(Command.ClosePath, 1)); + } + + return r; + } + + static int commandAndLength(int command, int repeat) { + return repeat << 3 | command; + } + + static int zigZagEncode(int n) { + // https://developers.google.com/protocol-buffers/docs/encoding#types + return (n << 1) ^ (n >> 31); + } + + private static final class Layer { + + final List features = new ArrayList(); + + private final Map keys = new LinkedHashMap(); + private final Map values = new LinkedHashMap(); + + public Integer key(String key) { + Integer i = keys.get(key); + if (i == null) { + i = Integer.valueOf(keys.size()); + keys.put(key, i); + } + return i; + } + + public List keys() { + return new ArrayList(keys.keySet()); + } + + public Integer value(Object value) { + Integer i = values.get(value); + if (i == null) { + i = Integer.valueOf(values.size()); + values.put(value, i); + } + return i; + } + + public List values() { + return Collections.unmodifiableList(new ArrayList(values.keySet())); + } + } + + private static final class Feature { + long id; + Geometry geometry; + final List tags = new ArrayList(); + + } +} diff --git a/web-bundle/src/main/java/vector_tile/VectorTile.java b/web-bundle/src/main/java/vector_tile/VectorTile.java new file mode 100644 index 00000000000..280c45d3f3d --- /dev/null +++ b/web-bundle/src/main/java/vector_tile/VectorTile.java @@ -0,0 +1,5576 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: src/main/resources/vector_tile.proto + +package vector_tile; + +public final class VectorTile { + private VectorTile() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface TileOrBuilder extends + // @@protoc_insertion_point(interface_extends:vector_tile.Tile) + com.google.protobuf.GeneratedMessageV3. + ExtendableMessageOrBuilder { + + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + java.util.List + getLayersList(); + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + vector_tile.VectorTile.Tile.Layer getLayers(int index); + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + int getLayersCount(); + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + java.util.List + getLayersOrBuilderList(); + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + vector_tile.VectorTile.Tile.LayerOrBuilder getLayersOrBuilder( + int index); + } + /** + * Protobuf type {@code vector_tile.Tile} + */ + public static final class Tile extends + com.google.protobuf.GeneratedMessageV3.ExtendableMessage< + Tile> implements + // @@protoc_insertion_point(message_implements:vector_tile.Tile) + TileOrBuilder { + private static final long serialVersionUID = 0L; + // Use Tile.newBuilder() to construct. + private Tile(com.google.protobuf.GeneratedMessageV3.ExtendableBuilder builder) { + super(builder); + } + private Tile() { + layers_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Tile( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 26: { + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + layers_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + layers_.add( + input.readMessage(vector_tile.VectorTile.Tile.Layer.PARSER, extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + layers_ = java.util.Collections.unmodifiableList(layers_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_fieldAccessorTable + .ensureFieldAccessorsInitialized( + vector_tile.VectorTile.Tile.class, vector_tile.VectorTile.Tile.Builder.class); + } + + /** + *
+     * GeomType is described in section 4.3.4 of the specification
+     * 
+ * + * Protobuf enum {@code vector_tile.Tile.GeomType} + */ + public enum GeomType + implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0), + /** + * POINT = 1; + */ + POINT(1), + /** + * LINESTRING = 2; + */ + LINESTRING(2), + /** + * POLYGON = 3; + */ + POLYGON(3), + ; + + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; + /** + * POINT = 1; + */ + public static final int POINT_VALUE = 1; + /** + * LINESTRING = 2; + */ + public static final int LINESTRING_VALUE = 2; + /** + * POLYGON = 3; + */ + public static final int POLYGON_VALUE = 3; + + + public final int getNumber() { + return value; + } + + /** + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static GeomType valueOf(int value) { + return forNumber(value); + } + + public static GeomType forNumber(int value) { + switch (value) { + case 0: return UNKNOWN; + case 1: return POINT; + case 2: return LINESTRING; + case 3: return POLYGON; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + GeomType> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public GeomType findValueByNumber(int number) { + return GeomType.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return vector_tile.VectorTile.Tile.getDescriptor().getEnumTypes().get(0); + } + + private static final GeomType[] VALUES = values(); + + public static GeomType valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private GeomType(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:vector_tile.Tile.GeomType) + } + + public interface ValueOrBuilder extends + // @@protoc_insertion_point(interface_extends:vector_tile.Tile.Value) + com.google.protobuf.GeneratedMessageV3. + ExtendableMessageOrBuilder { + + /** + *
+       * Exactly one of these values must be present in a valid message
+       * 
+ * + * optional string string_value = 1; + */ + boolean hasStringValue(); + /** + *
+       * Exactly one of these values must be present in a valid message
+       * 
+ * + * optional string string_value = 1; + */ + java.lang.String getStringValue(); + /** + *
+       * Exactly one of these values must be present in a valid message
+       * 
+ * + * optional string string_value = 1; + */ + com.google.protobuf.ByteString + getStringValueBytes(); + + /** + * optional float float_value = 2; + */ + boolean hasFloatValue(); + /** + * optional float float_value = 2; + */ + float getFloatValue(); + + /** + * optional double double_value = 3; + */ + boolean hasDoubleValue(); + /** + * optional double double_value = 3; + */ + double getDoubleValue(); + + /** + * optional int64 int_value = 4; + */ + boolean hasIntValue(); + /** + * optional int64 int_value = 4; + */ + long getIntValue(); + + /** + * optional uint64 uint_value = 5; + */ + boolean hasUintValue(); + /** + * optional uint64 uint_value = 5; + */ + long getUintValue(); + + /** + * optional sint64 sint_value = 6; + */ + boolean hasSintValue(); + /** + * optional sint64 sint_value = 6; + */ + long getSintValue(); + + /** + * optional bool bool_value = 7; + */ + boolean hasBoolValue(); + /** + * optional bool bool_value = 7; + */ + boolean getBoolValue(); + } + /** + *
+     * Variant type encoding
+     * The use of values is described in section 4.1 of the specification
+     * 
+ * + * Protobuf type {@code vector_tile.Tile.Value} + */ + public static final class Value extends + com.google.protobuf.GeneratedMessageV3.ExtendableMessage< + Value> implements + // @@protoc_insertion_point(message_implements:vector_tile.Tile.Value) + ValueOrBuilder { + private static final long serialVersionUID = 0L; + // Use Value.newBuilder() to construct. + private Value(com.google.protobuf.GeneratedMessageV3.ExtendableBuilder builder) { + super(builder); + } + private Value() { + stringValue_ = ""; + floatValue_ = 0F; + doubleValue_ = 0D; + intValue_ = 0L; + uintValue_ = 0L; + sintValue_ = 0L; + boolValue_ = false; + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Value( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + stringValue_ = bs; + break; + } + case 21: { + bitField0_ |= 0x00000002; + floatValue_ = input.readFloat(); + break; + } + case 25: { + bitField0_ |= 0x00000004; + doubleValue_ = input.readDouble(); + break; + } + case 32: { + bitField0_ |= 0x00000008; + intValue_ = input.readInt64(); + break; + } + case 40: { + bitField0_ |= 0x00000010; + uintValue_ = input.readUInt64(); + break; + } + case 48: { + bitField0_ |= 0x00000020; + sintValue_ = input.readSInt64(); + break; + } + case 56: { + bitField0_ |= 0x00000040; + boolValue_ = input.readBool(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Value_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Value_fieldAccessorTable + .ensureFieldAccessorsInitialized( + vector_tile.VectorTile.Tile.Value.class, vector_tile.VectorTile.Tile.Value.Builder.class); + } + + private int bitField0_; + public static final int STRING_VALUE_FIELD_NUMBER = 1; + private volatile java.lang.Object stringValue_; + /** + *
+       * Exactly one of these values must be present in a valid message
+       * 
+ * + * optional string string_value = 1; + */ + public boolean hasStringValue() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + *
+       * Exactly one of these values must be present in a valid message
+       * 
+ * + * optional string string_value = 1; + */ + public java.lang.String getStringValue() { + java.lang.Object ref = stringValue_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + stringValue_ = s; + } + return s; + } + } + /** + *
+       * Exactly one of these values must be present in a valid message
+       * 
+ * + * optional string string_value = 1; + */ + public com.google.protobuf.ByteString + getStringValueBytes() { + java.lang.Object ref = stringValue_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + stringValue_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int FLOAT_VALUE_FIELD_NUMBER = 2; + private float floatValue_; + /** + * optional float float_value = 2; + */ + public boolean hasFloatValue() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional float float_value = 2; + */ + public float getFloatValue() { + return floatValue_; + } + + public static final int DOUBLE_VALUE_FIELD_NUMBER = 3; + private double doubleValue_; + /** + * optional double double_value = 3; + */ + public boolean hasDoubleValue() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional double double_value = 3; + */ + public double getDoubleValue() { + return doubleValue_; + } + + public static final int INT_VALUE_FIELD_NUMBER = 4; + private long intValue_; + /** + * optional int64 int_value = 4; + */ + public boolean hasIntValue() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional int64 int_value = 4; + */ + public long getIntValue() { + return intValue_; + } + + public static final int UINT_VALUE_FIELD_NUMBER = 5; + private long uintValue_; + /** + * optional uint64 uint_value = 5; + */ + public boolean hasUintValue() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint64 uint_value = 5; + */ + public long getUintValue() { + return uintValue_; + } + + public static final int SINT_VALUE_FIELD_NUMBER = 6; + private long sintValue_; + /** + * optional sint64 sint_value = 6; + */ + public boolean hasSintValue() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional sint64 sint_value = 6; + */ + public long getSintValue() { + return sintValue_; + } + + public static final int BOOL_VALUE_FIELD_NUMBER = 7; + private boolean boolValue_; + /** + * optional bool bool_value = 7; + */ + public boolean hasBoolValue() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bool bool_value = 7; + */ + public boolean getBoolValue() { + return boolValue_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!extensionsAreInitialized()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + com.google.protobuf.GeneratedMessageV3 + .ExtendableMessage.ExtensionWriter + extensionWriter = newExtensionWriter(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, stringValue_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeFloat(2, floatValue_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeDouble(3, doubleValue_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeInt64(4, intValue_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeUInt64(5, uintValue_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeSInt64(6, sintValue_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeBool(7, boolValue_); + } + extensionWriter.writeUntil(536870912, output); + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, stringValue_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeFloatSize(2, floatValue_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeDoubleSize(3, doubleValue_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(4, intValue_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(5, uintValue_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeSInt64Size(6, sintValue_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(7, boolValue_); + } + size += extensionsSerializedSize(); + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof vector_tile.VectorTile.Tile.Value)) { + return super.equals(obj); + } + vector_tile.VectorTile.Tile.Value other = (vector_tile.VectorTile.Tile.Value) obj; + + boolean result = true; + result = result && (hasStringValue() == other.hasStringValue()); + if (hasStringValue()) { + result = result && getStringValue() + .equals(other.getStringValue()); + } + result = result && (hasFloatValue() == other.hasFloatValue()); + if (hasFloatValue()) { + result = result && ( + java.lang.Float.floatToIntBits(getFloatValue()) + == java.lang.Float.floatToIntBits( + other.getFloatValue())); + } + result = result && (hasDoubleValue() == other.hasDoubleValue()); + if (hasDoubleValue()) { + result = result && ( + java.lang.Double.doubleToLongBits(getDoubleValue()) + == java.lang.Double.doubleToLongBits( + other.getDoubleValue())); + } + result = result && (hasIntValue() == other.hasIntValue()); + if (hasIntValue()) { + result = result && (getIntValue() + == other.getIntValue()); + } + result = result && (hasUintValue() == other.hasUintValue()); + if (hasUintValue()) { + result = result && (getUintValue() + == other.getUintValue()); + } + result = result && (hasSintValue() == other.hasSintValue()); + if (hasSintValue()) { + result = result && (getSintValue() + == other.getSintValue()); + } + result = result && (hasBoolValue() == other.hasBoolValue()); + if (hasBoolValue()) { + result = result && (getBoolValue() + == other.getBoolValue()); + } + result = result && unknownFields.equals(other.unknownFields); + result = result && + getExtensionFields().equals(other.getExtensionFields()); + return result; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasStringValue()) { + hash = (37 * hash) + STRING_VALUE_FIELD_NUMBER; + hash = (53 * hash) + getStringValue().hashCode(); + } + if (hasFloatValue()) { + hash = (37 * hash) + FLOAT_VALUE_FIELD_NUMBER; + hash = (53 * hash) + java.lang.Float.floatToIntBits( + getFloatValue()); + } + if (hasDoubleValue()) { + hash = (37 * hash) + DOUBLE_VALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + java.lang.Double.doubleToLongBits(getDoubleValue())); + } + if (hasIntValue()) { + hash = (37 * hash) + INT_VALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getIntValue()); + } + if (hasUintValue()) { + hash = (37 * hash) + UINT_VALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getUintValue()); + } + if (hasSintValue()) { + hash = (37 * hash) + SINT_VALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getSintValue()); + } + if (hasBoolValue()) { + hash = (37 * hash) + BOOL_VALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getBoolValue()); + } + hash = hashFields(hash, getExtensionFields()); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static vector_tile.VectorTile.Tile.Value parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Value parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Value parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Value parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Value parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Value parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Value parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Value parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Value parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Value parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Value parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Value parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(vector_tile.VectorTile.Tile.Value prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+       * Variant type encoding
+       * The use of values is described in section 4.1 of the specification
+       * 
+ * + * Protobuf type {@code vector_tile.Tile.Value} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.ExtendableBuilder< + vector_tile.VectorTile.Tile.Value, Builder> implements + // @@protoc_insertion_point(builder_implements:vector_tile.Tile.Value) + vector_tile.VectorTile.Tile.ValueOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Value_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Value_fieldAccessorTable + .ensureFieldAccessorsInitialized( + vector_tile.VectorTile.Tile.Value.class, vector_tile.VectorTile.Tile.Value.Builder.class); + } + + // Construct using vector_tile.VectorTile.Tile.Value.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + stringValue_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + floatValue_ = 0F; + bitField0_ = (bitField0_ & ~0x00000002); + doubleValue_ = 0D; + bitField0_ = (bitField0_ & ~0x00000004); + intValue_ = 0L; + bitField0_ = (bitField0_ & ~0x00000008); + uintValue_ = 0L; + bitField0_ = (bitField0_ & ~0x00000010); + sintValue_ = 0L; + bitField0_ = (bitField0_ & ~0x00000020); + boolValue_ = false; + bitField0_ = (bitField0_ & ~0x00000040); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Value_descriptor; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Value getDefaultInstanceForType() { + return vector_tile.VectorTile.Tile.Value.getDefaultInstance(); + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Value build() { + vector_tile.VectorTile.Tile.Value result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Value buildPartial() { + vector_tile.VectorTile.Tile.Value result = new vector_tile.VectorTile.Tile.Value(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.stringValue_ = stringValue_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.floatValue_ = floatValue_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.doubleValue_ = doubleValue_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.intValue_ = intValue_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.uintValue_ = uintValue_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.sintValue_ = sintValue_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.boolValue_ = boolValue_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return (Builder) super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return (Builder) super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return (Builder) super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return (Builder) super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder setExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile.Value, Type> extension, + Type value) { + return (Builder) super.setExtension(extension, value); + } + @java.lang.Override + public Builder setExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile.Value, java.util.List> extension, + int index, Type value) { + return (Builder) super.setExtension(extension, index, value); + } + @java.lang.Override + public Builder addExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile.Value, java.util.List> extension, + Type value) { + return (Builder) super.addExtension(extension, value); + } + @java.lang.Override + public Builder clearExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile.Value, ?> extension) { + return (Builder) super.clearExtension(extension); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof vector_tile.VectorTile.Tile.Value) { + return mergeFrom((vector_tile.VectorTile.Tile.Value)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(vector_tile.VectorTile.Tile.Value other) { + if (other == vector_tile.VectorTile.Tile.Value.getDefaultInstance()) return this; + if (other.hasStringValue()) { + bitField0_ |= 0x00000001; + stringValue_ = other.stringValue_; + onChanged(); + } + if (other.hasFloatValue()) { + setFloatValue(other.getFloatValue()); + } + if (other.hasDoubleValue()) { + setDoubleValue(other.getDoubleValue()); + } + if (other.hasIntValue()) { + setIntValue(other.getIntValue()); + } + if (other.hasUintValue()) { + setUintValue(other.getUintValue()); + } + if (other.hasSintValue()) { + setSintValue(other.getSintValue()); + } + if (other.hasBoolValue()) { + setBoolValue(other.getBoolValue()); + } + this.mergeExtensionFields(other); + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!extensionsAreInitialized()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + vector_tile.VectorTile.Tile.Value parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (vector_tile.VectorTile.Tile.Value) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object stringValue_ = ""; + /** + *
+         * Exactly one of these values must be present in a valid message
+         * 
+ * + * optional string string_value = 1; + */ + public boolean hasStringValue() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + *
+         * Exactly one of these values must be present in a valid message
+         * 
+ * + * optional string string_value = 1; + */ + public java.lang.String getStringValue() { + java.lang.Object ref = stringValue_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + stringValue_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+         * Exactly one of these values must be present in a valid message
+         * 
+ * + * optional string string_value = 1; + */ + public com.google.protobuf.ByteString + getStringValueBytes() { + java.lang.Object ref = stringValue_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + stringValue_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+         * Exactly one of these values must be present in a valid message
+         * 
+ * + * optional string string_value = 1; + */ + public Builder setStringValue( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + stringValue_ = value; + onChanged(); + return this; + } + /** + *
+         * Exactly one of these values must be present in a valid message
+         * 
+ * + * optional string string_value = 1; + */ + public Builder clearStringValue() { + bitField0_ = (bitField0_ & ~0x00000001); + stringValue_ = getDefaultInstance().getStringValue(); + onChanged(); + return this; + } + /** + *
+         * Exactly one of these values must be present in a valid message
+         * 
+ * + * optional string string_value = 1; + */ + public Builder setStringValueBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + stringValue_ = value; + onChanged(); + return this; + } + + private float floatValue_ ; + /** + * optional float float_value = 2; + */ + public boolean hasFloatValue() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional float float_value = 2; + */ + public float getFloatValue() { + return floatValue_; + } + /** + * optional float float_value = 2; + */ + public Builder setFloatValue(float value) { + bitField0_ |= 0x00000002; + floatValue_ = value; + onChanged(); + return this; + } + /** + * optional float float_value = 2; + */ + public Builder clearFloatValue() { + bitField0_ = (bitField0_ & ~0x00000002); + floatValue_ = 0F; + onChanged(); + return this; + } + + private double doubleValue_ ; + /** + * optional double double_value = 3; + */ + public boolean hasDoubleValue() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional double double_value = 3; + */ + public double getDoubleValue() { + return doubleValue_; + } + /** + * optional double double_value = 3; + */ + public Builder setDoubleValue(double value) { + bitField0_ |= 0x00000004; + doubleValue_ = value; + onChanged(); + return this; + } + /** + * optional double double_value = 3; + */ + public Builder clearDoubleValue() { + bitField0_ = (bitField0_ & ~0x00000004); + doubleValue_ = 0D; + onChanged(); + return this; + } + + private long intValue_ ; + /** + * optional int64 int_value = 4; + */ + public boolean hasIntValue() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional int64 int_value = 4; + */ + public long getIntValue() { + return intValue_; + } + /** + * optional int64 int_value = 4; + */ + public Builder setIntValue(long value) { + bitField0_ |= 0x00000008; + intValue_ = value; + onChanged(); + return this; + } + /** + * optional int64 int_value = 4; + */ + public Builder clearIntValue() { + bitField0_ = (bitField0_ & ~0x00000008); + intValue_ = 0L; + onChanged(); + return this; + } + + private long uintValue_ ; + /** + * optional uint64 uint_value = 5; + */ + public boolean hasUintValue() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional uint64 uint_value = 5; + */ + public long getUintValue() { + return uintValue_; + } + /** + * optional uint64 uint_value = 5; + */ + public Builder setUintValue(long value) { + bitField0_ |= 0x00000010; + uintValue_ = value; + onChanged(); + return this; + } + /** + * optional uint64 uint_value = 5; + */ + public Builder clearUintValue() { + bitField0_ = (bitField0_ & ~0x00000010); + uintValue_ = 0L; + onChanged(); + return this; + } + + private long sintValue_ ; + /** + * optional sint64 sint_value = 6; + */ + public boolean hasSintValue() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional sint64 sint_value = 6; + */ + public long getSintValue() { + return sintValue_; + } + /** + * optional sint64 sint_value = 6; + */ + public Builder setSintValue(long value) { + bitField0_ |= 0x00000020; + sintValue_ = value; + onChanged(); + return this; + } + /** + * optional sint64 sint_value = 6; + */ + public Builder clearSintValue() { + bitField0_ = (bitField0_ & ~0x00000020); + sintValue_ = 0L; + onChanged(); + return this; + } + + private boolean boolValue_ ; + /** + * optional bool bool_value = 7; + */ + public boolean hasBoolValue() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional bool bool_value = 7; + */ + public boolean getBoolValue() { + return boolValue_; + } + /** + * optional bool bool_value = 7; + */ + public Builder setBoolValue(boolean value) { + bitField0_ |= 0x00000040; + boolValue_ = value; + onChanged(); + return this; + } + /** + * optional bool bool_value = 7; + */ + public Builder clearBoolValue() { + bitField0_ = (bitField0_ & ~0x00000040); + boolValue_ = false; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:vector_tile.Tile.Value) + } + + // @@protoc_insertion_point(class_scope:vector_tile.Tile.Value) + private static final vector_tile.VectorTile.Tile.Value DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new vector_tile.VectorTile.Tile.Value(); + } + + public static vector_tile.VectorTile.Tile.Value getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Value parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Value(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Value getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface FeatureOrBuilder extends + // @@protoc_insertion_point(interface_extends:vector_tile.Tile.Feature) + com.google.protobuf.MessageOrBuilder { + + /** + * optional uint64 id = 1 [default = 0]; + */ + boolean hasId(); + /** + * optional uint64 id = 1 [default = 0]; + */ + long getId(); + + /** + *
+       * Tags of this feature are encoded as repeated pairs of
+       * integers.
+       * A detailed description of tags is located in sections
+       * 4.2 and 4.4 of the specification
+       * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + java.util.List getTagsList(); + /** + *
+       * Tags of this feature are encoded as repeated pairs of
+       * integers.
+       * A detailed description of tags is located in sections
+       * 4.2 and 4.4 of the specification
+       * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + int getTagsCount(); + /** + *
+       * Tags of this feature are encoded as repeated pairs of
+       * integers.
+       * A detailed description of tags is located in sections
+       * 4.2 and 4.4 of the specification
+       * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + int getTags(int index); + + /** + *
+       * The type of geometry stored in this feature.
+       * 
+ * + * optional .vector_tile.Tile.GeomType type = 3 [default = UNKNOWN]; + */ + boolean hasType(); + /** + *
+       * The type of geometry stored in this feature.
+       * 
+ * + * optional .vector_tile.Tile.GeomType type = 3 [default = UNKNOWN]; + */ + vector_tile.VectorTile.Tile.GeomType getType(); + + /** + *
+       * Contains a stream of commands and parameters (vertices).
+       * A detailed description on geometry encoding is located in 
+       * section 4.3 of the specification.
+       * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + java.util.List getGeometryList(); + /** + *
+       * Contains a stream of commands and parameters (vertices).
+       * A detailed description on geometry encoding is located in 
+       * section 4.3 of the specification.
+       * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + int getGeometryCount(); + /** + *
+       * Contains a stream of commands and parameters (vertices).
+       * A detailed description on geometry encoding is located in 
+       * section 4.3 of the specification.
+       * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + int getGeometry(int index); + } + /** + *
+     * Features are described in section 4.2 of the specification
+     * 
+ * + * Protobuf type {@code vector_tile.Tile.Feature} + */ + public static final class Feature extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:vector_tile.Tile.Feature) + FeatureOrBuilder { + private static final long serialVersionUID = 0L; + // Use Feature.newBuilder() to construct. + private Feature(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private Feature() { + id_ = 0L; + tags_ = java.util.Collections.emptyList(); + type_ = 0; + geometry_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Feature( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt64(); + break; + } + case 16: { + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + tags_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + tags_.add(input.readUInt32()); + break; + } + case 18: { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (!((mutable_bitField0_ & 0x00000002) == 0x00000002) && input.getBytesUntilLimit() > 0) { + tags_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000002; + } + while (input.getBytesUntilLimit() > 0) { + tags_.add(input.readUInt32()); + } + input.popLimit(limit); + break; + } + case 24: { + int rawValue = input.readEnum(); + @SuppressWarnings("deprecation") + vector_tile.VectorTile.Tile.GeomType value = vector_tile.VectorTile.Tile.GeomType.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(3, rawValue); + } else { + bitField0_ |= 0x00000002; + type_ = rawValue; + } + break; + } + case 32: { + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + geometry_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + geometry_.add(input.readUInt32()); + break; + } + case 34: { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008) && input.getBytesUntilLimit() > 0) { + geometry_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000008; + } + while (input.getBytesUntilLimit() > 0) { + geometry_.add(input.readUInt32()); + } + input.popLimit(limit); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) { + tags_ = java.util.Collections.unmodifiableList(tags_); + } + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + geometry_ = java.util.Collections.unmodifiableList(geometry_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Feature_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Feature_fieldAccessorTable + .ensureFieldAccessorsInitialized( + vector_tile.VectorTile.Tile.Feature.class, vector_tile.VectorTile.Tile.Feature.Builder.class); + } + + private int bitField0_; + public static final int ID_FIELD_NUMBER = 1; + private long id_; + /** + * optional uint64 id = 1 [default = 0]; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1 [default = 0]; + */ + public long getId() { + return id_; + } + + public static final int TAGS_FIELD_NUMBER = 2; + private java.util.List tags_; + /** + *
+       * Tags of this feature are encoded as repeated pairs of
+       * integers.
+       * A detailed description of tags is located in sections
+       * 4.2 and 4.4 of the specification
+       * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public java.util.List + getTagsList() { + return tags_; + } + /** + *
+       * Tags of this feature are encoded as repeated pairs of
+       * integers.
+       * A detailed description of tags is located in sections
+       * 4.2 and 4.4 of the specification
+       * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public int getTagsCount() { + return tags_.size(); + } + /** + *
+       * Tags of this feature are encoded as repeated pairs of
+       * integers.
+       * A detailed description of tags is located in sections
+       * 4.2 and 4.4 of the specification
+       * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public int getTags(int index) { + return tags_.get(index); + } + private int tagsMemoizedSerializedSize = -1; + + public static final int TYPE_FIELD_NUMBER = 3; + private int type_; + /** + *
+       * The type of geometry stored in this feature.
+       * 
+ * + * optional .vector_tile.Tile.GeomType type = 3 [default = UNKNOWN]; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + *
+       * The type of geometry stored in this feature.
+       * 
+ * + * optional .vector_tile.Tile.GeomType type = 3 [default = UNKNOWN]; + */ + public vector_tile.VectorTile.Tile.GeomType getType() { + @SuppressWarnings("deprecation") + vector_tile.VectorTile.Tile.GeomType result = vector_tile.VectorTile.Tile.GeomType.valueOf(type_); + return result == null ? vector_tile.VectorTile.Tile.GeomType.UNKNOWN : result; + } + + public static final int GEOMETRY_FIELD_NUMBER = 4; + private java.util.List geometry_; + /** + *
+       * Contains a stream of commands and parameters (vertices).
+       * A detailed description on geometry encoding is located in 
+       * section 4.3 of the specification.
+       * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public java.util.List + getGeometryList() { + return geometry_; + } + /** + *
+       * Contains a stream of commands and parameters (vertices).
+       * A detailed description on geometry encoding is located in 
+       * section 4.3 of the specification.
+       * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public int getGeometryCount() { + return geometry_.size(); + } + /** + *
+       * Contains a stream of commands and parameters (vertices).
+       * A detailed description on geometry encoding is located in 
+       * section 4.3 of the specification.
+       * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public int getGeometry(int index) { + return geometry_.get(index); + } + private int geometryMemoizedSerializedSize = -1; + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt64(1, id_); + } + if (getTagsList().size() > 0) { + output.writeUInt32NoTag(18); + output.writeUInt32NoTag(tagsMemoizedSerializedSize); + } + for (int i = 0; i < tags_.size(); i++) { + output.writeUInt32NoTag(tags_.get(i)); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeEnum(3, type_); + } + if (getGeometryList().size() > 0) { + output.writeUInt32NoTag(34); + output.writeUInt32NoTag(geometryMemoizedSerializedSize); + } + for (int i = 0; i < geometry_.size(); i++) { + output.writeUInt32NoTag(geometry_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(1, id_); + } + { + int dataSize = 0; + for (int i = 0; i < tags_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeUInt32SizeNoTag(tags_.get(i)); + } + size += dataSize; + if (!getTagsList().isEmpty()) { + size += 1; + size += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(dataSize); + } + tagsMemoizedSerializedSize = dataSize; + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(3, type_); + } + { + int dataSize = 0; + for (int i = 0; i < geometry_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeUInt32SizeNoTag(geometry_.get(i)); + } + size += dataSize; + if (!getGeometryList().isEmpty()) { + size += 1; + size += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(dataSize); + } + geometryMemoizedSerializedSize = dataSize; + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof vector_tile.VectorTile.Tile.Feature)) { + return super.equals(obj); + } + vector_tile.VectorTile.Tile.Feature other = (vector_tile.VectorTile.Tile.Feature) obj; + + boolean result = true; + result = result && (hasId() == other.hasId()); + if (hasId()) { + result = result && (getId() + == other.getId()); + } + result = result && getTagsList() + .equals(other.getTagsList()); + result = result && (hasType() == other.hasType()); + if (hasType()) { + result = result && type_ == other.type_; + } + result = result && getGeometryList() + .equals(other.getGeometryList()); + result = result && unknownFields.equals(other.unknownFields); + return result; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasId()) { + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getId()); + } + if (getTagsCount() > 0) { + hash = (37 * hash) + TAGS_FIELD_NUMBER; + hash = (53 * hash) + getTagsList().hashCode(); + } + if (hasType()) { + hash = (37 * hash) + TYPE_FIELD_NUMBER; + hash = (53 * hash) + type_; + } + if (getGeometryCount() > 0) { + hash = (37 * hash) + GEOMETRY_FIELD_NUMBER; + hash = (53 * hash) + getGeometryList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static vector_tile.VectorTile.Tile.Feature parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Feature parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Feature parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Feature parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(vector_tile.VectorTile.Tile.Feature prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+       * Features are described in section 4.2 of the specification
+       * 
+ * + * Protobuf type {@code vector_tile.Tile.Feature} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:vector_tile.Tile.Feature) + vector_tile.VectorTile.Tile.FeatureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Feature_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Feature_fieldAccessorTable + .ensureFieldAccessorsInitialized( + vector_tile.VectorTile.Tile.Feature.class, vector_tile.VectorTile.Tile.Feature.Builder.class); + } + + // Construct using vector_tile.VectorTile.Tile.Feature.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + id_ = 0L; + bitField0_ = (bitField0_ & ~0x00000001); + tags_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + type_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + geometry_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Feature_descriptor; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Feature getDefaultInstanceForType() { + return vector_tile.VectorTile.Tile.Feature.getDefaultInstance(); + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Feature build() { + vector_tile.VectorTile.Tile.Feature result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Feature buildPartial() { + vector_tile.VectorTile.Tile.Feature result = new vector_tile.VectorTile.Tile.Feature(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + tags_ = java.util.Collections.unmodifiableList(tags_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.tags_ = tags_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000002; + } + result.type_ = type_; + if (((bitField0_ & 0x00000008) == 0x00000008)) { + geometry_ = java.util.Collections.unmodifiableList(geometry_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.geometry_ = geometry_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return (Builder) super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return (Builder) super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return (Builder) super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return (Builder) super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof vector_tile.VectorTile.Tile.Feature) { + return mergeFrom((vector_tile.VectorTile.Tile.Feature)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(vector_tile.VectorTile.Tile.Feature other) { + if (other == vector_tile.VectorTile.Tile.Feature.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (!other.tags_.isEmpty()) { + if (tags_.isEmpty()) { + tags_ = other.tags_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureTagsIsMutable(); + tags_.addAll(other.tags_); + } + onChanged(); + } + if (other.hasType()) { + setType(other.getType()); + } + if (!other.geometry_.isEmpty()) { + if (geometry_.isEmpty()) { + geometry_ = other.geometry_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureGeometryIsMutable(); + geometry_.addAll(other.geometry_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + vector_tile.VectorTile.Tile.Feature parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (vector_tile.VectorTile.Tile.Feature) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private long id_ ; + /** + * optional uint64 id = 1 [default = 0]; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint64 id = 1 [default = 0]; + */ + public long getId() { + return id_; + } + /** + * optional uint64 id = 1 [default = 0]; + */ + public Builder setId(long value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint64 id = 1 [default = 0]; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0L; + onChanged(); + return this; + } + + private java.util.List tags_ = java.util.Collections.emptyList(); + private void ensureTagsIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + tags_ = new java.util.ArrayList(tags_); + bitField0_ |= 0x00000002; + } + } + /** + *
+         * Tags of this feature are encoded as repeated pairs of
+         * integers.
+         * A detailed description of tags is located in sections
+         * 4.2 and 4.4 of the specification
+         * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public java.util.List + getTagsList() { + return java.util.Collections.unmodifiableList(tags_); + } + /** + *
+         * Tags of this feature are encoded as repeated pairs of
+         * integers.
+         * A detailed description of tags is located in sections
+         * 4.2 and 4.4 of the specification
+         * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public int getTagsCount() { + return tags_.size(); + } + /** + *
+         * Tags of this feature are encoded as repeated pairs of
+         * integers.
+         * A detailed description of tags is located in sections
+         * 4.2 and 4.4 of the specification
+         * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public int getTags(int index) { + return tags_.get(index); + } + /** + *
+         * Tags of this feature are encoded as repeated pairs of
+         * integers.
+         * A detailed description of tags is located in sections
+         * 4.2 and 4.4 of the specification
+         * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public Builder setTags( + int index, int value) { + ensureTagsIsMutable(); + tags_.set(index, value); + onChanged(); + return this; + } + /** + *
+         * Tags of this feature are encoded as repeated pairs of
+         * integers.
+         * A detailed description of tags is located in sections
+         * 4.2 and 4.4 of the specification
+         * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public Builder addTags(int value) { + ensureTagsIsMutable(); + tags_.add(value); + onChanged(); + return this; + } + /** + *
+         * Tags of this feature are encoded as repeated pairs of
+         * integers.
+         * A detailed description of tags is located in sections
+         * 4.2 and 4.4 of the specification
+         * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public Builder addAllTags( + java.lang.Iterable values) { + ensureTagsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, tags_); + onChanged(); + return this; + } + /** + *
+         * Tags of this feature are encoded as repeated pairs of
+         * integers.
+         * A detailed description of tags is located in sections
+         * 4.2 and 4.4 of the specification
+         * 
+ * + * repeated uint32 tags = 2 [packed = true]; + */ + public Builder clearTags() { + tags_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + + private int type_ = 0; + /** + *
+         * The type of geometry stored in this feature.
+         * 
+ * + * optional .vector_tile.Tile.GeomType type = 3 [default = UNKNOWN]; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + *
+         * The type of geometry stored in this feature.
+         * 
+ * + * optional .vector_tile.Tile.GeomType type = 3 [default = UNKNOWN]; + */ + public vector_tile.VectorTile.Tile.GeomType getType() { + @SuppressWarnings("deprecation") + vector_tile.VectorTile.Tile.GeomType result = vector_tile.VectorTile.Tile.GeomType.valueOf(type_); + return result == null ? vector_tile.VectorTile.Tile.GeomType.UNKNOWN : result; + } + /** + *
+         * The type of geometry stored in this feature.
+         * 
+ * + * optional .vector_tile.Tile.GeomType type = 3 [default = UNKNOWN]; + */ + public Builder setType(vector_tile.VectorTile.Tile.GeomType value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + type_ = value.getNumber(); + onChanged(); + return this; + } + /** + *
+         * The type of geometry stored in this feature.
+         * 
+ * + * optional .vector_tile.Tile.GeomType type = 3 [default = UNKNOWN]; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000004); + type_ = 0; + onChanged(); + return this; + } + + private java.util.List geometry_ = java.util.Collections.emptyList(); + private void ensureGeometryIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + geometry_ = new java.util.ArrayList(geometry_); + bitField0_ |= 0x00000008; + } + } + /** + *
+         * Contains a stream of commands and parameters (vertices).
+         * A detailed description on geometry encoding is located in 
+         * section 4.3 of the specification.
+         * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public java.util.List + getGeometryList() { + return java.util.Collections.unmodifiableList(geometry_); + } + /** + *
+         * Contains a stream of commands and parameters (vertices).
+         * A detailed description on geometry encoding is located in 
+         * section 4.3 of the specification.
+         * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public int getGeometryCount() { + return geometry_.size(); + } + /** + *
+         * Contains a stream of commands and parameters (vertices).
+         * A detailed description on geometry encoding is located in 
+         * section 4.3 of the specification.
+         * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public int getGeometry(int index) { + return geometry_.get(index); + } + /** + *
+         * Contains a stream of commands and parameters (vertices).
+         * A detailed description on geometry encoding is located in 
+         * section 4.3 of the specification.
+         * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public Builder setGeometry( + int index, int value) { + ensureGeometryIsMutable(); + geometry_.set(index, value); + onChanged(); + return this; + } + /** + *
+         * Contains a stream of commands and parameters (vertices).
+         * A detailed description on geometry encoding is located in 
+         * section 4.3 of the specification.
+         * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public Builder addGeometry(int value) { + ensureGeometryIsMutable(); + geometry_.add(value); + onChanged(); + return this; + } + /** + *
+         * Contains a stream of commands and parameters (vertices).
+         * A detailed description on geometry encoding is located in 
+         * section 4.3 of the specification.
+         * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public Builder addAllGeometry( + java.lang.Iterable values) { + ensureGeometryIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, geometry_); + onChanged(); + return this; + } + /** + *
+         * Contains a stream of commands and parameters (vertices).
+         * A detailed description on geometry encoding is located in 
+         * section 4.3 of the specification.
+         * 
+ * + * repeated uint32 geometry = 4 [packed = true]; + */ + public Builder clearGeometry() { + geometry_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:vector_tile.Tile.Feature) + } + + // @@protoc_insertion_point(class_scope:vector_tile.Tile.Feature) + private static final vector_tile.VectorTile.Tile.Feature DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new vector_tile.VectorTile.Tile.Feature(); + } + + public static vector_tile.VectorTile.Tile.Feature getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Feature parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Feature(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Feature getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface LayerOrBuilder extends + // @@protoc_insertion_point(interface_extends:vector_tile.Tile.Layer) + com.google.protobuf.GeneratedMessageV3. + ExtendableMessageOrBuilder { + + /** + *
+       * Any compliant implementation must first read the version
+       * number encoded in this message and choose the correct
+       * implementation for this version number before proceeding to
+       * decode other parts of this message.
+       * 
+ * + * required uint32 version = 15 [default = 1]; + */ + boolean hasVersion(); + /** + *
+       * Any compliant implementation must first read the version
+       * number encoded in this message and choose the correct
+       * implementation for this version number before proceeding to
+       * decode other parts of this message.
+       * 
+ * + * required uint32 version = 15 [default = 1]; + */ + int getVersion(); + + /** + * required string name = 1; + */ + boolean hasName(); + /** + * required string name = 1; + */ + java.lang.String getName(); + /** + * required string name = 1; + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + java.util.List + getFeaturesList(); + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + vector_tile.VectorTile.Tile.Feature getFeatures(int index); + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + int getFeaturesCount(); + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + java.util.List + getFeaturesOrBuilderList(); + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + vector_tile.VectorTile.Tile.FeatureOrBuilder getFeaturesOrBuilder( + int index); + + /** + *
+       * Dictionary encoding for keys
+       * 
+ * + * repeated string keys = 3; + */ + java.util.List + getKeysList(); + /** + *
+       * Dictionary encoding for keys
+       * 
+ * + * repeated string keys = 3; + */ + int getKeysCount(); + /** + *
+       * Dictionary encoding for keys
+       * 
+ * + * repeated string keys = 3; + */ + java.lang.String getKeys(int index); + /** + *
+       * Dictionary encoding for keys
+       * 
+ * + * repeated string keys = 3; + */ + com.google.protobuf.ByteString + getKeysBytes(int index); + + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + java.util.List + getValuesList(); + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + vector_tile.VectorTile.Tile.Value getValues(int index); + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + int getValuesCount(); + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + java.util.List + getValuesOrBuilderList(); + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + vector_tile.VectorTile.Tile.ValueOrBuilder getValuesOrBuilder( + int index); + + /** + *
+       * Although this is an "optional" field it is required by the specification.
+       * See https://github.com/mapbox/vector-tile-spec/issues/47
+       * 
+ * + * optional uint32 extent = 5 [default = 4096]; + */ + boolean hasExtent(); + /** + *
+       * Although this is an "optional" field it is required by the specification.
+       * See https://github.com/mapbox/vector-tile-spec/issues/47
+       * 
+ * + * optional uint32 extent = 5 [default = 4096]; + */ + int getExtent(); + } + /** + *
+     * Layers are described in section 4.1 of the specification
+     * 
+ * + * Protobuf type {@code vector_tile.Tile.Layer} + */ + public static final class Layer extends + com.google.protobuf.GeneratedMessageV3.ExtendableMessage< + Layer> implements + // @@protoc_insertion_point(message_implements:vector_tile.Tile.Layer) + LayerOrBuilder { + private static final long serialVersionUID = 0L; + // Use Layer.newBuilder() to construct. + private Layer(com.google.protobuf.GeneratedMessageV3.ExtendableBuilder builder) { + super(builder); + } + private Layer() { + version_ = 1; + name_ = ""; + features_ = java.util.Collections.emptyList(); + keys_ = com.google.protobuf.LazyStringArrayList.EMPTY; + values_ = java.util.Collections.emptyList(); + extent_ = 4096; + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Layer( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + name_ = bs; + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) { + features_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000004; + } + features_.add( + input.readMessage(vector_tile.VectorTile.Tile.Feature.PARSER, extensionRegistry)); + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + if (!((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + keys_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000008; + } + keys_.add(bs); + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + values_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000010; + } + values_.add( + input.readMessage(vector_tile.VectorTile.Tile.Value.PARSER, extensionRegistry)); + break; + } + case 40: { + bitField0_ |= 0x00000004; + extent_ = input.readUInt32(); + break; + } + case 120: { + bitField0_ |= 0x00000001; + version_ = input.readUInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000004) == 0x00000004)) { + features_ = java.util.Collections.unmodifiableList(features_); + } + if (((mutable_bitField0_ & 0x00000008) == 0x00000008)) { + keys_ = keys_.getUnmodifiableView(); + } + if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) { + values_ = java.util.Collections.unmodifiableList(values_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Layer_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Layer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + vector_tile.VectorTile.Tile.Layer.class, vector_tile.VectorTile.Tile.Layer.Builder.class); + } + + private int bitField0_; + public static final int VERSION_FIELD_NUMBER = 15; + private int version_; + /** + *
+       * Any compliant implementation must first read the version
+       * number encoded in this message and choose the correct
+       * implementation for this version number before proceeding to
+       * decode other parts of this message.
+       * 
+ * + * required uint32 version = 15 [default = 1]; + */ + public boolean hasVersion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + *
+       * Any compliant implementation must first read the version
+       * number encoded in this message and choose the correct
+       * implementation for this version number before proceeding to
+       * decode other parts of this message.
+       * 
+ * + * required uint32 version = 15 [default = 1]; + */ + public int getVersion() { + return version_; + } + + public static final int NAME_FIELD_NUMBER = 1; + private volatile java.lang.Object name_; + /** + * required string name = 1; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * required string name = 1; + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } + } + /** + * required string name = 1; + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int FEATURES_FIELD_NUMBER = 2; + private java.util.List features_; + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public java.util.List getFeaturesList() { + return features_; + } + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public java.util.List + getFeaturesOrBuilderList() { + return features_; + } + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public int getFeaturesCount() { + return features_.size(); + } + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public vector_tile.VectorTile.Tile.Feature getFeatures(int index) { + return features_.get(index); + } + /** + *
+       * The actual features in this tile.
+       * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public vector_tile.VectorTile.Tile.FeatureOrBuilder getFeaturesOrBuilder( + int index) { + return features_.get(index); + } + + public static final int KEYS_FIELD_NUMBER = 3; + private com.google.protobuf.LazyStringList keys_; + /** + *
+       * Dictionary encoding for keys
+       * 
+ * + * repeated string keys = 3; + */ + public com.google.protobuf.ProtocolStringList + getKeysList() { + return keys_; + } + /** + *
+       * Dictionary encoding for keys
+       * 
+ * + * repeated string keys = 3; + */ + public int getKeysCount() { + return keys_.size(); + } + /** + *
+       * Dictionary encoding for keys
+       * 
+ * + * repeated string keys = 3; + */ + public java.lang.String getKeys(int index) { + return keys_.get(index); + } + /** + *
+       * Dictionary encoding for keys
+       * 
+ * + * repeated string keys = 3; + */ + public com.google.protobuf.ByteString + getKeysBytes(int index) { + return keys_.getByteString(index); + } + + public static final int VALUES_FIELD_NUMBER = 4; + private java.util.List values_; + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public java.util.List getValuesList() { + return values_; + } + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public java.util.List + getValuesOrBuilderList() { + return values_; + } + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public int getValuesCount() { + return values_.size(); + } + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public vector_tile.VectorTile.Tile.Value getValues(int index) { + return values_.get(index); + } + /** + *
+       * Dictionary encoding for values
+       * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public vector_tile.VectorTile.Tile.ValueOrBuilder getValuesOrBuilder( + int index) { + return values_.get(index); + } + + public static final int EXTENT_FIELD_NUMBER = 5; + private int extent_; + /** + *
+       * Although this is an "optional" field it is required by the specification.
+       * See https://github.com/mapbox/vector-tile-spec/issues/47
+       * 
+ * + * optional uint32 extent = 5 [default = 4096]; + */ + public boolean hasExtent() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + *
+       * Although this is an "optional" field it is required by the specification.
+       * See https://github.com/mapbox/vector-tile-spec/issues/47
+       * 
+ * + * optional uint32 extent = 5 [default = 4096]; + */ + public int getExtent() { + return extent_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + if (!hasVersion()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasName()) { + memoizedIsInitialized = 0; + return false; + } + for (int i = 0; i < getValuesCount(); i++) { + if (!getValues(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } + if (!extensionsAreInitialized()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + com.google.protobuf.GeneratedMessageV3 + .ExtendableMessage.ExtensionWriter + extensionWriter = newExtensionWriter(); + if (((bitField0_ & 0x00000002) == 0x00000002)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, name_); + } + for (int i = 0; i < features_.size(); i++) { + output.writeMessage(2, features_.get(i)); + } + for (int i = 0; i < keys_.size(); i++) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, keys_.getRaw(i)); + } + for (int i = 0; i < values_.size(); i++) { + output.writeMessage(4, values_.get(i)); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(5, extent_); + } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(15, version_); + } + extensionWriter.writeUntil(536870912, output); + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, name_); + } + for (int i = 0; i < features_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, features_.get(i)); + } + { + int dataSize = 0; + for (int i = 0; i < keys_.size(); i++) { + dataSize += computeStringSizeNoTag(keys_.getRaw(i)); + } + size += dataSize; + size += 1 * getKeysList().size(); + } + for (int i = 0; i < values_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, values_.get(i)); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(5, extent_); + } + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(15, version_); + } + size += extensionsSerializedSize(); + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof vector_tile.VectorTile.Tile.Layer)) { + return super.equals(obj); + } + vector_tile.VectorTile.Tile.Layer other = (vector_tile.VectorTile.Tile.Layer) obj; + + boolean result = true; + result = result && (hasVersion() == other.hasVersion()); + if (hasVersion()) { + result = result && (getVersion() + == other.getVersion()); + } + result = result && (hasName() == other.hasName()); + if (hasName()) { + result = result && getName() + .equals(other.getName()); + } + result = result && getFeaturesList() + .equals(other.getFeaturesList()); + result = result && getKeysList() + .equals(other.getKeysList()); + result = result && getValuesList() + .equals(other.getValuesList()); + result = result && (hasExtent() == other.hasExtent()); + if (hasExtent()) { + result = result && (getExtent() + == other.getExtent()); + } + result = result && unknownFields.equals(other.unknownFields); + result = result && + getExtensionFields().equals(other.getExtensionFields()); + return result; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasVersion()) { + hash = (37 * hash) + VERSION_FIELD_NUMBER; + hash = (53 * hash) + getVersion(); + } + if (hasName()) { + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + } + if (getFeaturesCount() > 0) { + hash = (37 * hash) + FEATURES_FIELD_NUMBER; + hash = (53 * hash) + getFeaturesList().hashCode(); + } + if (getKeysCount() > 0) { + hash = (37 * hash) + KEYS_FIELD_NUMBER; + hash = (53 * hash) + getKeysList().hashCode(); + } + if (getValuesCount() > 0) { + hash = (37 * hash) + VALUES_FIELD_NUMBER; + hash = (53 * hash) + getValuesList().hashCode(); + } + if (hasExtent()) { + hash = (37 * hash) + EXTENT_FIELD_NUMBER; + hash = (53 * hash) + getExtent(); + } + hash = hashFields(hash, getExtensionFields()); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static vector_tile.VectorTile.Tile.Layer parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Layer parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Layer parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile.Layer parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(vector_tile.VectorTile.Tile.Layer prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+       * Layers are described in section 4.1 of the specification
+       * 
+ * + * Protobuf type {@code vector_tile.Tile.Layer} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.ExtendableBuilder< + vector_tile.VectorTile.Tile.Layer, Builder> implements + // @@protoc_insertion_point(builder_implements:vector_tile.Tile.Layer) + vector_tile.VectorTile.Tile.LayerOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Layer_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Layer_fieldAccessorTable + .ensureFieldAccessorsInitialized( + vector_tile.VectorTile.Tile.Layer.class, vector_tile.VectorTile.Tile.Layer.Builder.class); + } + + // Construct using vector_tile.VectorTile.Tile.Layer.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getFeaturesFieldBuilder(); + getValuesFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + version_ = 1; + bitField0_ = (bitField0_ & ~0x00000001); + name_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + if (featuresBuilder_ == null) { + features_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + } else { + featuresBuilder_.clear(); + } + keys_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + if (valuesBuilder_ == null) { + values_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + } else { + valuesBuilder_.clear(); + } + extent_ = 4096; + bitField0_ = (bitField0_ & ~0x00000020); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_Layer_descriptor; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Layer getDefaultInstanceForType() { + return vector_tile.VectorTile.Tile.Layer.getDefaultInstance(); + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Layer build() { + vector_tile.VectorTile.Tile.Layer result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Layer buildPartial() { + vector_tile.VectorTile.Tile.Layer result = new vector_tile.VectorTile.Tile.Layer(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.version_ = version_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.name_ = name_; + if (featuresBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004)) { + features_ = java.util.Collections.unmodifiableList(features_); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.features_ = features_; + } else { + result.features_ = featuresBuilder_.build(); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + keys_ = keys_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.keys_ = keys_; + if (valuesBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010)) { + values_ = java.util.Collections.unmodifiableList(values_); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.values_ = values_; + } else { + result.values_ = valuesBuilder_.build(); + } + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000004; + } + result.extent_ = extent_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return (Builder) super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return (Builder) super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return (Builder) super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return (Builder) super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder setExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile.Layer, Type> extension, + Type value) { + return (Builder) super.setExtension(extension, value); + } + @java.lang.Override + public Builder setExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile.Layer, java.util.List> extension, + int index, Type value) { + return (Builder) super.setExtension(extension, index, value); + } + @java.lang.Override + public Builder addExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile.Layer, java.util.List> extension, + Type value) { + return (Builder) super.addExtension(extension, value); + } + @java.lang.Override + public Builder clearExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile.Layer, ?> extension) { + return (Builder) super.clearExtension(extension); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof vector_tile.VectorTile.Tile.Layer) { + return mergeFrom((vector_tile.VectorTile.Tile.Layer)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(vector_tile.VectorTile.Tile.Layer other) { + if (other == vector_tile.VectorTile.Tile.Layer.getDefaultInstance()) return this; + if (other.hasVersion()) { + setVersion(other.getVersion()); + } + if (other.hasName()) { + bitField0_ |= 0x00000002; + name_ = other.name_; + onChanged(); + } + if (featuresBuilder_ == null) { + if (!other.features_.isEmpty()) { + if (features_.isEmpty()) { + features_ = other.features_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureFeaturesIsMutable(); + features_.addAll(other.features_); + } + onChanged(); + } + } else { + if (!other.features_.isEmpty()) { + if (featuresBuilder_.isEmpty()) { + featuresBuilder_.dispose(); + featuresBuilder_ = null; + features_ = other.features_; + bitField0_ = (bitField0_ & ~0x00000004); + featuresBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getFeaturesFieldBuilder() : null; + } else { + featuresBuilder_.addAllMessages(other.features_); + } + } + } + if (!other.keys_.isEmpty()) { + if (keys_.isEmpty()) { + keys_ = other.keys_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureKeysIsMutable(); + keys_.addAll(other.keys_); + } + onChanged(); + } + if (valuesBuilder_ == null) { + if (!other.values_.isEmpty()) { + if (values_.isEmpty()) { + values_ = other.values_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensureValuesIsMutable(); + values_.addAll(other.values_); + } + onChanged(); + } + } else { + if (!other.values_.isEmpty()) { + if (valuesBuilder_.isEmpty()) { + valuesBuilder_.dispose(); + valuesBuilder_ = null; + values_ = other.values_; + bitField0_ = (bitField0_ & ~0x00000010); + valuesBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getValuesFieldBuilder() : null; + } else { + valuesBuilder_.addAllMessages(other.values_); + } + } + } + if (other.hasExtent()) { + setExtent(other.getExtent()); + } + this.mergeExtensionFields(other); + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + if (!hasVersion()) { + return false; + } + if (!hasName()) { + return false; + } + for (int i = 0; i < getValuesCount(); i++) { + if (!getValues(i).isInitialized()) { + return false; + } + } + if (!extensionsAreInitialized()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + vector_tile.VectorTile.Tile.Layer parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (vector_tile.VectorTile.Tile.Layer) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int version_ = 1; + /** + *
+         * Any compliant implementation must first read the version
+         * number encoded in this message and choose the correct
+         * implementation for this version number before proceeding to
+         * decode other parts of this message.
+         * 
+ * + * required uint32 version = 15 [default = 1]; + */ + public boolean hasVersion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + *
+         * Any compliant implementation must first read the version
+         * number encoded in this message and choose the correct
+         * implementation for this version number before proceeding to
+         * decode other parts of this message.
+         * 
+ * + * required uint32 version = 15 [default = 1]; + */ + public int getVersion() { + return version_; + } + /** + *
+         * Any compliant implementation must first read the version
+         * number encoded in this message and choose the correct
+         * implementation for this version number before proceeding to
+         * decode other parts of this message.
+         * 
+ * + * required uint32 version = 15 [default = 1]; + */ + public Builder setVersion(int value) { + bitField0_ |= 0x00000001; + version_ = value; + onChanged(); + return this; + } + /** + *
+         * Any compliant implementation must first read the version
+         * number encoded in this message and choose the correct
+         * implementation for this version number before proceeding to
+         * decode other parts of this message.
+         * 
+ * + * required uint32 version = 15 [default = 1]; + */ + public Builder clearVersion() { + bitField0_ = (bitField0_ & ~0x00000001); + version_ = 1; + onChanged(); + return this; + } + + private java.lang.Object name_ = ""; + /** + * required string name = 1; + */ + public boolean hasName() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * required string name = 1; + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + name_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * required string name = 1; + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * required string name = 1; + */ + public Builder setName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + name_ = value; + onChanged(); + return this; + } + /** + * required string name = 1; + */ + public Builder clearName() { + bitField0_ = (bitField0_ & ~0x00000002); + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * required string name = 1; + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + name_ = value; + onChanged(); + return this; + } + + private java.util.List features_ = + java.util.Collections.emptyList(); + private void ensureFeaturesIsMutable() { + if (!((bitField0_ & 0x00000004) == 0x00000004)) { + features_ = new java.util.ArrayList(features_); + bitField0_ |= 0x00000004; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Feature, vector_tile.VectorTile.Tile.Feature.Builder, vector_tile.VectorTile.Tile.FeatureOrBuilder> featuresBuilder_; + + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public java.util.List getFeaturesList() { + if (featuresBuilder_ == null) { + return java.util.Collections.unmodifiableList(features_); + } else { + return featuresBuilder_.getMessageList(); + } + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public int getFeaturesCount() { + if (featuresBuilder_ == null) { + return features_.size(); + } else { + return featuresBuilder_.getCount(); + } + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public vector_tile.VectorTile.Tile.Feature getFeatures(int index) { + if (featuresBuilder_ == null) { + return features_.get(index); + } else { + return featuresBuilder_.getMessage(index); + } + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder setFeatures( + int index, vector_tile.VectorTile.Tile.Feature value) { + if (featuresBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureFeaturesIsMutable(); + features_.set(index, value); + onChanged(); + } else { + featuresBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder setFeatures( + int index, vector_tile.VectorTile.Tile.Feature.Builder builderForValue) { + if (featuresBuilder_ == null) { + ensureFeaturesIsMutable(); + features_.set(index, builderForValue.build()); + onChanged(); + } else { + featuresBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder addFeatures(vector_tile.VectorTile.Tile.Feature value) { + if (featuresBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureFeaturesIsMutable(); + features_.add(value); + onChanged(); + } else { + featuresBuilder_.addMessage(value); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder addFeatures( + int index, vector_tile.VectorTile.Tile.Feature value) { + if (featuresBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureFeaturesIsMutable(); + features_.add(index, value); + onChanged(); + } else { + featuresBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder addFeatures( + vector_tile.VectorTile.Tile.Feature.Builder builderForValue) { + if (featuresBuilder_ == null) { + ensureFeaturesIsMutable(); + features_.add(builderForValue.build()); + onChanged(); + } else { + featuresBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder addFeatures( + int index, vector_tile.VectorTile.Tile.Feature.Builder builderForValue) { + if (featuresBuilder_ == null) { + ensureFeaturesIsMutable(); + features_.add(index, builderForValue.build()); + onChanged(); + } else { + featuresBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder addAllFeatures( + java.lang.Iterable values) { + if (featuresBuilder_ == null) { + ensureFeaturesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, features_); + onChanged(); + } else { + featuresBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder clearFeatures() { + if (featuresBuilder_ == null) { + features_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + } else { + featuresBuilder_.clear(); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public Builder removeFeatures(int index) { + if (featuresBuilder_ == null) { + ensureFeaturesIsMutable(); + features_.remove(index); + onChanged(); + } else { + featuresBuilder_.remove(index); + } + return this; + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public vector_tile.VectorTile.Tile.Feature.Builder getFeaturesBuilder( + int index) { + return getFeaturesFieldBuilder().getBuilder(index); + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public vector_tile.VectorTile.Tile.FeatureOrBuilder getFeaturesOrBuilder( + int index) { + if (featuresBuilder_ == null) { + return features_.get(index); } else { + return featuresBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public java.util.List + getFeaturesOrBuilderList() { + if (featuresBuilder_ != null) { + return featuresBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(features_); + } + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public vector_tile.VectorTile.Tile.Feature.Builder addFeaturesBuilder() { + return getFeaturesFieldBuilder().addBuilder( + vector_tile.VectorTile.Tile.Feature.getDefaultInstance()); + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public vector_tile.VectorTile.Tile.Feature.Builder addFeaturesBuilder( + int index) { + return getFeaturesFieldBuilder().addBuilder( + index, vector_tile.VectorTile.Tile.Feature.getDefaultInstance()); + } + /** + *
+         * The actual features in this tile.
+         * 
+ * + * repeated .vector_tile.Tile.Feature features = 2; + */ + public java.util.List + getFeaturesBuilderList() { + return getFeaturesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Feature, vector_tile.VectorTile.Tile.Feature.Builder, vector_tile.VectorTile.Tile.FeatureOrBuilder> + getFeaturesFieldBuilder() { + if (featuresBuilder_ == null) { + featuresBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Feature, vector_tile.VectorTile.Tile.Feature.Builder, vector_tile.VectorTile.Tile.FeatureOrBuilder>( + features_, + ((bitField0_ & 0x00000004) == 0x00000004), + getParentForChildren(), + isClean()); + features_ = null; + } + return featuresBuilder_; + } + + private com.google.protobuf.LazyStringList keys_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureKeysIsMutable() { + if (!((bitField0_ & 0x00000008) == 0x00000008)) { + keys_ = new com.google.protobuf.LazyStringArrayList(keys_); + bitField0_ |= 0x00000008; + } + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public com.google.protobuf.ProtocolStringList + getKeysList() { + return keys_.getUnmodifiableView(); + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public int getKeysCount() { + return keys_.size(); + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public java.lang.String getKeys(int index) { + return keys_.get(index); + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public com.google.protobuf.ByteString + getKeysBytes(int index) { + return keys_.getByteString(index); + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public Builder setKeys( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureKeysIsMutable(); + keys_.set(index, value); + onChanged(); + return this; + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public Builder addKeys( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureKeysIsMutable(); + keys_.add(value); + onChanged(); + return this; + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public Builder addAllKeys( + java.lang.Iterable values) { + ensureKeysIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, keys_); + onChanged(); + return this; + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public Builder clearKeys() { + keys_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + /** + *
+         * Dictionary encoding for keys
+         * 
+ * + * repeated string keys = 3; + */ + public Builder addKeysBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureKeysIsMutable(); + keys_.add(value); + onChanged(); + return this; + } + + private java.util.List values_ = + java.util.Collections.emptyList(); + private void ensureValuesIsMutable() { + if (!((bitField0_ & 0x00000010) == 0x00000010)) { + values_ = new java.util.ArrayList(values_); + bitField0_ |= 0x00000010; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Value, vector_tile.VectorTile.Tile.Value.Builder, vector_tile.VectorTile.Tile.ValueOrBuilder> valuesBuilder_; + + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public java.util.List getValuesList() { + if (valuesBuilder_ == null) { + return java.util.Collections.unmodifiableList(values_); + } else { + return valuesBuilder_.getMessageList(); + } + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public int getValuesCount() { + if (valuesBuilder_ == null) { + return values_.size(); + } else { + return valuesBuilder_.getCount(); + } + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public vector_tile.VectorTile.Tile.Value getValues(int index) { + if (valuesBuilder_ == null) { + return values_.get(index); + } else { + return valuesBuilder_.getMessage(index); + } + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder setValues( + int index, vector_tile.VectorTile.Tile.Value value) { + if (valuesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureValuesIsMutable(); + values_.set(index, value); + onChanged(); + } else { + valuesBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder setValues( + int index, vector_tile.VectorTile.Tile.Value.Builder builderForValue) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + values_.set(index, builderForValue.build()); + onChanged(); + } else { + valuesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder addValues(vector_tile.VectorTile.Tile.Value value) { + if (valuesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureValuesIsMutable(); + values_.add(value); + onChanged(); + } else { + valuesBuilder_.addMessage(value); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder addValues( + int index, vector_tile.VectorTile.Tile.Value value) { + if (valuesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureValuesIsMutable(); + values_.add(index, value); + onChanged(); + } else { + valuesBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder addValues( + vector_tile.VectorTile.Tile.Value.Builder builderForValue) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + values_.add(builderForValue.build()); + onChanged(); + } else { + valuesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder addValues( + int index, vector_tile.VectorTile.Tile.Value.Builder builderForValue) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + values_.add(index, builderForValue.build()); + onChanged(); + } else { + valuesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder addAllValues( + java.lang.Iterable values) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, values_); + onChanged(); + } else { + valuesBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder clearValues() { + if (valuesBuilder_ == null) { + values_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + } else { + valuesBuilder_.clear(); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public Builder removeValues(int index) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + values_.remove(index); + onChanged(); + } else { + valuesBuilder_.remove(index); + } + return this; + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public vector_tile.VectorTile.Tile.Value.Builder getValuesBuilder( + int index) { + return getValuesFieldBuilder().getBuilder(index); + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public vector_tile.VectorTile.Tile.ValueOrBuilder getValuesOrBuilder( + int index) { + if (valuesBuilder_ == null) { + return values_.get(index); } else { + return valuesBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public java.util.List + getValuesOrBuilderList() { + if (valuesBuilder_ != null) { + return valuesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(values_); + } + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public vector_tile.VectorTile.Tile.Value.Builder addValuesBuilder() { + return getValuesFieldBuilder().addBuilder( + vector_tile.VectorTile.Tile.Value.getDefaultInstance()); + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public vector_tile.VectorTile.Tile.Value.Builder addValuesBuilder( + int index) { + return getValuesFieldBuilder().addBuilder( + index, vector_tile.VectorTile.Tile.Value.getDefaultInstance()); + } + /** + *
+         * Dictionary encoding for values
+         * 
+ * + * repeated .vector_tile.Tile.Value values = 4; + */ + public java.util.List + getValuesBuilderList() { + return getValuesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Value, vector_tile.VectorTile.Tile.Value.Builder, vector_tile.VectorTile.Tile.ValueOrBuilder> + getValuesFieldBuilder() { + if (valuesBuilder_ == null) { + valuesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Value, vector_tile.VectorTile.Tile.Value.Builder, vector_tile.VectorTile.Tile.ValueOrBuilder>( + values_, + ((bitField0_ & 0x00000010) == 0x00000010), + getParentForChildren(), + isClean()); + values_ = null; + } + return valuesBuilder_; + } + + private int extent_ = 4096; + /** + *
+         * Although this is an "optional" field it is required by the specification.
+         * See https://github.com/mapbox/vector-tile-spec/issues/47
+         * 
+ * + * optional uint32 extent = 5 [default = 4096]; + */ + public boolean hasExtent() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + *
+         * Although this is an "optional" field it is required by the specification.
+         * See https://github.com/mapbox/vector-tile-spec/issues/47
+         * 
+ * + * optional uint32 extent = 5 [default = 4096]; + */ + public int getExtent() { + return extent_; + } + /** + *
+         * Although this is an "optional" field it is required by the specification.
+         * See https://github.com/mapbox/vector-tile-spec/issues/47
+         * 
+ * + * optional uint32 extent = 5 [default = 4096]; + */ + public Builder setExtent(int value) { + bitField0_ |= 0x00000020; + extent_ = value; + onChanged(); + return this; + } + /** + *
+         * Although this is an "optional" field it is required by the specification.
+         * See https://github.com/mapbox/vector-tile-spec/issues/47
+         * 
+ * + * optional uint32 extent = 5 [default = 4096]; + */ + public Builder clearExtent() { + bitField0_ = (bitField0_ & ~0x00000020); + extent_ = 4096; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:vector_tile.Tile.Layer) + } + + // @@protoc_insertion_point(class_scope:vector_tile.Tile.Layer) + private static final vector_tile.VectorTile.Tile.Layer DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new vector_tile.VectorTile.Tile.Layer(); + } + + public static vector_tile.VectorTile.Tile.Layer getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Layer parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Layer(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile.Layer getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public static final int LAYERS_FIELD_NUMBER = 3; + private java.util.List layers_; + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public java.util.List getLayersList() { + return layers_; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public java.util.List + getLayersOrBuilderList() { + return layers_; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public int getLayersCount() { + return layers_.size(); + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public vector_tile.VectorTile.Tile.Layer getLayers(int index) { + return layers_.get(index); + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public vector_tile.VectorTile.Tile.LayerOrBuilder getLayersOrBuilder( + int index) { + return layers_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + for (int i = 0; i < getLayersCount(); i++) { + if (!getLayers(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } + if (!extensionsAreInitialized()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + com.google.protobuf.GeneratedMessageV3 + .ExtendableMessage.ExtensionWriter + extensionWriter = newExtensionWriter(); + for (int i = 0; i < layers_.size(); i++) { + output.writeMessage(3, layers_.get(i)); + } + extensionWriter.writeUntil(8192, output); + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < layers_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, layers_.get(i)); + } + size += extensionsSerializedSize(); + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof vector_tile.VectorTile.Tile)) { + return super.equals(obj); + } + vector_tile.VectorTile.Tile other = (vector_tile.VectorTile.Tile) obj; + + boolean result = true; + result = result && getLayersList() + .equals(other.getLayersList()); + result = result && unknownFields.equals(other.unknownFields); + result = result && + getExtensionFields().equals(other.getExtensionFields()); + return result; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getLayersCount() > 0) { + hash = (37 * hash) + LAYERS_FIELD_NUMBER; + hash = (53 * hash) + getLayersList().hashCode(); + } + hash = hashFields(hash, getExtensionFields()); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static vector_tile.VectorTile.Tile parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static vector_tile.VectorTile.Tile parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static vector_tile.VectorTile.Tile parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static vector_tile.VectorTile.Tile parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static vector_tile.VectorTile.Tile parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static vector_tile.VectorTile.Tile parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(vector_tile.VectorTile.Tile prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code vector_tile.Tile} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.ExtendableBuilder< + vector_tile.VectorTile.Tile, Builder> implements + // @@protoc_insertion_point(builder_implements:vector_tile.Tile) + vector_tile.VectorTile.TileOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_fieldAccessorTable + .ensureFieldAccessorsInitialized( + vector_tile.VectorTile.Tile.class, vector_tile.VectorTile.Tile.Builder.class); + } + + // Construct using vector_tile.VectorTile.Tile.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getLayersFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + if (layersBuilder_ == null) { + layers_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + layersBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return vector_tile.VectorTile.internal_static_vector_tile_Tile_descriptor; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile getDefaultInstanceForType() { + return vector_tile.VectorTile.Tile.getDefaultInstance(); + } + + @java.lang.Override + public vector_tile.VectorTile.Tile build() { + vector_tile.VectorTile.Tile result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile buildPartial() { + vector_tile.VectorTile.Tile result = new vector_tile.VectorTile.Tile(this); + int from_bitField0_ = bitField0_; + if (layersBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001)) { + layers_ = java.util.Collections.unmodifiableList(layers_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.layers_ = layers_; + } else { + result.layers_ = layersBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return (Builder) super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return (Builder) super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return (Builder) super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return (Builder) super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return (Builder) super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder setExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile, Type> extension, + Type value) { + return (Builder) super.setExtension(extension, value); + } + @java.lang.Override + public Builder setExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile, java.util.List> extension, + int index, Type value) { + return (Builder) super.setExtension(extension, index, value); + } + @java.lang.Override + public Builder addExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile, java.util.List> extension, + Type value) { + return (Builder) super.addExtension(extension, value); + } + @java.lang.Override + public Builder clearExtension( + com.google.protobuf.GeneratedMessage.GeneratedExtension< + vector_tile.VectorTile.Tile, ?> extension) { + return (Builder) super.clearExtension(extension); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof vector_tile.VectorTile.Tile) { + return mergeFrom((vector_tile.VectorTile.Tile)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(vector_tile.VectorTile.Tile other) { + if (other == vector_tile.VectorTile.Tile.getDefaultInstance()) return this; + if (layersBuilder_ == null) { + if (!other.layers_.isEmpty()) { + if (layers_.isEmpty()) { + layers_ = other.layers_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureLayersIsMutable(); + layers_.addAll(other.layers_); + } + onChanged(); + } + } else { + if (!other.layers_.isEmpty()) { + if (layersBuilder_.isEmpty()) { + layersBuilder_.dispose(); + layersBuilder_ = null; + layers_ = other.layers_; + bitField0_ = (bitField0_ & ~0x00000001); + layersBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getLayersFieldBuilder() : null; + } else { + layersBuilder_.addAllMessages(other.layers_); + } + } + } + this.mergeExtensionFields(other); + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + for (int i = 0; i < getLayersCount(); i++) { + if (!getLayers(i).isInitialized()) { + return false; + } + } + if (!extensionsAreInitialized()) { + return false; + } + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + vector_tile.VectorTile.Tile parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (vector_tile.VectorTile.Tile) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List layers_ = + java.util.Collections.emptyList(); + private void ensureLayersIsMutable() { + if (!((bitField0_ & 0x00000001) == 0x00000001)) { + layers_ = new java.util.ArrayList(layers_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Layer, vector_tile.VectorTile.Tile.Layer.Builder, vector_tile.VectorTile.Tile.LayerOrBuilder> layersBuilder_; + + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public java.util.List getLayersList() { + if (layersBuilder_ == null) { + return java.util.Collections.unmodifiableList(layers_); + } else { + return layersBuilder_.getMessageList(); + } + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public int getLayersCount() { + if (layersBuilder_ == null) { + return layers_.size(); + } else { + return layersBuilder_.getCount(); + } + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public vector_tile.VectorTile.Tile.Layer getLayers(int index) { + if (layersBuilder_ == null) { + return layers_.get(index); + } else { + return layersBuilder_.getMessage(index); + } + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder setLayers( + int index, vector_tile.VectorTile.Tile.Layer value) { + if (layersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureLayersIsMutable(); + layers_.set(index, value); + onChanged(); + } else { + layersBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder setLayers( + int index, vector_tile.VectorTile.Tile.Layer.Builder builderForValue) { + if (layersBuilder_ == null) { + ensureLayersIsMutable(); + layers_.set(index, builderForValue.build()); + onChanged(); + } else { + layersBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder addLayers(vector_tile.VectorTile.Tile.Layer value) { + if (layersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureLayersIsMutable(); + layers_.add(value); + onChanged(); + } else { + layersBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder addLayers( + int index, vector_tile.VectorTile.Tile.Layer value) { + if (layersBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureLayersIsMutable(); + layers_.add(index, value); + onChanged(); + } else { + layersBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder addLayers( + vector_tile.VectorTile.Tile.Layer.Builder builderForValue) { + if (layersBuilder_ == null) { + ensureLayersIsMutable(); + layers_.add(builderForValue.build()); + onChanged(); + } else { + layersBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder addLayers( + int index, vector_tile.VectorTile.Tile.Layer.Builder builderForValue) { + if (layersBuilder_ == null) { + ensureLayersIsMutable(); + layers_.add(index, builderForValue.build()); + onChanged(); + } else { + layersBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder addAllLayers( + java.lang.Iterable values) { + if (layersBuilder_ == null) { + ensureLayersIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, layers_); + onChanged(); + } else { + layersBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder clearLayers() { + if (layersBuilder_ == null) { + layers_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + layersBuilder_.clear(); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public Builder removeLayers(int index) { + if (layersBuilder_ == null) { + ensureLayersIsMutable(); + layers_.remove(index); + onChanged(); + } else { + layersBuilder_.remove(index); + } + return this; + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public vector_tile.VectorTile.Tile.Layer.Builder getLayersBuilder( + int index) { + return getLayersFieldBuilder().getBuilder(index); + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public vector_tile.VectorTile.Tile.LayerOrBuilder getLayersOrBuilder( + int index) { + if (layersBuilder_ == null) { + return layers_.get(index); } else { + return layersBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public java.util.List + getLayersOrBuilderList() { + if (layersBuilder_ != null) { + return layersBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(layers_); + } + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public vector_tile.VectorTile.Tile.Layer.Builder addLayersBuilder() { + return getLayersFieldBuilder().addBuilder( + vector_tile.VectorTile.Tile.Layer.getDefaultInstance()); + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public vector_tile.VectorTile.Tile.Layer.Builder addLayersBuilder( + int index) { + return getLayersFieldBuilder().addBuilder( + index, vector_tile.VectorTile.Tile.Layer.getDefaultInstance()); + } + /** + * repeated .vector_tile.Tile.Layer layers = 3; + */ + public java.util.List + getLayersBuilderList() { + return getLayersFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Layer, vector_tile.VectorTile.Tile.Layer.Builder, vector_tile.VectorTile.Tile.LayerOrBuilder> + getLayersFieldBuilder() { + if (layersBuilder_ == null) { + layersBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + vector_tile.VectorTile.Tile.Layer, vector_tile.VectorTile.Tile.Layer.Builder, vector_tile.VectorTile.Tile.LayerOrBuilder>( + layers_, + ((bitField0_ & 0x00000001) == 0x00000001), + getParentForChildren(), + isClean()); + layers_ = null; + } + return layersBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:vector_tile.Tile) + } + + // @@protoc_insertion_point(class_scope:vector_tile.Tile) + private static final vector_tile.VectorTile.Tile DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new vector_tile.VectorTile.Tile(); + } + + public static vector_tile.VectorTile.Tile getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + @java.lang.Deprecated public static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Tile parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Tile(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public vector_tile.VectorTile.Tile getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_vector_tile_Tile_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_vector_tile_Tile_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_vector_tile_Tile_Value_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_vector_tile_Tile_Value_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_vector_tile_Tile_Feature_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_vector_tile_Tile_Feature_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_vector_tile_Tile_Layer_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_vector_tile_Tile_Layer_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n$src/main/resources/vector_tile.proto\022\013" + + "vector_tile\"\300\004\n\004Tile\022\'\n\006layers\030\003 \003(\0132\027.v" + + "ector_tile.Tile.Layer\032\241\001\n\005Value\022\024\n\014strin" + + "g_value\030\001 \001(\t\022\023\n\013float_value\030\002 \001(\002\022\024\n\014do" + + "uble_value\030\003 \001(\001\022\021\n\tint_value\030\004 \001(\003\022\022\n\nu" + + "int_value\030\005 \001(\004\022\022\n\nsint_value\030\006 \001(\022\022\022\n\nb" + + "ool_value\030\007 \001(\010*\010\010\010\020\200\200\200\200\002\032s\n\007Feature\022\r\n\002" + + "id\030\001 \001(\004:\0010\022\020\n\004tags\030\002 \003(\rB\002\020\001\0221\n\004type\030\003 " + + "\001(\0162\032.vector_tile.Tile.GeomType:\007UNKNOWN" + + "\022\024\n\010geometry\030\004 \003(\rB\002\020\001\032\255\001\n\005Layer\022\022\n\007vers" + + "ion\030\017 \002(\r:\0011\022\014\n\004name\030\001 \002(\t\022+\n\010features\030\002" + + " \003(\0132\031.vector_tile.Tile.Feature\022\014\n\004keys\030" + + "\003 \003(\t\022\'\n\006values\030\004 \003(\0132\027.vector_tile.Tile" + + ".Value\022\024\n\006extent\030\005 \001(\r:\0044096*\010\010\020\020\200\200\200\200\002\"?" + + "\n\010GeomType\022\013\n\007UNKNOWN\020\000\022\t\n\005POINT\020\001\022\016\n\nLI" + + "NESTRING\020\002\022\013\n\007POLYGON\020\003*\005\010\020\020\200@" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + internal_static_vector_tile_Tile_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_vector_tile_Tile_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_vector_tile_Tile_descriptor, + new java.lang.String[] { "Layers", }); + internal_static_vector_tile_Tile_Value_descriptor = + internal_static_vector_tile_Tile_descriptor.getNestedTypes().get(0); + internal_static_vector_tile_Tile_Value_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_vector_tile_Tile_Value_descriptor, + new java.lang.String[] { "StringValue", "FloatValue", "DoubleValue", "IntValue", "UintValue", "SintValue", "BoolValue", }); + internal_static_vector_tile_Tile_Feature_descriptor = + internal_static_vector_tile_Tile_descriptor.getNestedTypes().get(1); + internal_static_vector_tile_Tile_Feature_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_vector_tile_Tile_Feature_descriptor, + new java.lang.String[] { "Id", "Tags", "Type", "Geometry", }); + internal_static_vector_tile_Tile_Layer_descriptor = + internal_static_vector_tile_Tile_descriptor.getNestedTypes().get(2); + internal_static_vector_tile_Tile_Layer_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_vector_tile_Tile_Layer_descriptor, + new java.lang.String[] { "Version", "Name", "Features", "Keys", "Values", "Extent", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/config.js b/web-bundle/src/main/resources/com/graphhopper/maps/config.js new file mode 100644 index 00000000000..3caed0352ce --- /dev/null +++ b/web-bundle/src/main/resources/com/graphhopper/maps/config.js @@ -0,0 +1,22 @@ +const config = { + routingApi: location.origin + '/', + geocodingApi: '', + defaultTiles: 'OpenStreetMap', + keys: { + graphhopper: "", + maptiler: "missing_api_key", + omniscale: "missing_api_key", + thunderforest: "missing_api_key", + kurviger: "missing_api_key" + }, + routingGraphLayerAllowed: true, + request: { + details: [ + 'road_class', + 'road_environment', + 'max_speed', + 'average_speed', + ], + snapPreventions: ['ferry'], + }, +} \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/L.Control.Heightgraph.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/L.Control.Heightgraph.css deleted file mode 100755 index 3267718ec94..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/L.Control.Heightgraph.css +++ /dev/null @@ -1,148 +0,0 @@ -.heightgraph-container { - background-color: rgba(250,250,250,.8); - border-radius: 10px; - display: none; - cursor: default; - user-select: none; -} - -.heightgraph-toggle { - cursor: pointer; - box-shadow: 0 1px 7px rgba(0, 0, 0, .4); - border-radius: 5px; - width: 28px; - height: 28px; - background: #f8f8f9; - display: block; -} - -.heightgraph-toggle-icon { - background: url(images/area-chart.svg) no-repeat center center; - background-size: 14px 14px; - width: 26px; - height: 26px; - position: absolute; -} - -.heightgraph-close-icon { - background: url(images/remove.svg) no-repeat center center; - background-size: 14px 14px; - width: 26px; - height: 26px; - position: absolute; - right: 0; - display: none; - cursor: pointer; -} - -.border-top { - fill: none; -} - -.legend-hover { - cursor: pointer; -} - -.legend-text { - fill: #000; - font-size: 10px; - cursor: pointer; -} - -.tick, .tick text { - fill: #000; - pointer-events: none; -} - -.axis .tick line { - visibility: hidden; - pointer-events: none; -} - -.axis path { - stroke: black; - fill: none; - stroke-width: 2px; - shape-rendering: crispEdges; - pointer-events: none; -} - -.focusbox { - display: none; - font-size: 10px; - fill: #000; - pointer-events: none; -} - -.focusbox rect { - fill: rgba(255, 255, 255, 0.8); - stroke-width: 1px; - stroke: #888; - pointer-events: none; -} - -.focusbox text { - font-size: 12px; -} - -.focusLine line { - stroke-width: 1px; - stroke: rgb(20, 20, 20); - display: none; - cursor: default; - shape-rendering: crispEdges; -} - -.height-focus.label rect { - fill: rgba(255, 255, 255, 0.5); - stroke-width: 1px; - stroke: #888; - pointer-events: none; - shape-rendering: crispEdges; -} - -.height-focus.line { - stroke: rgb(20, 20, 20); - stroke-width: 1px; - shape-rendering: crispEdges; -} - -.height-focus.circle { - stroke: #FFF; - stroke-width: 1px; -} - -.mouse-height-box-text{ - font-size: 12px; -} - -.grid .tick { - pointer-events: none; -} - -.grid .tick line { - stroke: #EEE; - stroke-width: 1px; - shape-rendering: crispEdges; -} - -.grid path { - stroke-width: 0; - pointer-events: none; -} - -.tspan { - font-weight: bold; -} - -.select-symbol { - cursor: pointer; -} - -.select-info { - cursor: default; -} - -.lineSelection { - cursor: move; -} \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/addon/hint/show-hint.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/addon/hint/show-hint.css deleted file mode 100644 index 83bba5393ee..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/addon/hint/show-hint.css +++ /dev/null @@ -1,37 +0,0 @@ -/* Copied from code mirror 5.59.2, do not edit this file */ -.CodeMirror-hints { - position: absolute; - z-index: 10; - overflow: hidden; - list-style: none; - - margin: 0; - padding: 2px; - - -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); - -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); - box-shadow: 2px 3px 5px rgba(0,0,0,.2); - border-radius: 3px; - border: 1px solid silver; - - background: white; - font-size: 90%; - font-family: monospace; - - max-height: 20em; - overflow-y: auto; -} - -.CodeMirror-hint { - margin: 0; - padding: 0 4px; - border-radius: 2px; - white-space: pre; - color: black; - cursor: pointer; -} - -li.CodeMirror-hint-active { - background: #08f; - color: white; -} diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/addon/lint/lint.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/addon/lint/lint.css deleted file mode 100644 index 6a59c0e6fd6..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/addon/lint/lint.css +++ /dev/null @@ -1,73 +0,0 @@ -/* Copied from code mirror 5.59.2, do not edit this file */ - -/* The lint marker gutter */ -.CodeMirror-lint-markers { - width: 16px; -} - -.CodeMirror-lint-tooltip { - background-color: #ffd; - border: 1px solid black; - border-radius: 4px 4px 4px 4px; - color: black; - font-family: monospace; - font-size: 10pt; - overflow: hidden; - padding: 2px 5px; - position: fixed; - white-space: pre; - white-space: pre-wrap; - z-index: 100; - max-width: 600px; - opacity: 0; - transition: opacity .4s; - -moz-transition: opacity .4s; - -webkit-transition: opacity .4s; - -o-transition: opacity .4s; - -ms-transition: opacity .4s; -} - -.CodeMirror-lint-mark { - background-position: left bottom; - background-repeat: repeat-x; -} - -.CodeMirror-lint-mark-warning { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); -} - -.CodeMirror-lint-mark-error { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg=="); -} - -.CodeMirror-lint-marker { - background-position: center center; - background-repeat: no-repeat; - cursor: pointer; - display: inline-block; - height: 16px; - width: 16px; - vertical-align: middle; - position: relative; -} - -.CodeMirror-lint-message { - padding-left: 18px; - background-position: top left; - background-repeat: no-repeat; -} - -.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); -} - -.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); -} - -.CodeMirror-lint-marker-multiple { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); - background-repeat: no-repeat; - background-position: right bottom; - width: 100%; height: 100%; -} diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/lib/codemirror.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/lib/codemirror.css deleted file mode 100644 index 0ce9be8de80..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/codemirror/lib/codemirror.css +++ /dev/null @@ -1,352 +0,0 @@ -/* Copied from code mirror 5.59.2, do not edit this file */ - -/* BASICS */ - -.CodeMirror { - /* Set height, width, borders, and global font properties here */ - font-family: monospace; - height: 300px; - color: black; - direction: ltr; -} - -/* PADDING */ - -.CodeMirror-lines { - padding: 4px 0; /* Vertical padding around content */ -} -.CodeMirror pre.CodeMirror-line, -.CodeMirror pre.CodeMirror-line-like { - padding: 0 4px; /* Horizontal padding of content */ -} - -.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - background-color: transparent; /* The little square between H and V scrollbars */ -} - -/* GUTTER */ - -.CodeMirror-gutters { - border-right: 1px solid #ddd; - background-color: #f7f7f7; - white-space: nowrap; -} -.CodeMirror-linenumbers {} -.CodeMirror-linenumber { - padding: 0 3px 0 5px; - min-width: 20px; - text-align: right; - color: #999; - white-space: nowrap; -} - -.CodeMirror-guttermarker { color: black; } -.CodeMirror-guttermarker-subtle { color: #999; } - -/* CURSOR */ - -.CodeMirror-cursor { - border-left: 1px solid black; - border-right: none; - width: 0; -} -/* Shown when moving in bi-directional text */ -.CodeMirror div.CodeMirror-secondarycursor { - border-left: 1px solid silver; -} -.cm-fat-cursor .CodeMirror-cursor { - width: auto; - border: 0 !important; - background: #7e7; -} -.cm-fat-cursor div.CodeMirror-cursors { - z-index: 1; -} -.cm-fat-cursor-mark { - background-color: rgba(20, 255, 20, 0.5); - -webkit-animation: blink 1.06s steps(1) infinite; - -moz-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; -} -.cm-animate-fat-cursor { - width: auto; - border: 0; - -webkit-animation: blink 1.06s steps(1) infinite; - -moz-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; - background-color: #7e7; -} -@-moz-keyframes blink { - 0% {} - 50% { background-color: transparent; } - 100% {} -} -@-webkit-keyframes blink { - 0% {} - 50% { background-color: transparent; } - 100% {} -} -@keyframes blink { - 0% {} - 50% { background-color: transparent; } - 100% {} -} - -/* Can style cursor different in overwrite (non-insert) mode */ -.CodeMirror-overwrite .CodeMirror-cursor {} - -.cm-tab { display: inline-block; text-decoration: inherit; } - -.CodeMirror-rulers { - position: absolute; - left: 0; right: 0; top: -50px; bottom: 0; - overflow: hidden; -} -.CodeMirror-ruler { - border-left: 1px solid #ccc; - top: 0; bottom: 0; - position: absolute; -} - -/* DEFAULT THEME */ - -.cm-s-default .cm-header {color: blue;} -.cm-s-default .cm-quote {color: #090;} -.cm-negative {color: #d44;} -.cm-positive {color: #292;} -.cm-header, .cm-strong {font-weight: bold;} -.cm-em {font-style: italic;} -.cm-link {text-decoration: underline;} -.cm-strikethrough {text-decoration: line-through;} - -.cm-s-default .cm-keyword {color: #708;} -.cm-s-default .cm-atom {color: #219;} -.cm-s-default .cm-number {color: #164;} -.cm-s-default .cm-def {color: #00f;} -.cm-s-default .cm-variable, -.cm-s-default .cm-punctuation, -.cm-s-default .cm-property, -.cm-s-default .cm-operator {} -.cm-s-default .cm-variable-2 {color: #05a;} -.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} -.cm-s-default .cm-comment {color: #a50;} -.cm-s-default .cm-string {color: #a11;} -.cm-s-default .cm-string-2 {color: #f50;} -.cm-s-default .cm-meta {color: #555;} -.cm-s-default .cm-qualifier {color: #555;} -.cm-s-default .cm-builtin {color: #30a;} -.cm-s-default .cm-bracket {color: #997;} -.cm-s-default .cm-tag {color: #170;} -.cm-s-default .cm-attribute {color: #00c;} -.cm-s-default .cm-hr {color: #999;} -.cm-s-default .cm-link {color: #00c;} - -.cm-s-default .cm-error {color: #f00;} -.cm-invalidchar {color: #f00;} - -.CodeMirror-composing { border-bottom: 2px solid; } - -/* Default styles for common addons */ - -div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} -.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } -.CodeMirror-activeline-background {background: #e8f2ff;} - -/* STOP */ - -/* The rest of this file contains styles related to the mechanics of - the editor. You probably shouldn't touch them. */ - -.CodeMirror { - position: relative; - overflow: hidden; - background: white; -} - -.CodeMirror-scroll { - overflow: scroll !important; /* Things will break if this is overridden */ - /* 50px is the magic margin used to hide the element's real scrollbars */ - /* See overflow: hidden in .CodeMirror */ - margin-bottom: -50px; margin-right: -50px; - padding-bottom: 50px; - height: 100%; - outline: none; /* Prevent dragging from highlighting the element */ - position: relative; -} -.CodeMirror-sizer { - position: relative; - border-right: 50px solid transparent; -} - -/* The fake, visible scrollbars. Used to force redraw during scrolling - before actual scrolling happens, thus preventing shaking and - flickering artifacts. */ -.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - position: absolute; - z-index: 6; - display: none; - outline: none; -} -.CodeMirror-vscrollbar { - right: 0; top: 0; - overflow-x: hidden; - overflow-y: scroll; -} -.CodeMirror-hscrollbar { - bottom: 0; left: 0; - overflow-y: hidden; - overflow-x: scroll; -} -.CodeMirror-scrollbar-filler { - right: 0; bottom: 0; -} -.CodeMirror-gutter-filler { - left: 0; bottom: 0; -} - -.CodeMirror-gutters { - position: absolute; left: 0; top: 0; - min-height: 100%; - z-index: 3; -} -.CodeMirror-gutter { - white-space: normal; - height: 100%; - display: inline-block; - vertical-align: top; - margin-bottom: -50px; -} -.CodeMirror-gutter-wrapper { - position: absolute; - z-index: 4; - background: none !important; - border: none !important; -} -.CodeMirror-gutter-background { - position: absolute; - top: 0; bottom: 0; - z-index: 4; -} -.CodeMirror-gutter-elt { - position: absolute; - cursor: default; - z-index: 4; -} -.CodeMirror-gutter-wrapper ::selection { background-color: transparent } -.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } - -.CodeMirror-lines { - cursor: text; - min-height: 1px; /* prevents collapsing before first draw */ -} -.CodeMirror pre.CodeMirror-line, -.CodeMirror pre.CodeMirror-line-like { - /* Reset some styles that the rest of the page might have set */ - -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; - border-width: 0; - background: transparent; - font-family: inherit; - font-size: inherit; - margin: 0; - white-space: pre; - word-wrap: normal; - line-height: inherit; - color: inherit; - z-index: 2; - position: relative; - overflow: visible; - -webkit-tap-highlight-color: transparent; - -webkit-font-variant-ligatures: contextual; - font-variant-ligatures: contextual; -} -.CodeMirror-wrap pre.CodeMirror-line, -.CodeMirror-wrap pre.CodeMirror-line-like { - word-wrap: break-word; - white-space: pre-wrap; - word-break: normal; -} - -.CodeMirror-linebackground { - position: absolute; - left: 0; right: 0; top: 0; bottom: 0; - z-index: 0; -} - -.CodeMirror-linewidget { - position: relative; - z-index: 2; - padding: 0.1px; /* Force widget margins to stay inside of the container */ -} - -.CodeMirror-widget {} - -.CodeMirror-rtl pre { direction: rtl; } - -.CodeMirror-code { - outline: none; -} - -/* Force content-box sizing for the elements where we expect it */ -.CodeMirror-scroll, -.CodeMirror-sizer, -.CodeMirror-gutter, -.CodeMirror-gutters, -.CodeMirror-linenumber { - -moz-box-sizing: content-box; - box-sizing: content-box; -} - -.CodeMirror-measure { - position: absolute; - width: 100%; - height: 0; - overflow: hidden; - visibility: hidden; -} - -.CodeMirror-cursor { - position: absolute; - pointer-events: none; -} -.CodeMirror-measure pre { position: static; } - -div.CodeMirror-cursors { - visibility: hidden; - position: relative; - z-index: 3; -} -div.CodeMirror-dragcursors { - visibility: visible; -} - -.CodeMirror-focused div.CodeMirror-cursors { - visibility: visible; -} - -.CodeMirror-selected { background: #d9d9d9; } -.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } -.CodeMirror-crosshair { cursor: crosshair; } -.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } -.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } - -.cm-searching { - background-color: #ffa; - background-color: rgba(255, 255, 0, .4); -} - -/* Used to force a border model for a node */ -.cm-force-border { padding-right: .1px; } - -@media print { - /* Hide the cursor when printing */ - .CodeMirror div.CodeMirror-cursors { - visibility: hidden; - } -} - -/* See issue #2901 */ -.cm-tab-wrap-hack:after { content: ''; } - -/* Help users use markselection to safely style text background */ -span.CodeMirror-selectedtext { background: none; } diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/flatpickr.min.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/flatpickr.min.css deleted file mode 100644 index 292438b5a32..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/flatpickr.min.css +++ /dev/null @@ -1,13 +0,0 @@ -.flatpickr-calendar{background:transparent;opacity:0;display:none;text-align:center;visibility:hidden;padding:0;-webkit-animation:none;animation:none;direction:ltr;border:0;font-size:14px;line-height:24px;border-radius:5px;position:absolute;width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-touch-action:manipulation;touch-action:manipulation;background:#fff;-webkit-box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);}.flatpickr-calendar.open,.flatpickr-calendar.inline{opacity:1;max-height:640px;visibility:visible}.flatpickr-calendar.open{display:inline-block;z-index:99999}.flatpickr-calendar.animate.open{-webkit-animation:fpFadeInDown 300ms cubic-bezier(.23,1,.32,1);animation:fpFadeInDown 300ms cubic-bezier(.23,1,.32,1)}.flatpickr-calendar.inline{display:block;position:relative;top:2px}.flatpickr-calendar.static{position:absolute;top:calc(100% + 2px);}.flatpickr-calendar.static.open{z-index:999;display:block}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7){-webkit-box-shadow:none !important;box-shadow:none !important}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1){-webkit-box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6}.flatpickr-calendar .hasWeeks .dayContainer,.flatpickr-calendar .hasTime .dayContainer{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.flatpickr-calendar .hasWeeks .dayContainer{border-left:0}.flatpickr-calendar.showTimeInput.hasTime .flatpickr-time{height:40px;border-top:1px solid #e6e6e6}.flatpickr-calendar.noCalendar.hasTime .flatpickr-time{height:auto}.flatpickr-calendar:before,.flatpickr-calendar:after{position:absolute;display:block;pointer-events:none;border:solid transparent;content:'';height:0;width:0;left:22px}.flatpickr-calendar.rightMost:before,.flatpickr-calendar.rightMost:after{left:auto;right:22px}.flatpickr-calendar:before{border-width:5px;margin:0 -5px}.flatpickr-calendar:after{border-width:4px;margin:0 -4px}.flatpickr-calendar.arrowTop:before,.flatpickr-calendar.arrowTop:after{bottom:100%}.flatpickr-calendar.arrowTop:before{border-bottom-color:#e6e6e6}.flatpickr-calendar.arrowTop:after{border-bottom-color:#fff}.flatpickr-calendar.arrowBottom:before,.flatpickr-calendar.arrowBottom:after{top:100%}.flatpickr-calendar.arrowBottom:before{border-top-color:#e6e6e6}.flatpickr-calendar.arrowBottom:after{border-top-color:#fff}.flatpickr-calendar:focus{outline:0}.flatpickr-wrapper{position:relative;display:inline-block}.flatpickr-months{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}.flatpickr-months .flatpickr-month{background:transparent;color:rgba(0,0,0,0.9);fill:rgba(0,0,0,0.9);height:28px;line-height:1;text-align:center;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:hidden;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.flatpickr-months .flatpickr-prev-month,.flatpickr-months .flatpickr-next-month{text-decoration:none;cursor:pointer;position:absolute;top:0;line-height:16px;height:28px;padding:10px;z-index:3;}.flatpickr-months .flatpickr-prev-month.disabled,.flatpickr-months .flatpickr-next-month.disabled{display:none}.flatpickr-months .flatpickr-prev-month i,.flatpickr-months .flatpickr-next-month i{position:relative}.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,.flatpickr-months .flatpickr-next-month.flatpickr-prev-month{/* - /*rtl:begin:ignore*/left:0;/* - /*rtl:end:ignore*/}/* - /*rtl:begin:ignore*/ -/* - /*rtl:end:ignore*/ -.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,.flatpickr-months .flatpickr-next-month.flatpickr-next-month{/* - /*rtl:begin:ignore*/right:0;/* - /*rtl:end:ignore*/}/* - /*rtl:begin:ignore*/ -/* - /*rtl:end:ignore*/ -.flatpickr-months .flatpickr-prev-month:hover,.flatpickr-months .flatpickr-next-month:hover{color:#959ea9;}.flatpickr-months .flatpickr-prev-month:hover svg,.flatpickr-months .flatpickr-next-month:hover svg{fill:#f64747}.flatpickr-months .flatpickr-prev-month svg,.flatpickr-months .flatpickr-next-month svg{width:14px;height:14px;}.flatpickr-months .flatpickr-prev-month svg path,.flatpickr-months .flatpickr-next-month svg path{-webkit-transition:fill .1s;transition:fill .1s;fill:inherit}.numInputWrapper{position:relative;height:auto;}.numInputWrapper input,.numInputWrapper span{display:inline-block}.numInputWrapper input{width:100%;}.numInputWrapper input::-ms-clear{display:none}.numInputWrapper span{position:absolute;right:0;width:14px;padding:0 4px 0 2px;height:50%;line-height:50%;opacity:0;cursor:pointer;border:1px solid rgba(57,57,57,0.15);-webkit-box-sizing:border-box;box-sizing:border-box;}.numInputWrapper span:hover{background:rgba(0,0,0,0.1)}.numInputWrapper span:active{background:rgba(0,0,0,0.2)}.numInputWrapper span:after{display:block;content:"";position:absolute}.numInputWrapper span.arrowUp{top:0;border-bottom:0;}.numInputWrapper span.arrowUp:after{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:4px solid rgba(57,57,57,0.6);top:26%}.numInputWrapper span.arrowDown{top:50%;}.numInputWrapper span.arrowDown:after{border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid rgba(57,57,57,0.6);top:40%}.numInputWrapper span svg{width:inherit;height:auto;}.numInputWrapper span svg path{fill:rgba(0,0,0,0.5)}.numInputWrapper:hover{background:rgba(0,0,0,0.05);}.numInputWrapper:hover span{opacity:1}.flatpickr-current-month{font-size:135%;line-height:inherit;font-weight:300;color:inherit;position:absolute;width:75%;left:12.5%;padding:6.16px 0 0 0;line-height:1;height:28px;display:inline-block;text-align:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}.flatpickr-current-month span.cur-month{font-family:inherit;font-weight:700;color:inherit;display:inline-block;margin-left:.5ch;padding:0;}.flatpickr-current-month span.cur-month:hover{background:rgba(0,0,0,0.05)}.flatpickr-current-month .numInputWrapper{width:6ch;width:7ch\0;display:inline-block;}.flatpickr-current-month .numInputWrapper span.arrowUp:after{border-bottom-color:rgba(0,0,0,0.9)}.flatpickr-current-month .numInputWrapper span.arrowDown:after{border-top-color:rgba(0,0,0,0.9)}.flatpickr-current-month input.cur-year{background:transparent;-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;cursor:text;padding:0 0 0 .5ch;margin:0;display:inline-block;font-size:inherit;font-family:inherit;font-weight:300;line-height:inherit;height:auto;border:0;border-radius:0;vertical-align:initial;}.flatpickr-current-month input.cur-year:focus{outline:0}.flatpickr-current-month input.cur-year[disabled],.flatpickr-current-month input.cur-year[disabled]:hover{font-size:100%;color:rgba(0,0,0,0.5);background:transparent;pointer-events:none}.flatpickr-weekdays{background:transparent;text-align:center;overflow:hidden;width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:28px;}.flatpickr-weekdays .flatpickr-weekdaycontainer{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}span.flatpickr-weekday{cursor:default;font-size:90%;background:transparent;color:rgba(0,0,0,0.54);line-height:1;margin:0;text-align:center;display:block;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-weight:bolder}.dayContainer,.flatpickr-weeks{padding:1px 0 0 0}.flatpickr-days{position:relative;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;width:307.875px;}.flatpickr-days:focus{outline:0}.dayContainer{padding:0;outline:0;text-align:left;width:307.875px;min-width:307.875px;max-width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;display:inline-block;display:-ms-flexbox;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-wrap:wrap;-ms-flex-pack:justify;-webkit-justify-content:space-around;justify-content:space-around;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1;}.dayContainer + .dayContainer{-webkit-box-shadow:-1px 0 0 #e6e6e6;box-shadow:-1px 0 0 #e6e6e6}.flatpickr-day{background:none;border:1px solid transparent;border-radius:150px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#393939;cursor:pointer;font-weight:400;width:14.2857143%;-webkit-flex-basis:14.2857143%;-ms-flex-preferred-size:14.2857143%;flex-basis:14.2857143%;max-width:39px;height:39px;line-height:39px;margin:0;display:inline-block;position:relative;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;}.flatpickr-day.inRange,.flatpickr-day.prevMonthDay.inRange,.flatpickr-day.nextMonthDay.inRange,.flatpickr-day.today.inRange,.flatpickr-day.prevMonthDay.today.inRange,.flatpickr-day.nextMonthDay.today.inRange,.flatpickr-day:hover,.flatpickr-day.prevMonthDay:hover,.flatpickr-day.nextMonthDay:hover,.flatpickr-day:focus,.flatpickr-day.prevMonthDay:focus,.flatpickr-day.nextMonthDay:focus{cursor:pointer;outline:0;background:#e6e6e6;border-color:#e6e6e6}.flatpickr-day.today{border-color:#959ea9;}.flatpickr-day.today:hover,.flatpickr-day.today:focus{border-color:#959ea9;background:#959ea9;color:#fff}.flatpickr-day.selected,.flatpickr-day.startRange,.flatpickr-day.endRange,.flatpickr-day.selected.inRange,.flatpickr-day.startRange.inRange,.flatpickr-day.endRange.inRange,.flatpickr-day.selected:focus,.flatpickr-day.startRange:focus,.flatpickr-day.endRange:focus,.flatpickr-day.selected:hover,.flatpickr-day.startRange:hover,.flatpickr-day.endRange:hover,.flatpickr-day.selected.prevMonthDay,.flatpickr-day.startRange.prevMonthDay,.flatpickr-day.endRange.prevMonthDay,.flatpickr-day.selected.nextMonthDay,.flatpickr-day.startRange.nextMonthDay,.flatpickr-day.endRange.nextMonthDay{background:#569ff7;-webkit-box-shadow:none;box-shadow:none;color:#fff;border-color:#569ff7}.flatpickr-day.selected.startRange,.flatpickr-day.startRange.startRange,.flatpickr-day.endRange.startRange{border-radius:50px 0 0 50px}.flatpickr-day.selected.endRange,.flatpickr-day.startRange.endRange,.flatpickr-day.endRange.endRange{border-radius:0 50px 50px 0}.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)){-webkit-box-shadow:-10px 0 0 #569ff7;box-shadow:-10px 0 0 #569ff7}.flatpickr-day.selected.startRange.endRange,.flatpickr-day.startRange.startRange.endRange,.flatpickr-day.endRange.startRange.endRange{border-radius:50px}.flatpickr-day.inRange{border-radius:0;-webkit-box-shadow:-5px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-5px 0 0 #e6e6e6,5px 0 0 #e6e6e6}.flatpickr-day.disabled,.flatpickr-day.disabled:hover,.flatpickr-day.prevMonthDay,.flatpickr-day.nextMonthDay,.flatpickr-day.notAllowed,.flatpickr-day.notAllowed.prevMonthDay,.flatpickr-day.notAllowed.nextMonthDay{color:rgba(57,57,57,0.3);background:transparent;border-color:transparent;cursor:default}.flatpickr-day.disabled,.flatpickr-day.disabled:hover{cursor:not-allowed;color:rgba(57,57,57,0.1)}.flatpickr-day.week.selected{border-radius:0;-webkit-box-shadow:-5px 0 0 #569ff7,5px 0 0 #569ff7;box-shadow:-5px 0 0 #569ff7,5px 0 0 #569ff7}.flatpickr-day.hidden{visibility:hidden}.rangeMode .flatpickr-day{margin-top:1px}.flatpickr-weekwrapper{display:inline-block;float:left;}.flatpickr-weekwrapper .flatpickr-weeks{padding:0 12px;-webkit-box-shadow:1px 0 0 #e6e6e6;box-shadow:1px 0 0 #e6e6e6}.flatpickr-weekwrapper .flatpickr-weekday{float:none;width:100%;line-height:28px}.flatpickr-weekwrapper span.flatpickr-day,.flatpickr-weekwrapper span.flatpickr-day:hover{display:block;width:100%;max-width:none;color:rgba(57,57,57,0.3);background:transparent;cursor:default;border:none}.flatpickr-innerContainer{display:block;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;}.flatpickr-rContainer{display:inline-block;padding:0;-webkit-box-sizing:border-box;box-sizing:border-box}.flatpickr-time{text-align:center;outline:0;display:block;height:0;line-height:40px;max-height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}.flatpickr-time:after{content:"";display:table;clear:both}.flatpickr-time .numInputWrapper{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;width:40%;height:40px;float:left;}.flatpickr-time .numInputWrapper span.arrowUp:after{border-bottom-color:#393939}.flatpickr-time .numInputWrapper span.arrowDown:after{border-top-color:#393939}.flatpickr-time.hasSeconds .numInputWrapper{width:26%}.flatpickr-time.time24hr .numInputWrapper{width:49%}.flatpickr-time input{background:transparent;-webkit-box-shadow:none;box-shadow:none;border:0;border-radius:0;text-align:center;margin:0;padding:0;height:inherit;line-height:inherit;cursor:pointer;color:#393939;font-size:14px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;}.flatpickr-time input.flatpickr-hour{font-weight:bold}.flatpickr-time input.flatpickr-minute,.flatpickr-time input.flatpickr-second{font-weight:400}.flatpickr-time input:focus{outline:0;border:0}.flatpickr-time .flatpickr-time-separator,.flatpickr-time .flatpickr-am-pm{height:inherit;display:inline-block;float:left;line-height:inherit;color:#393939;font-weight:bold;width:2%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.flatpickr-time .flatpickr-am-pm{outline:0;width:18%;cursor:pointer;text-align:center;font-weight:400;}.flatpickr-time .flatpickr-am-pm:hover,.flatpickr-time .flatpickr-am-pm:focus{background:#f0f0f0}.flatpickr-input[readonly]{cursor:pointer}@-webkit-keyframes fpFadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fpFadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}} \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/area-chart.svg b/web-bundle/src/main/resources/com/graphhopper/maps/css/images/area-chart.svg deleted file mode 100755 index cf8b4a36b01..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/area-chart.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/elevation.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/images/elevation.png deleted file mode 100644 index 68fb537e42d..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/elevation.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/layers-2x.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/images/layers-2x.png deleted file mode 100644 index 200c333dca9..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/layers-2x.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/layers.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/images/layers.png deleted file mode 100644 index 1a72e5784b2..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/layers.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-icon-2x.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-icon-2x.png deleted file mode 100644 index 88f9e501888..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-icon-2x.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-icon.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-icon.png deleted file mode 100644 index 950edf24677..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-icon.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-shadow.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-shadow.png deleted file mode 100644 index 9fd2979532a..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/marker-shadow.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/remove.svg b/web-bundle/src/main/resources/com/graphhopper/maps/css/images/remove.svg deleted file mode 100755 index f063b3df429..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/images/remove.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.contextmenu.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.contextmenu.css deleted file mode 100644 index 0b5e2defca7..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.contextmenu.css +++ /dev/null @@ -1,54 +0,0 @@ -.leaflet-contextmenu { - display: none; - box-shadow: 0 1px 7px rgba(0,0,0,0.4); - -webkit-border-radius: 4px; - border-radius: 4px; - padding: 4px 0; - background-color: #fff; - cursor: default; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} - -.leaflet-contextmenu a.leaflet-contextmenu-item { - display: block; - color: #222; - font-size: 12px; - line-height: 20px; - text-decoration: none; - padding: 0 12px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - cursor: default; - outline: none; -} - -.leaflet-contextmenu a.leaflet-contextmenu-item-disabled { - opacity: 0.5; -} - -.leaflet-contextmenu a.leaflet-contextmenu-item.over { - background-color: #f4f4f4; - border-top: 1px solid #f0f0f0; - border-bottom: 1px solid #f0f0f0; -} - -.leaflet-contextmenu a.leaflet-contextmenu-item-disabled.over { - background-color: inherit; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; -} - -.leaflet-contextmenu-icon { - margin: 2px 8px 0 0; - width: 16px; - height: 16px; - float: left; - border: 0; -} - -.leaflet-contextmenu-separator { - border-bottom: 1px solid #ccc; - margin: 5px 0; -} diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.css deleted file mode 100644 index 609a6624536..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.css +++ /dev/null @@ -1,640 +0,0 @@ -/* required styles */ - -.leaflet-pane, -.leaflet-tile, -.leaflet-marker-icon, -.leaflet-marker-shadow, -.leaflet-tile-container, -.leaflet-pane > svg, -.leaflet-pane > canvas, -.leaflet-zoom-box, -.leaflet-image-layer, -.leaflet-layer { - position: absolute; - left: 0; - top: 0; - } -.leaflet-container { - overflow: hidden; - } -.leaflet-tile, -.leaflet-marker-icon, -.leaflet-marker-shadow { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - -webkit-user-drag: none; - } -/* Prevents IE11 from highlighting tiles in blue */ -.leaflet-tile::selection { - background: transparent; -} -/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ -.leaflet-safari .leaflet-tile { - image-rendering: -webkit-optimize-contrast; - } -/* hack that prevents hw layers "stretching" when loading new tiles */ -.leaflet-safari .leaflet-tile-container { - width: 1600px; - height: 1600px; - -webkit-transform-origin: 0 0; - } -.leaflet-marker-icon, -.leaflet-marker-shadow { - display: block; - } -/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ -/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ -.leaflet-container .leaflet-overlay-pane svg, -.leaflet-container .leaflet-marker-pane img, -.leaflet-container .leaflet-shadow-pane img, -.leaflet-container .leaflet-tile-pane img, -.leaflet-container img.leaflet-image-layer, -.leaflet-container .leaflet-tile { - max-width: none !important; - max-height: none !important; - } - -.leaflet-container.leaflet-touch-zoom { - -ms-touch-action: pan-x pan-y; - touch-action: pan-x pan-y; - } -.leaflet-container.leaflet-touch-drag { - -ms-touch-action: pinch-zoom; - /* Fallback for FF which doesn't support pinch-zoom */ - touch-action: none; - touch-action: pinch-zoom; -} -.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { - -ms-touch-action: none; - touch-action: none; -} -.leaflet-container { - -webkit-tap-highlight-color: transparent; -} -.leaflet-container a { - -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); -} -.leaflet-tile { - filter: inherit; - visibility: hidden; - } -.leaflet-tile-loaded { - visibility: inherit; - } -.leaflet-zoom-box { - width: 0; - height: 0; - -moz-box-sizing: border-box; - box-sizing: border-box; - z-index: 800; - } -/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ -.leaflet-overlay-pane svg { - -moz-user-select: none; - } - -.leaflet-pane { z-index: 400; } - -.leaflet-tile-pane { z-index: 200; } -.leaflet-overlay-pane { z-index: 400; } -.leaflet-shadow-pane { z-index: 500; } -.leaflet-marker-pane { z-index: 600; } -.leaflet-tooltip-pane { z-index: 650; } -.leaflet-popup-pane { z-index: 700; } - -.leaflet-map-pane canvas { z-index: 100; } -.leaflet-map-pane svg { z-index: 200; } - -.leaflet-vml-shape { - width: 1px; - height: 1px; - } -.lvml { - behavior: url(#default#VML); - display: inline-block; - position: absolute; - } - - -/* control positioning */ - -.leaflet-control { - position: relative; - z-index: 800; - pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ - pointer-events: auto; - } -.leaflet-top, -.leaflet-bottom { - position: absolute; - z-index: 1000; - pointer-events: none; - } -.leaflet-top { - top: 0; - } -.leaflet-right { - right: 0; - } -.leaflet-bottom { - bottom: 0; - } -.leaflet-left { - left: 0; - } -.leaflet-control { - float: left; - clear: both; - } -.leaflet-right .leaflet-control { - float: right; - } -.leaflet-top .leaflet-control { - margin-top: 10px; - } -.leaflet-bottom .leaflet-control { - margin-bottom: 10px; - } -.leaflet-left .leaflet-control { - margin-left: 10px; - } -.leaflet-right .leaflet-control { - margin-right: 10px; - } - - -/* zoom and fade animations */ - -.leaflet-fade-anim .leaflet-tile { - will-change: opacity; - } -.leaflet-fade-anim .leaflet-popup { - opacity: 0; - -webkit-transition: opacity 0.2s linear; - -moz-transition: opacity 0.2s linear; - transition: opacity 0.2s linear; - } -.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { - opacity: 1; - } -.leaflet-zoom-animated { - -webkit-transform-origin: 0 0; - -ms-transform-origin: 0 0; - transform-origin: 0 0; - } -.leaflet-zoom-anim .leaflet-zoom-animated { - will-change: transform; - } -.leaflet-zoom-anim .leaflet-zoom-animated { - -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); - -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); - transition: transform 0.25s cubic-bezier(0,0,0.25,1); - } -.leaflet-zoom-anim .leaflet-tile, -.leaflet-pan-anim .leaflet-tile { - -webkit-transition: none; - -moz-transition: none; - transition: none; - } - -.leaflet-zoom-anim .leaflet-zoom-hide { - visibility: hidden; - } - - -/* cursors */ - -.leaflet-interactive { - cursor: pointer; - } -.leaflet-grab { - cursor: -webkit-grab; - cursor: -moz-grab; - cursor: grab; - } -.leaflet-crosshair, -.leaflet-crosshair .leaflet-interactive { - cursor: crosshair; - } -.leaflet-popup-pane, -.leaflet-control { - cursor: auto; - } -.leaflet-dragging .leaflet-grab, -.leaflet-dragging .leaflet-grab .leaflet-interactive, -.leaflet-dragging .leaflet-marker-draggable { - cursor: move; - cursor: -webkit-grabbing; - cursor: -moz-grabbing; - cursor: grabbing; - } - -/* marker & overlays interactivity */ -.leaflet-marker-icon, -.leaflet-marker-shadow, -.leaflet-image-layer, -.leaflet-pane > svg path, -.leaflet-tile-container { - pointer-events: none; - } - -.leaflet-marker-icon.leaflet-interactive, -.leaflet-image-layer.leaflet-interactive, -.leaflet-pane > svg path.leaflet-interactive, -svg.leaflet-image-layer.leaflet-interactive path { - pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ - pointer-events: auto; - } - -/* visual tweaks */ - -.leaflet-container { - background: #ddd; - outline: 0; - } -.leaflet-container a { - color: #0078A8; - } -.leaflet-container a.leaflet-active { - outline: 2px solid orange; - } -.leaflet-zoom-box { - border: 2px dotted #38f; - background: rgba(255,255,255,0.5); - } - - -/* general typography */ -.leaflet-container { - font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; - } - - -/* general toolbar styles */ - -.leaflet-bar { - box-shadow: 0 1px 5px rgba(0,0,0,0.65); - border-radius: 4px; - } -.leaflet-bar a, -.leaflet-bar a:hover { - background-color: #fff; - border-bottom: 1px solid #ccc; - width: 26px; - height: 26px; - line-height: 26px; - display: block; - text-align: center; - text-decoration: none; - color: black; - } -.leaflet-bar a, -.leaflet-control-layers-toggle { - background-position: 50% 50%; - background-repeat: no-repeat; - display: block; - } -.leaflet-bar a:hover { - background-color: #f4f4f4; - } -.leaflet-bar a:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - } -.leaflet-bar a:last-child { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom: none; - } -.leaflet-bar a.leaflet-disabled { - cursor: default; - background-color: #f4f4f4; - color: #bbb; - } - -.leaflet-touch .leaflet-bar a { - width: 30px; - height: 30px; - line-height: 30px; - } -.leaflet-touch .leaflet-bar a:first-child { - border-top-left-radius: 2px; - border-top-right-radius: 2px; - } -.leaflet-touch .leaflet-bar a:last-child { - border-bottom-left-radius: 2px; - border-bottom-right-radius: 2px; - } - -/* zoom control */ - -.leaflet-control-zoom-in, -.leaflet-control-zoom-out { - font: bold 18px 'Lucida Console', Monaco, monospace; - text-indent: 1px; - } - -.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { - font-size: 22px; - } - - -/* layers control */ - -.leaflet-control-layers { - box-shadow: 0 1px 5px rgba(0,0,0,0.4); - background: #fff; - border-radius: 5px; - } -.leaflet-control-layers-toggle { - background-image: url(images/layers.png); - width: 36px; - height: 36px; - } -.leaflet-retina .leaflet-control-layers-toggle { - background-image: url(images/layers-2x.png); - background-size: 26px 26px; - } -.leaflet-touch .leaflet-control-layers-toggle { - width: 44px; - height: 44px; - } -.leaflet-control-layers .leaflet-control-layers-list, -.leaflet-control-layers-expanded .leaflet-control-layers-toggle { - display: none; - } -.leaflet-control-layers-expanded .leaflet-control-layers-list { - display: block; - position: relative; - } -.leaflet-control-layers-expanded { - padding: 6px 10px 6px 6px; - color: #333; - background: #fff; - } -.leaflet-control-layers-scrollbar { - overflow-y: scroll; - overflow-x: hidden; - padding-right: 5px; - } -.leaflet-control-layers-selector { - margin-top: 2px; - position: relative; - top: 1px; - } -.leaflet-control-layers label { - display: block; - } -.leaflet-control-layers-separator { - height: 0; - border-top: 1px solid #ddd; - margin: 5px -10px 5px -6px; - } - -/* Default icon URLs */ -.leaflet-default-icon-path { - background-image: url(images/marker-icon.png); - } - - -/* attribution and scale controls */ - -.leaflet-container .leaflet-control-attribution { - background: #fff; - background: rgba(255, 255, 255, 0.7); - margin: 0; - } -.leaflet-control-attribution, -.leaflet-control-scale-line { - padding: 0 5px; - color: #333; - } -.leaflet-control-attribution a { - text-decoration: none; - } -.leaflet-control-attribution a:hover { - text-decoration: underline; - } -.leaflet-container .leaflet-control-attribution, -.leaflet-container .leaflet-control-scale { - font-size: 11px; - } -.leaflet-left .leaflet-control-scale { - margin-left: 5px; - } -.leaflet-bottom .leaflet-control-scale { - margin-bottom: 5px; - } -.leaflet-control-scale-line { - border: 2px solid #777; - border-top: none; - line-height: 1.1; - padding: 2px 5px 1px; - font-size: 11px; - white-space: nowrap; - overflow: hidden; - -moz-box-sizing: border-box; - box-sizing: border-box; - - background: #fff; - background: rgba(255, 255, 255, 0.5); - } -.leaflet-control-scale-line:not(:first-child) { - border-top: 2px solid #777; - border-bottom: none; - margin-top: -2px; - } -.leaflet-control-scale-line:not(:first-child):not(:last-child) { - border-bottom: 2px solid #777; - } - -.leaflet-touch .leaflet-control-attribution, -.leaflet-touch .leaflet-control-layers, -.leaflet-touch .leaflet-bar { - box-shadow: none; - } -.leaflet-touch .leaflet-control-layers, -.leaflet-touch .leaflet-bar { - border: 2px solid rgba(0,0,0,0.2); - background-clip: padding-box; - } - - -/* popup */ - -.leaflet-popup { - position: absolute; - text-align: center; - margin-bottom: 20px; - } -.leaflet-popup-content-wrapper { - padding: 1px; - text-align: left; - border-radius: 12px; - } -.leaflet-popup-content { - margin: 13px 19px; - line-height: 1.4; - } -.leaflet-popup-content p { - margin: 18px 0; - } -.leaflet-popup-tip-container { - width: 40px; - height: 20px; - position: absolute; - left: 50%; - margin-left: -20px; - overflow: hidden; - pointer-events: none; - } -.leaflet-popup-tip { - width: 17px; - height: 17px; - padding: 1px; - - margin: -10px auto 0; - - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); - } -.leaflet-popup-content-wrapper, -.leaflet-popup-tip { - background: white; - color: #333; - box-shadow: 0 3px 14px rgba(0,0,0,0.4); - } -.leaflet-container a.leaflet-popup-close-button { - position: absolute; - top: 0; - right: 0; - padding: 4px 4px 0 0; - border: none; - text-align: center; - width: 18px; - height: 14px; - font: 16px/14px Tahoma, Verdana, sans-serif; - color: #c3c3c3; - text-decoration: none; - font-weight: bold; - background: transparent; - } -.leaflet-container a.leaflet-popup-close-button:hover { - color: #999; - } -.leaflet-popup-scrolled { - overflow: auto; - border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; - } - -.leaflet-oldie .leaflet-popup-content-wrapper { - zoom: 1; - } -.leaflet-oldie .leaflet-popup-tip { - width: 24px; - margin: 0 auto; - - -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; - filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); - } -.leaflet-oldie .leaflet-popup-tip-container { - margin-top: -1px; - } - -.leaflet-oldie .leaflet-control-zoom, -.leaflet-oldie .leaflet-control-layers, -.leaflet-oldie .leaflet-popup-content-wrapper, -.leaflet-oldie .leaflet-popup-tip { - border: 1px solid #999; - } - - -/* div icon */ - -.leaflet-div-icon { - background: #fff; - border: 1px solid #666; - } - - -/* Tooltip */ -/* Base styles for the element that has a tooltip */ -.leaflet-tooltip { - position: absolute; - padding: 6px; - background-color: #fff; - border: 1px solid #fff; - border-radius: 3px; - color: #222; - white-space: nowrap; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - pointer-events: none; - box-shadow: 0 1px 3px rgba(0,0,0,0.4); - } -.leaflet-tooltip.leaflet-clickable { - cursor: pointer; - pointer-events: auto; - } -.leaflet-tooltip-top:before, -.leaflet-tooltip-bottom:before, -.leaflet-tooltip-left:before, -.leaflet-tooltip-right:before { - position: absolute; - pointer-events: none; - border: 6px solid transparent; - background: transparent; - content: ""; - } - -/* Directions */ - -.leaflet-tooltip-bottom { - margin-top: 6px; -} -.leaflet-tooltip-top { - margin-top: -6px; -} -.leaflet-tooltip-bottom:before, -.leaflet-tooltip-top:before { - left: 50%; - margin-left: -6px; - } -.leaflet-tooltip-top:before { - bottom: 0; - margin-bottom: -12px; - border-top-color: #fff; - } -.leaflet-tooltip-bottom:before { - top: 0; - margin-top: -12px; - margin-left: -6px; - border-bottom-color: #fff; - } -.leaflet-tooltip-left { - margin-left: -6px; -} -.leaflet-tooltip-right { - margin-left: 6px; -} -.leaflet-tooltip-left:before, -.leaflet-tooltip-right:before { - top: 50%; - margin-top: -6px; - } -.leaflet-tooltip-left:before { - right: 0; - margin-right: -12px; - border-left-color: #fff; - } -.leaflet-tooltip-right:before { - left: 0; - margin-left: -12px; - border-right-color: #fff; - } diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.loading.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.loading.css deleted file mode 100644 index f4157bc6f31..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet.loading.css +++ /dev/null @@ -1,26 +0,0 @@ -.leaflet-control-loading:empty { - /* Spinner via ajaxload.info, base64-encoded */ - background-image: url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2NmhoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAAKAAEALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQACgACACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQACgADACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkEAAoABAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkEAAoABQAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkEAAoABgAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAAKAAcALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkEAAoACAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAAKAAkALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQACgAKACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQACgALACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA==); - background-repeat: no-repeat; -} - -.leaflet-control-loading, -.leaflet-control-zoom a.leaflet-control-loading, -.leaflet-control-zoomslider a.leaflet-control-loading, -.leaflet-control-layer-container { - display: none; -} - -.leaflet-control-loading.is-loading, -.leaflet-control-zoom a.leaflet-control-loading.is-loading, -.leaflet-control-zoomslider a.leaflet-control-loading.is-loading, -.leaflet-control-layer-container.is-loading { - display: block; -} - -/* Necessary for display consistency in Leaflet >= 0.6 */ -.leaflet-bar-part-bottom { - border-bottom: medium none; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet_numbered_markers.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet_numbered_markers.css deleted file mode 100644 index 2edafa072d2..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/leaflet_numbered_markers.css +++ /dev/null @@ -1,12 +0,0 @@ -.leaflet-div-icon { - background: transparent; - border: none; -} - -.leaflet-marker-icon .number{ - position: relative; - top: -41px; - font-size: 12px; - width: 25px; - text-align: center; -} \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/style.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/style.css deleted file mode 100644 index 91c21711f17..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/style.css +++ /dev/null @@ -1,528 +0,0 @@ -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - line-height: 1.4; - color: #111; - background-color: white; - margin: 0; - min-width: 300px; -} -#map { - /* set size via JS */ - cursor: default; -} - -#input { - float: left; - margin-left: 10px; - /*padding-right: 15px; */ -} - -#info { - margin-top: 10px; - display: none; - padding: 5px; -} - -#info a { - padding-right: 5px; -} - -#info ul { - margin-left:5px; - padding-left:5px; - list-style-type: none; -} - -.route_results { - max-height: 40%; -} - -#input { - min-height: 300px; - max-width: 270px; -} - -.instructions_info { - overflow: auto; - width: 100%; -} - -.route_description { - padding: 7px 5px; -} - -#input, #instructions, #footer { - width: 280px; -} - -.pointInput { - width: 215px; - float: left; -} - -.pointResolveFound a { - background-color: white; - padding-left: 4px; - padding-right: 3px; -} -.pointResolveFound { - overflow: hidden; - max-height: 24px; - font-size: 10px; - color: blue; - float: right; - cursor: pointer; - margin-top: -20px; - margin-right: 2px; -} - -.pointResolveError { - float: left; - font-size: 12px; - padding-bottom: 4px; - width: 100%; - text-align: right; -} - -.time_input { - display: none; - float: right; - padding-right: 20px; -} - -.time_input input { - float: left; - margin-left: 10px; - width: 220px; -} - -.boxSizingBorder { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -.warn { - color: orange; -} -.error, .pointResolveError { - color: red; -} -#moreattribution a { - text-decoration: none; - color: black -} -#moreattribution { - padding-top: 20px; - color: #666666 -} - -#locationpoints { - padding-bottom: 10px; - float: left; -} - -#locationpoints>div:nth-child(n+2) { - counter-increment: pointcounter; -} - -#locationpoints>div:nth-child(n+2):nth-last-child(n+3)::before { - content: counter(pointcounter); - position: absolute; - padding-top: 0.45em; - right: calc(100% - 1.2em); - text-align: right; - font-size: 60%; -} - -.pointDiv { - margin: 6px 0; -} -.pointDiv > img { - margin-top: 4px; - padding-left: 3px; - padding-right: 3px; - float: left; -} -.left { - float: left; -} -.right { - float: right; -} -#routeDetails { - padding: 15px; - font-size: 12px; - padding-bottom: 4px; -} -#versionDetails { - font-size: 10px; - padding-top: 4px; - padding-left: 15px; - padding-right: 15px; -} -.hidden { - display: none; -} -.no_link { - text-decoration: none; - color: black; -} -.no_link img { - margin-bottom: -10px; -} -.small_text { - font-size: small; -} -#options { - padding-bottom: 10px; - padding-left: 20px; - padding-right: 20px; -} -.vehicle-info-link { - padding-top: 3px; - float: right; - text-decoration: none; -} -.bold { - font-weight: bold; -} -#header_img { - padding-top: 15px; - padding-left: 20px; - padding-bottom: 15px; -} -#header_img img { - width: 230px; -} - -#gpx_dialog { - display: none; -} - -#gpxExportButton { - margin-top: -3px; -} -#gpxExportButton img { - padding-left: 6px; -} -#searchButton { - float: right; - margin-bottom: 5px; - margin-right: 5px; -} - -#searchButton:hover { - cursor: pointer; -} -.clear { - clear: both; -} -#hosting { - clear: both; - display: none; - /* float: left;*/ - padding-top: 4px; - padding-bottom: 10px; - /* color: #666666; */ - font-size: smaller; - opacity: 0.8; -} - -#footer { - position: fixed; - bottom: 5px; - left: 30px; -} - -#hosting a, .footer-link { - text-decoration: none; - color: #00cc33; -} -.footer-link { - padding-right: 12px; - font-size: 12px; -} - -#export-link { - padding-left: 3px; - padding-top: 3px; - float: left; -} - -tr.instruction { - cursor: pointer; - border-bottom: #dadada dashed thin; -} - -.instructions { - table-layout:fixed; - border-collapse: collapse; - padding-top: 10px; - width: 98%; - font-size: smaller; -} - -.instructions td { - padding: 7px 5px; -} - -td.instr_title { - width: 130px; -} -td.instr_distance { - min-width: 50px; - float: right; - color: gray; - text-align: right; -} - -.instructions tr .instr_pic { - width: 20px; -} -td img.pic { - max-height: 24px; - max-width: 16px; -} - -.vehicle-btn { - padding: 0; - width: 32px; - height: 32px; - border-width: 1px; - -moz-border-radius: 0px; - -webkit-border-radius: 0px; - background-image: linear-gradient(to bottom, white, #e7e7e7); -} - -.vehicle-btn img { - width: 24px; - height: 24px; - vertical-align: middle; -} - -.selectprofile { - background-color: #bbb; - -moz-border-radius: 0px; - -webkit-border-radius: 0px; - background-image: linear-gradient(to bottom, #9f9f9f, #e7e7e7); -} - -#more-vehicle-btn { - cursor: pointer; -} - -.autocomplete { - border: 1px solid #999; - background: #FFF; - overflow: auto; -} -.autocomplete strong { - font-weight: normal; - color: #04B431; -} -.autocomplete-suggestion { - padding: 5px 5px; - white-space: nowrap; - overflow: hidden; - font-size: small; - background-image: linear-gradient(to bottom, white, #e7e7e7); -} -.autocomplete-selected { - padding: 5px 5px; - background: #e7e7e7; - background-image: linear-gradient(to bottom, #9f9f9f, #e7e7e7); -} -.light { font-weight: lighter; color: #9f9f9f; font-size: smaller } -.wikilink { float: right; } -.wikilink img { max-width: 16px; } - -/* .nameseg { border-top: #dadada dashed thin; } */ -.cityseg { float: left; font-size: 10px; } -.moreseg { float: right; font-size: 9px; } - -.pointFlag { - width: 16px; - height: 16px; -} -.pointFlag:hover { cursor: ns-resize; } -.pointAdd { - padding-left: 3px; -} -.pointDelete { - float: left; - padding-left: 2px; -} -.pointDelete:hover, .pointAdd:hover { cursor: pointer; } -.pointDelete:disabled { - background: red; -} - -.expandDetails { - color: gray; - font-size:11px; - float: right; - font-weight: bold; - width: 20px; - height: 20px; - height: 20px; - margin: 0 10px; - padding: 0; - background-image: linear-gradient(to bottom, white, #e7e7e7); -} -#moreButton { - background-image: linear-gradient(to bottom, white, #e7e7e7); -} - -.ui-dialog { - /* Must be > 700, the z-index of .leaflet-popup-pane */ - z-index: 1000; -} - -.ui-dialog > .ui-widget-header { - padding: 1em; - border: 1px solid black; -} - -.ui-dialog .ui-dialog-content { - padding: 1em; -} - -.ui-dialog .ui-widget-content { - background: white; -} - -.ui-widget-content { - background-image: none; -} - -.ui-dialog .ui-dialog-titlebar { - padding: 10px; - background: #00cc33; -} - -.alt_route_img { - padding-left: 2px; - margin-bottom: -3px; -} - -#route_result_tabs { - list-style: none; - padding: 0; - margin: 0; -} - -#route_result_tabs li { - background-color: #FFF; - cursor: pointer; - float: left; - padding: 5px 10px; -} - -#route_result_tabs li.current { - font-weight: bold; - background-image: linear-gradient(to bottom, #e7e7e7, white); - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} - -.route_result_tab { - border: lightgray groove thin; - display: none; -} - -.route_result_tab.current { - display: block; -} - -#donate_form { - padding: 0; -} - -#donate_form_button { - background: none; - border: none; - padding-left: 0; - margin-top: 5px; - cursor: pointer; - float: left; -} - -.gray { - color: gray; -} - -.plain_text_button { - font-size: small; - padding: 0; - border: none; - background: none; - cursor: pointer; -} - -.fullscreen-reverse-btn, .fullscreen-btn { - cursor: pointer; - width: 30px; - height: 30px; - border-radius: 4px; - background-color: white; - background-position: 50% 50%; - background-repeat: no-repeat; - box-shadow: none; - border: 2px solid rgba(0, 0, 0, 0.2); -} - -.fullscreen-btn { - background-image: url("../img/full.png"); -} - -.fullscreen-reverse-btn { - background-image: url("../img/full-reverse.png"); -} - -.CodeMirror-lint-tooltip { - /* tool tip should not be hidden by map*/ - z-index: 2000; -} - -.CodeMirror-hints { - /* hints should not be hidden by map*/ - z-index: 1000; -} - -/* override default json mode colors in custom model editor */ -.cm-s-default .cm-string { - color: #067D17; -} -.cm-s-default .cm-property { - color: #871094; -} -.cm-s-default .cm-number { - color: #1750EB; -} -.cm-s-default .cm-atom { - color: #0033B3; -} - -#custom-model-util-buttons button { - background: none; - border: none; - padding: 0; - cursor: pointer; - text-decoration: underline; - color: #00cc33; -} - -#custom-model-docs { - color: #00cc33; -} - -#custom-model-search-button { - float: right; - margin-right: 5px; - cursor: pointer -} - -#custom-model-search-button:disabled { - cursor: default; -} - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/LICENSE.txt b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/LICENSE.txt deleted file mode 100644 index 4819e54213f..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/LICENSE.txt +++ /dev/null @@ -1,43 +0,0 @@ -Copyright jQuery Foundation and other contributors, https://jquery.org/ - -This software consists of voluntary contributions made by many -individuals. For exact contribution history, see the revision history -available at https://github.com/jquery/jquery-ui - -The following license applies to all parts of this software except as -documented below: - -==== - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -==== - -Copyright and related rights for sample code are waived via CC0. Sample -code is defined as all source code contained within the demos directory. - -CC0: http://creativecommons.org/publicdomain/zero/1.0/ - -==== - -All files located in the node_modules and external directories are -externally maintained libraries used by this software which have their -own licenses; we recommend you read them, as their terms may differ from -the terms above. diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png deleted file mode 100644 index a9229606fa0..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png deleted file mode 100644 index d87fd6dfca1..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png deleted file mode 100644 index 29e3d7c105a..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png deleted file mode 100644 index f363a133a53..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png deleted file mode 100644 index 09959c62eb3..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png deleted file mode 100644 index 12c589efc10..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png deleted file mode 100644 index 646cc3e3235..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png deleted file mode 100644 index 5f4e1fa73f4..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_222222_256x240.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_222222_256x240.png deleted file mode 100644 index e723e17cb54..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_222222_256x240.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_228ef1_256x240.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_228ef1_256x240.png deleted file mode 100644 index ab0da63d7c5..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_228ef1_256x240.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ef8c08_256x240.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ef8c08_256x240.png deleted file mode 100644 index 14500792424..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ef8c08_256x240.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ffd27a_256x240.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ffd27a_256x240.png deleted file mode 100644 index d92e3bd6df5..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ffd27a_256x240.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ffffff_256x240.png b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ffffff_256x240.png deleted file mode 100644 index fe41d2d0fdd..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/images/ui-icons_ffffff_256x240.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/jquery-ui.min.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/jquery-ui.min.css deleted file mode 100644 index 80a19f6d259..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/jquery-ui.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! jQuery UI - v1.12.0 - 2016-07-26 -* http://jqueryui.com -* Includes: draggable.css, core.css, resizable.css, sortable.css, button.css, controlgroup.css, checkboxradio.css, dialog.css, theme.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=ui-lightness&cornerRadiusShadow=5px&offsetLeftShadow=-5px&offsetTopShadow=-5px&thicknessShadow=5px&opacityShadow=20&bgImgOpacityShadow=10&bgTextureShadow=flat&bgColorShadow=000000&opacityOverlay=50&bgImgOpacityOverlay=20&bgTextureOverlay=diagonals_thick&bgColorOverlay=666666&iconColorError=ffd27a&fcError=ffffff&borderColorError=cd0a0a&bgImgOpacityError=18&bgTextureError=diagonals_thick&bgColorError=b81900&iconColorHighlight=228ef1&fcHighlight=363636&borderColorHighlight=fed22f&bgImgOpacityHighlight=75&bgTextureHighlight=highlight_soft&bgColorHighlight=ffe45c&iconColorActive=ef8c08&fcActive=eb8f00&borderColorActive=fbd850&bgImgOpacityActive=65&bgTextureActive=glass&bgColorActive=ffffff&iconColorHover=ef8c08&fcHover=c77405&borderColorHover=fbcb09&bgImgOpacityHover=100&bgTextureHover=glass&bgColorHover=fdf5ce&iconColorDefault=ef8c08&fcDefault=1c94c4&borderColorDefault=cccccc&bgImgOpacityDefault=100&bgTextureDefault=glass&bgColorDefault=f6f6f6&iconColorContent=222222&fcContent=333333&borderColorContent=dddddd&bgImgOpacityContent=100&bgTextureContent=highlight_soft&bgColorContent=eeeeee&iconColorHeader=ffffff&fcHeader=ffffff&borderColorHeader=e78f08&bgImgOpacityHeader=35&bgTextureHeader=gloss_wave&bgColorHeader=f6a828&cornerRadius=4px&fsDefault=1.1em&fwDefault=bold&ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif -* Copyright jQuery Foundation and other contributors; Licensed MIT */ - -.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup > .ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup > .ui-controlgroup-item:focus,.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical > .ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #ccc}.ui-widget-content{border:1px solid #ddd;background:#eee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #ccc;background:#f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #fbcb09;background:#fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#c77405;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #fbd850;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-icon-background,.ui-state-active .ui-icon-background{border:#fbd850;background-color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x;color:#363636}.ui-state-checked{border:1px solid #fed22f;background:#ffe45c}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon,.ui-state-default .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_228ef1_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_ffd27a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{-webkit-box-shadow:-5px -5px 5px #000;box-shadow:-5px -5px 5px #000} \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/jquery-ui.theme.min.css b/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/jquery-ui.theme.min.css deleted file mode 100644 index 09ace29b29e..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/css/ui-lightness/jquery-ui.theme.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! jQuery UI - v1.12.0 - 2016-07-26 -* http://jqueryui.com -* Copyright jQuery Foundation and other contributors; Licensed MIT */ - -.ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #ccc}.ui-widget-content{border:1px solid #ddd;background:#eee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #ccc;background:#f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #fbcb09;background:#fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#c77405;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #fbd850;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-icon-background,.ui-state-active .ui-icon-background{border:#fbd850;background-color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x;color:#363636}.ui-state-checked{border:1px solid #fed22f;background:#ffe45c}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon,.ui-state-default .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_228ef1_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_ffd27a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{-webkit-box-shadow:-5px -5px 5px #000;box-shadow:-5px -5px 5px #000} \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/favicon.ico b/web-bundle/src/main/resources/com/graphhopper/maps/favicon.ico deleted file mode 100644 index a9c5dc7ee1e..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/favicon.ico and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/alt_route.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/alt_route.png deleted file mode 100644 index 3f4aaf6fb9e..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/alt_route.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/bike.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/bike.png deleted file mode 100644 index 3d7bb4bb100..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/bike.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/bike2.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/bike2.png deleted file mode 100644 index 54a802bb124..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/bike2.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/bus.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/bus.png deleted file mode 100644 index 07de3a723ff..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/bus.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/bus.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/bus.svg deleted file mode 100644 index 39b9496fa73..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/bus.svg +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/car.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/car.png deleted file mode 100644 index 1a0b843b157..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/car.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/car4wd.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/car4wd.png deleted file mode 100644 index 0219a457359..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/car4wd.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/car4wd.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/car4wd.svg deleted file mode 100644 index 0ea884489cd..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/car4wd.svg +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/continue.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/continue.png deleted file mode 100644 index e74e2042d66..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/continue.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/delete.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/delete.png deleted file mode 100644 index 4b7ad6f739e..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/delete.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/direction-icons.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/direction-icons.svg deleted file mode 100644 index d97df165cae..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/direction-icons.svg +++ /dev/null @@ -1,499 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/foot.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/foot.png deleted file mode 100644 index 45ff1e27938..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/foot.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/foot.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/foot.svg deleted file mode 100644 index 57c954ebcbb..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/foot.svg +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/full-reverse.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/full-reverse.png deleted file mode 100644 index fbc3ee41fde..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/full-reverse.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/full-reverse.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/full-reverse.svg deleted file mode 100644 index 70b115ce2d9..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/full-reverse.svg +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - image/svg+xml - - rail-15.svg - - - - - - - - rail-15.svg - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/full.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/full.png deleted file mode 100644 index b243dd72b71..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/full.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/full.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/full.svg deleted file mode 100644 index 7b0b5b4d026..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/full.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - image/svg+xml - - rail-15.svg - - - - - - - - rail-15.svg - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/gpx.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/gpx.png deleted file mode 100644 index 1ecb22ccada..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/gpx.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/header.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/header.png deleted file mode 100755 index 6d74c0fe66c..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/header.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/hike.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/hike.png deleted file mode 100644 index 2a31cb8da9c..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/hike.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/hike.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/hike.svg deleted file mode 100644 index 457ab13b9d0..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/hike.svg +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/icon.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/icon.png deleted file mode 100644 index 21011419307..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/icon.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/indicator-bar.gif b/web-bundle/src/main/resources/com/graphhopper/maps/img/indicator-bar.gif deleted file mode 100644 index 6bdc3b5e698..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/indicator-bar.gif and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/indicator.gif b/web-bundle/src/main/resources/com/graphhopper/maps/img/indicator.gif deleted file mode 100644 index e192ca895cd..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/indicator.gif and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/keep_left.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/keep_left.png deleted file mode 100644 index b098f1f279c..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/keep_left.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/keep_right.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/keep_right.png deleted file mode 100644 index 3809d8518f8..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/keep_right.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/left.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/left.png deleted file mode 100644 index 286b17aedd3..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/left.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/link.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/link.png deleted file mode 100644 index 25eacb7c252..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/link.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/loading.gif b/web-bundle/src/main/resources/com/graphhopper/maps/img/loading.gif deleted file mode 100644 index ed65b705afe..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/loading.gif and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-from.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-from.png deleted file mode 100644 index e4bc611f87b..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-from.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-blue.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-blue.png deleted file mode 100644 index d0a81621aa9..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-blue.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-green.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-green.png deleted file mode 100644 index 4dd6e355068..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-green.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-red.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-red.png deleted file mode 100644 index 364ac35ad11..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-icon-red.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-blue.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-blue.png deleted file mode 100644 index 73b0845b400..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-blue.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-green.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-green.png deleted file mode 100644 index d4b959ebe1a..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-green.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-red.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-red.png deleted file mode 100644 index ea5f6ef1628..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-small-red.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-to.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-to.png deleted file mode 100644 index e8a602da7b1..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker-to.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker_hole.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/marker_hole.png deleted file mode 100644 index 17481f2ebbc..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/marker_hole.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/motorcycle.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/motorcycle.png deleted file mode 100644 index 552a98a5871..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/motorcycle.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/motorcycle.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/motorcycle.svg deleted file mode 100644 index cdfe16e184f..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/motorcycle.svg +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/mtb-bicycle.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/mtb-bicycle.svg deleted file mode 100644 index 155e16298ac..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/mtb-bicycle.svg +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/mtb.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/mtb.png deleted file mode 100644 index 2a5bc0ab4d1..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/mtb.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/point_add.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/point_add.png deleted file mode 100644 index a7424380d6e..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/point_add.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/point_delete.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/point_delete.png deleted file mode 100644 index d1898cd4eff..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/point_delete.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/pt.png deleted file mode 100644 index 8bb8743d601..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/pt.svg deleted file mode 100644 index 367e08660bc..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - image/svg+xml - - - - - - - rail-15.svg - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_end_trip.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_end_trip.png deleted file mode 100644 index f67dfdd9ef0..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_end_trip.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_start_trip.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_start_trip.png deleted file mode 100644 index 6c91d000e7a..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_start_trip.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_transfer_to.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_transfer_to.png deleted file mode 100644 index c629fc43152..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/pt_transfer_to.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/racingbike.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/racingbike.png deleted file mode 100644 index fa40d2ba69a..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/racingbike.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/racingbike.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/racingbike.svg deleted file mode 100644 index 354b426510f..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/racingbike.svg +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/right.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/right.png deleted file mode 100644 index 40203ca7496..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/right.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/roundabout.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/roundabout.png deleted file mode 100644 index 1a02efd5b91..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/roundabout.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/scooter.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/scooter.png deleted file mode 100644 index 82ff2469aba..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/scooter.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/scooter.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/scooter.svg deleted file mode 100644 index 3b9af6685b9..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/scooter.svg +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/sharp_left.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/sharp_left.png deleted file mode 100644 index 6b8d2f449a9..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/sharp_left.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/sharp_right.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/sharp_right.png deleted file mode 100644 index 0c40d140bfc..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/sharp_right.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/slight_left.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/slight_left.png deleted file mode 100644 index fc4b3b7ecea..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/slight_left.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/slight_right.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/slight_right.png deleted file mode 100644 index 41409d79955..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/slight_right.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/small_truck.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/small_truck.png deleted file mode 100644 index 1f6bd47caeb..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/small_truck.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/small_truck.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/small_truck.svg deleted file mode 100644 index e001e957ec0..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/small_truck.svg +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/truck.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/truck.png deleted file mode 100644 index 052de16454e..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/truck.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/truck.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/truck.svg deleted file mode 100644 index 75f1e2f0481..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/truck.svg +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn.png deleted file mode 100644 index 453d682af0e..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn_left.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn_left.png deleted file mode 100644 index 453d682af0e..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn_left.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn_right.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn_right.png deleted file mode 100644 index 91b847ce7ba..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/u_turn_right.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/unknown.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/unknown.png deleted file mode 100644 index cad03562680..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/unknown.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/wheelchair.png b/web-bundle/src/main/resources/com/graphhopper/maps/img/wheelchair.png deleted file mode 100644 index c8f17d664c0..00000000000 Binary files a/web-bundle/src/main/resources/com/graphhopper/maps/img/wheelchair.png and /dev/null differ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/img/wheelchair.svg b/web-bundle/src/main/resources/com/graphhopper/maps/img/wheelchair.svg deleted file mode 100644 index 329d16d17fb..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/img/wheelchair.svg +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/index.html b/web-bundle/src/main/resources/com/graphhopper/maps/index.html deleted file mode 100644 index a7ee8b1158c..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/index.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - - - - Driving Directions - GraphHopper Maps - - - - - - - - - - - - - - - -
-
-
- - GraphHopper - -
- -
- - - -
-
-
- -
-
-
-
-
- -
-
-
custom
- -
gpx
-
- -
Powered by GraphHopper API
-
-
-
-
-
-
- - - - -
-
-
- -
- -
- -
-
-
-
-
- - - - - - - diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/isochrone/index.js b/web-bundle/src/main/resources/com/graphhopper/maps/isochrone/index.js index 307bbc5706c..d68ae56ebf2 100644 --- a/web-bundle/src/main/resources/com/graphhopper/maps/isochrone/index.js +++ b/web-bundle/src/main/resources/com/graphhopper/maps/isochrone/index.js @@ -105,7 +105,7 @@ function _drawMap(bbox) { // add GraphHopper vector tiles of road network. this is also called when we change the style map.addSource('gh-mvt', { type: 'vector', - tiles: ['http://' + window.location.host + '/mvt/{z}/{x}/{y}.mvt?details=road_class'] + tiles: ['http://' + window.location.host + '/mvt/{z}/{x}/{y}.mvt'] }); var boundsPolygon = [[bbox[0], bbox[1]], [bbox[2], bbox[1]], [bbox[2], bbox[3]], [bbox[0], bbox[3]], [bbox[0], bbox[1]]]; map.addLayer({ diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/autocomplete.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/autocomplete.js deleted file mode 100644 index 0ecbb3d5061..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/autocomplete.js +++ /dev/null @@ -1,174 +0,0 @@ -var formatTools = require('./tools/format.js'); -var GHInput = require('./graphhopper/GHInput.js'); -var GHRoute = require('./graphhopper/GHRoute.js'); -var mapLayer = require('./map.js'); - -var dataToHtml = function (data, query) { - var element = ""; - if (data.name) { - var name = data.name; - if (data.housenumber) - name = formatTools.insComma(name, data.housenumber); - - element += "
" + formatTools.formatValue(name, query) + "
"; - } - - var addStr = ""; - if (data.postcode) - addStr = data.postcode; - if (data.city) - addStr = formatTools.insComma(addStr, data.city); - if (data.country) - addStr = formatTools.insComma(addStr, data.country); - - if (addStr) - element += "
" + formatTools.formatValue(addStr, query) + "
"; - - if (data.osm_key === "highway") { - // ignore - } - if (data.osm_key === "place") { - element += "" + data.osm_value + ""; - } else - element += "" + data.osm_key + ""; - return element; -}; - -var dataToText = function (data) { - var text = ""; - if (data.name) { - text = data.name; - } else if (data.street) { - text = data.street; - if (data.housenumber) - text = formatTools.insComma(text, data.housenumber); - } - - if (data.postcode) - text = formatTools.insComma(text, data.postcode); - - // make sure name won't be duplicated - if (data.city && text.indexOf(data.city) < 0) - text = formatTools.insComma(text, data.city); - - if (data.country && text.indexOf(data.country) < 0) - text = formatTools.insComma(text, data.country); - return text; -}; - -var AutoComplete = function (host, key) { - this.host = host; - this.key = key; - this.dataType = "json"; - this.api_params = {"locale": "en"}; -}; - -AutoComplete.prototype.createPath = function (url) { - for (var key in this.api_params) { - var val = this.api_params[key]; - if (GHRoute.isArray(val)) { - for (var keyIndex in val) { - url += "&" + encodeURIComponent(key) + "=" + encodeURIComponent(val[keyIndex]); - } - } else { - url += "&" + encodeURIComponent(key) + "=" + encodeURIComponent(val); - } - } - return url; -}; - -AutoComplete.prototype.createGeocodeURL = function (ghRequest, prevIndex) { - var path = this.createPath(this.host + "/geocode?limit=6&type=" + this.dataType + "&key=" + this.key); - if (prevIndex >= 0 && prevIndex < ghRequest.route.size()) { - var point = ghRequest.route.getIndex(prevIndex); - if (point.isResolved()) { - path += "&point=" + point.lat + "," + point.lng; - } - } - return path; -}; - -AutoComplete.prototype.getAutoCompleteDiv = function (index) { - return $('#locationpoints > div.pointDiv').eq(index).find(".pointInput"); -}; - -AutoComplete.prototype.hide = function () { - $(':input[id$="_Input"]').autocomplete().hide(); -}; - -AutoComplete.prototype.showListForIndex = function (ghRequest, routeIfAllResolved, index) { - var myAutoDiv = this.getAutoCompleteDiv(index); - var url = this.createGeocodeURL(ghRequest, index - 1); - - var options = { - containerClass: "autocomplete", - timeout: 1000, - /* avoid too many requests when typing quickly */ - deferRequestBy: 200, - minChars: 2, - maxHeight: 510, - noCache: true, - /* this default could be problematic: preventBadQueries: true, */ - triggerSelectOnValidInput: false, - autoSelectFirst: false, - paramName: "q", - dataType: ghRequest.dataType, - onSearchStart: function (params) { - // query server only if not a parsable point (i.e. format lat,lon) - var val = new GHInput(params.q).lat; - return val === undefined; - }, - serviceUrl: function () { - return url; - }, - transformResult: function (response, originalQuery) { - response.suggestions = []; - if (response.hits) - for (var i = 0; i < response.hits.length; i++) { - var hit = response.hits[i]; - response.suggestions.push({value: dataToText(hit), data: hit}); - } - return response; - }, - onSearchError: function (element, q, jqXHR, textStatus, errorThrown) { - // too many errors if interrupted console.log(element + ", " + JSON.stringify(q) + ", textStatus " + textStatus + ", " + errorThrown); - }, - formatResult: function (suggestion, currInput) { - // avoid highlighting for now as this breaks the html sometimes - return dataToHtml(suggestion.data, currInput); - }, - onSelect: function (suggestion) { - options.onPreSelect(suggestion); - }, - onPreSelect: function (suggestion) { - var req = ghRequest.route.getIndex(index); - - myAutoDiv.autocomplete().disable(); - - var point = suggestion.data.point; - req.setCoord(point.lat, point.lng); - - req.input = suggestion.value; - if (!routeIfAllResolved(true)) - mapLayer.focus(req, 15, index); - - myAutoDiv.autocomplete().enable(); - } - }; - - myAutoDiv.autocomplete(options); -}; - -AutoComplete.prototype.createStub = function () { - complete = new AutoComplete(); - complete.showListForIndex = function () {}; - complete.hide = function () {}; - return complete; -}; - -AutoComplete.prototype.setLocale = function (locale) { - if (locale) - this.api_params.locale = locale; -}; - -module.exports = AutoComplete; diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/config/options.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/config/options.js deleted file mode 100644 index b44962d9045..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/config/options.js +++ /dev/null @@ -1,21 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// We know that you love 'free', we love it too :)! And so the entire GraphHopper routing engine is not -// only free but even Open Source! The GraphHopper Directions API is also free for development. -// Grab an API key and have fun with installing anything: https://graphhopper.com/#directions-api -// Misuse of API keys that you don't own is prohibited and you'll be blocked. -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Easily replace this options.js with an additional file that you provide as options_prod.js activate via: -// BROWSERIFYSWAP_ENV='production' npm run watch -// see also package.json and https://github.com/thlorenz/browserify-swap -exports.options = { - with_tiles: true, - environment: "development", - // use this if you serve the web UI from a different server like live-server and the GH server runs on the standard port - // routing: {host: 'http://localhost:8989', api_key: ''}, - routing: {host: '', api_key: ''}, - geocoding: {host: '', api_key: ''}, - thunderforest: {api_key: ''}, - omniscale: {api_key: ''}, - mapilion: {api_key: ''} -}; \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/config/tileLayers.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/config/tileLayers.js deleted file mode 100644 index 9ea9b155359..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/config/tileLayers.js +++ /dev/null @@ -1,177 +0,0 @@ -var ghenv = require("./options.js").options; -var tfAddition = ''; -if (ghenv.thunderforest.api_key) - tfAddition = '?apikey=' + ghenv.thunderforest.api_key; - -var mapilionAddition = ''; -if (ghenv.mapilion.api_key) - mapilionAddition = '?key=' + ghenv.mapilion.api_key; - -var osAPIKey = 'mapsgraph-bf48cc0b'; -if (ghenv.omniscale.api_key) - osAPIKey = ghenv.omniscale.api_key; - -var osmAttr = '© OpenStreetMap contributors'; - -// Automatically enable high-DPI tiles if provider and browser support it. -var retinaTiles = L.Browser.retina; - -var lyrk = L.tileLayer('https://tiles.lyrk.org/' + (retinaTiles ? 'lr' : 'ls') + '/{z}/{x}/{y}?apikey=6e8cfef737a140e2a58c8122aaa26077', { - attribution: osmAttr + ', Lyrk' -}); - -var omniscale = L.tileLayer('https://maps.omniscale.net/v2/' +osAPIKey + '/style.default/{z}/{x}/{y}.png' + (retinaTiles ? '?hq=true' : ''), { - layers: 'osm', - attribution: osmAttr + ', © Omniscale' -}); - -// Not an option as too fast over limit. -// var mapbox= L.tileLayer('https://{s}.tiles.mapbox.com/v4/peterk.map-vkt0kusv/{z}/{x}/{y}' + (retinaTiles ? '@2x' : '') + '.png?access_token=pk.eyJ1IjoicGV0ZXJrIiwiYSI6IkdFc2FJd2MifQ.YUd7dS_gOpT3xrQnB8_K-w', { -// attribution: osmAttr + ', © MapBox' -// }); - -var sorbianLang = L.tileLayer('http://a.tile.openstreetmap.de/tiles/osmhrb/{z}/{x}/{y}.png', { - attribution: osmAttr + ', © Alberding GmbH, CC-BY-SA' -}); - -var thunderTransport = L.tileLayer('https://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png' + tfAddition, { - attribution: osmAttr + ', Thunderforest Transport' -}); - -var thunderCycle = L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png' + tfAddition, { - attribution: osmAttr + ', Thunderforest Cycle' -}); - -var thunderOutdoors = L.tileLayer('https://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png' + tfAddition, { - attribution: osmAttr + ', Thunderforest Outdoors' -}); - -var thunderNeighbourhood = L.tileLayer('https://{s}.tile.thunderforest.com/neighbourhood/{z}/{x}/{y}.png' + tfAddition, { - attribution: osmAttr + ', Thunderforest Neighbourhood' -}); - -var kurvigerLiberty = L.tileLayer('https://{s}-tiles.mapilion.com/raster/styles/kurviger-liberty/{z}/{x}/{y}{r}.png'+mapilionAddition, { - subdomains: ['a', 'b', 'c', 'd', 'e'], - attribution: osmAttr + ',© Kurviger © Mapilion © OpenMapTiles' -}); - -var wrk = L.tileLayer('http://{s}.wanderreitkarte.de/topo/{z}/{x}/{y}.png', { - attribution: osmAttr + ', WanderReitKarte', - subdomains: ['topo4', 'topo', 'topo2', 'topo3'] -}); - -var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - attribution: osmAttr -}); - -var osmde = L.tileLayer('http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', { - attribution: osmAttr -}); - -var mapLink = 'Esri'; -var wholink = 'i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'; -var esriAerial = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { - attribution: '© ' + mapLink + ', ' + wholink, - maxZoom: 18 -}); - -var availableTileLayers = { - "Omniscale": omniscale, - "OpenStreetMap": osm, - "Esri Aerial": esriAerial, - "TF Transport": thunderTransport, - "TF Cycle": thunderCycle, - "TF Outdoors": thunderOutdoors, - "TF Neighbourhood": thunderNeighbourhood, - "Kurviger Liberty": kurvigerLiberty, - "Lyrk": lyrk, - "WanderReitKarte": wrk, - "Sorbian Language": sorbianLang, - "OpenStreetMap.de": osmde -}; - -var overlays; -module.exports.enableVectorTiles = function () { - var omniscaleGray = L.tileLayer('https://maps.omniscale.net/v2/' +osAPIKey + '/style.grayscale/layers.world,buildings,landusages,labels/{z}/{x}/{y}.png?' + (retinaTiles ? '&hq=true' : ''), { - layers: 'osm', - attribution: osmAttr + ', © Omniscale' - }); - availableTileLayers["Omniscale Dev"] = omniscaleGray; - - require('leaflet.vectorgrid'); - var vtLayer = L.vectorGrid.protobuf("/mvt/{z}/{x}/{y}.mvt?details=max_speed&details=road_class&details=road_environment", { - rendererFactory: L.canvas.tile, - maxZoom: 20, - minZoom: 10, - interactive: true, - vectorTileLayerStyles: { - 'roads': function(properties, zoom) { - // weight == line width - var color, opacity = 1, weight = 1, rc = properties.road_class; - // if(properties.speed < 30) console.log(properties) - if(rc == "motorway") { - color = '#dd504b'; // red - weight = 3; - } else if(rc == "primary" || rc == "trunk") { - color = '#e2a012'; // orange - weight = 2; - } else if(rc == "secondary") { - weight = 2; - color = '#f7c913'; // yellow - } else { - color = "#aaa5a7"; // grey - } - if(zoom > 16) - weight += 3; - else if(zoom > 15) - weight += 2; - else if(zoom > 13) - weight += 1; - - return { - weight: weight, - color: color, - opacity: opacity - } - }, - }, - }) - .on('click', function(e) { - }) - .on('mouseover', function(e) { - console.log(e.layer.properties); - // remove route info - $("#info").children("div").remove(); - // remove last vector tile info - $("#info").children("ul").remove(); - - var list = ""; - $.each(e.layer.properties, function (key, value) { - list += "
  • " + key + ": " + value + "
  • "; - }); - $("#info").append("
      "+list+"
    "); - $("#info").show(); - }).on('mouseout', function (e) { -// $("#info").html(""); - }); - overlays = { "Local MVT": vtLayer }; -} - -module.exports.activeLayerName = "Omniscale"; -module.exports.defaultLayer = omniscale; - -module.exports.getAvailableTileLayers = function () { - return availableTileLayers; -}; - -module.exports.getOverlays = function () { - return overlays; -}; - -module.exports.selectLayer = function (layerName) { - var defaultLayer = availableTileLayers[layerName]; - if (!defaultLayer) - defaultLayer = module.exports.defaultLayer; - - return defaultLayer; -}; diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/gpxexport.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/gpxexport.js deleted file mode 100644 index 40ddcc00826..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/gpxexport.js +++ /dev/null @@ -1,88 +0,0 @@ - -var ensureOneCheckboxSelected = function () { - //Make sure that at least one of the data types remains checked for the GPX export: - $("#gpx_route").change(function () { - if (!$(this).is(':checked')) - { - if (!$("#gpx_track").is(':checked')) - $("#gpx_waypoints").prop("disabled", true); - else { - if (!$("#gpx_waypoints").is(':checked')) - $("#gpx_track").prop("disabled", true); - } - } else - { - $("#gpx_track").prop("disabled", false); - $("#gpx_waypoints").prop("disabled", false); - } - }); - $("#gpx_track").change(function () { - if (!$(this).is(':checked')) - { - if (!$("#gpx_route").is(':checked')) - $("#gpx_waypoints").prop("disabled", true); - else { - if (!$("#gpx_waypoints").is(':checked')) - $("#gpx_route").prop("disabled", true); - } - } else - { - $("#gpx_route").prop("disabled", false); - $("#gpx_waypoints").prop("disabled", false); - } - }); - $("#gpx_waypoints").change(function () { - if (!$(this).is(':checked')) - { - if (!$("#gpx_route").is(':checked')) - $("#gpx_track").prop("disabled", true); - else { - if (!$("#gpx_track").is(':checked')) - $("#gpx_route").prop("disabled", true); - } - } else - { - $("#gpx_route").prop("disabled", false); - $("#gpx_track").prop("disabled", false); - } - }); -}; - -module.exports.addGpxExport = function (ghRequest) { - var dialog; - - function exportGPX(withRoute, withTrack, withWayPoint) { - if (ghRequest.route.isResolved()) - window.open(ghRequest.createGPXURL(withRoute, withTrack, withWayPoint)); - return false; - } - - function exportFlaggedGPX() { - exportGPX($("#gpx_route").is(':checked'), $("#gpx_track").is(':checked'), $("#gpx_waypoints").is(':checked')); - dialog.dialog("close"); - return false; - } - - $(function () { - dialog = $("#gpx_dialog").dialog({ - width: 420, - height: 260, - autoOpen: false, - resizable: false, - draggable: false, - buttons: { - "Export GPX": exportFlaggedGPX, - Cancel: function () { - $(this).dialog("close"); - } - } - }); - ensureOneCheckboxSelected(); - }); - - $('#gpxExportButton a').click(function (e) { - // no page reload - e.preventDefault(); - $("#gpx_dialog").dialog('open'); - }); -}; diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHInput.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHInput.js deleted file mode 100644 index 0ed1a2e71a9..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHInput.js +++ /dev/null @@ -1,67 +0,0 @@ -function round(val, precision) { - if (precision === undefined) - precision = 1e6; - return Math.round(val * precision) / precision; -} - -var GHInput = function (input) { - this.set(input); -}; - -GHInput.isObject = function (value) { - var stringValue = Object.prototype.toString.call(value); - return (stringValue.toLowerCase() === "[object object]"); -}; - -GHInput.isString = function (value) { - var stringValue = Object.prototype.toString.call(value); - return (stringValue.toLowerCase() === "[object string]"); -}; - -GHInput.prototype.isResolved = function () { - return !isNaN(this.lat) && !isNaN(this.lng); -}; - -GHInput.prototype.setCoord = function (lat, lng) { - this.lat = round(lat); - this.lng = round(lng); - this.input = this.toString(); -}; - -GHInput.prototype.setUnresolved = function () { - this.lat = undefined; - this.lng = undefined; -}; - -GHInput.prototype.set = function (strOrObject) { - // either text or coordinates or object - this.input = strOrObject; - // reset to unresolved - - - if (GHInput.isObject(strOrObject)) { - this.setCoord(strOrObject.lat, strOrObject.lng); - } else if (GHInput.isString(strOrObject)) { - var index = strOrObject.indexOf(","); - if (index >= 0) { - this.lat = round(parseFloat(strOrObject.substr(0, index))); - this.lng = round(parseFloat(strOrObject.substr(index + 1))); - - if (this.isResolved()) { - this.input = this.toString(); - } else { - this.setUnresolved(); - } - } else { - this.setUnresolved(); - } - } -}; - -GHInput.prototype.toString = function () { - if (this.lat !== undefined && this.lng !== undefined) - return this.lat + "," + this.lng; - return undefined; -}; - -module.exports = GHInput; diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHRequest.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHRequest.js deleted file mode 100644 index 1c7fc1e8cca..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHRequest.js +++ /dev/null @@ -1,351 +0,0 @@ -var GHRoute = require('./GHRoute.js'); -var GHInput = require('./GHInput.js'); -var graphhopperTools = require('./tools.js'); - -// compatibility script taken from http://stackoverflow.com/a/11054570/194609 -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== 'function') { - // closest thing possible to the ECMAScript 5 - // internal IsCallable function - throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () { - }, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; -} - -var GHRequest = function (host, api_key) { - this.host = host; - this.route = new GHRoute(new GHInput(), new GHInput()); - this.from = this.route.first(); - this.to = this.route.last(); - this.profiles = [] - - this.do_zoom = true; - this.useMiles = false; - this.dataType = "json"; - this.api_params = {"locale": "en", "key": api_key, "pt": {}}; - - // register events - this.route.addListener('route.add', function (evt) { - this.to = this.route.last(); - }.bind(this)); - this.route.addListener('route.remove', function (evt) { - this.from = this.route.first(); - this.to = this.route.last(); - }.bind(this)); - this.route.addListener('route.move', function (evt) { - this.from = this.route.first(); - this.to = this.route.last(); - }.bind(this)); - - this.route.addListener('route.reverse', function (evt) { - this.from = this.route.first(); - this.to = this.route.last(); - }.bind(this)); -}; - -GHRequest.prototype.init = function (params) { - for (var key in params) { - if (key === "point" || key === "mathRandom" || key === "do_zoom" || key === "layer" || key === "use_miles" || key === "selected_detail") - continue; - - var val = params[key]; - if (val === "false") - val = false; - else if (val === "true") - val = true; - - this.api_params[key] = val; - } - - if ('do_zoom' in params) - this.do_zoom = params.do_zoom; - - if ('use_miles' in params) - this.useMiles = params.use_miles; - - if ('selected_detail' in params) - this.selectedDetail = params.selected_detail; - - if (!this.api_params.profile) - this.api_params.profile = this.profiles[0].profile_name - - if (params.q) { - var qStr = params.q; - if (!params.point) - params.point = []; - var indexFrom = qStr.indexOf("from:"); - var indexTo = qStr.indexOf("to:"); - if (indexFrom >= 0 && indexTo >= 0) { - // google-alike query schema - if (indexFrom < indexTo) { - params.point.push(qStr.substring(indexFrom + 5, indexTo).trim()); - params.point.push(qStr.substring(indexTo + 3).trim()); - } else { - params.point.push(qStr.substring(indexTo + 3, indexFrom).trim()); - params.point.push(qStr.substring(indexFrom + 5).trim()); - } - } else { - var points = qStr.split("p:"); - for (var i = 0; i < points.length; i++) { - var str = points[i].trim(); - if (str.length === 0) - continue; - - params.point.push(str); - } - } - } -}; - -GHRequest.prototype.setEarliestDepartureTime = function (localdatetime) { - this.api_params.pt.earliest_departure_time = localdatetime; -}; - -GHRequest.prototype.getEarliestDepartureTime = function () { - if (this.api_params.pt.earliest_departure_time) - return this.api_params.pt.earliest_departure_time; - return undefined; -}; - -GHRequest.prototype.setProfile = function (profileName) { - this.api_params.profile = profileName; -}; - -GHRequest.prototype.getProfile = function () { - return this.api_params.profile; -}; - -GHRequest.prototype.setElevation = function (elevation) { - this.api_params.elevation = elevation; -}; - -GHRequest.prototype.hasElevation = function () { - if (Array.isArray(this.api_params.elevation)) { - return this.api_params.elevation.every(function (e) { - return e === true || e === "true"; - }) - } else - return this.api_params.elevation === true; -}; - -GHRequest.prototype.getVehicle = function () { - var profileName = this.api_params.profile; - var profile = this.profiles.find(function(p) { return p.profile_name === profileName; }); - if (!profile) - return ""; - else - return profile.vehicle; -}; - -GHRequest.prototype.isPublicTransit = function () { - // legacy support: we might have set vehicle=pt instead of pt - return this.getProfile() === "pt" || this.getVehicle() === "pt";; -}; - -GHRequest.prototype.removeProfileParameterIfLegacyRequest = function() { - // we still allow using legacy parameters to support older urls pasted from somewhere, but when they are used - // we may not add the profile parameter to the url - if ( - this.api_params["vehicle"] || - this.api_params["weighting"] || - this.api_params["turn_costs"] || - this.api_params["edge_based"] - ) { - delete this.api_params.profile; - } -} - -GHRequest.prototype.removeLegacyParameters = function() { - delete this.api_params["vehicle"]; - delete this.api_params["weighting"]; - delete this.api_params["turn_costs"]; - delete this.api_params["edge_based"]; -} - -GHRequest.prototype.createGeocodeURL = function (host, prevIndex) { - var tmpHost = this.host; - if (host) - tmpHost = host; - - var path = this.createPath(tmpHost + "/geocode?limit=6&type=" + this.dataType); - if (prevIndex >= 0 && prevIndex < this.route.size()) { - var point = this.route.getIndex(prevIndex); - if (point.isResolved()) { - path += "&point=" + point.lat + "," + point.lng; - } - } - return path; -}; - -GHRequest.prototype.createURL = function () { - return this.createPath(this.host + "/route?" + this.createPointParams(false) + "&type=" + this.dataType); -}; - -GHRequest.prototype.createGPXURL = function (withRoute, withTrack, withWayPoints) { - return this.createPath(this.host + "/route?" + this.createPointParams(false) + "&type=gpx&gpx.route=" + withRoute + "&gpx.track=" + withTrack + "&gpx.waypoints=" + withWayPoints); -}; - -GHRequest.prototype.createHistoryURL = function () { - var skip = {"key": true, "custom_model": true}; - return this.createPath("?" + this.createPointParams(true), skip) + "&use_miles=" + !!this.useMiles + (this.selectedDetail ? "&selected_detail=" + this.selectedDetail : ""); -}; - -GHRequest.prototype.createPointParams = function (useRawInput) { - var str = "", point, i, l; - - for (i = 0, l = this.route.size(); i < l; i++) { - point = this.route.getIndex(i); - if (i > 0) - str += "&"; - if (typeof point.input == 'undefined') - str += "point="; - else if (useRawInput) - str += "point=" + encodeURIComponent(point.input); - else - str += "point=" + encodeURIComponent(point.toString()); - } - return (str); -}; - -GHRequest.prototype.createPath = function (url, skipParameters) { - for (var key in this.api_params) { - if(skipParameters && skipParameters[key]) - continue; - - var val = this.api_params[key]; - url += this.flatParameter(key, val); - } - return url; -}; - -GHRequest.prototype.flatParameter = function (key, val) { - if(val == undefined) - return ""; - - if (GHRoute.isObject(val)) { - var url = ""; - var arr = Object.keys(val); - for (var keyIndex in arr) { - var objKey = arr[keyIndex]; - url += this.flatParameter(key + "." + objKey, val[objKey]); - } - return url; - - } else if (GHRoute.isArray(val)) { - var arr = val; - var url = ""; - for (var keyIndex in arr) { - url += this.flatParameter(key, arr[keyIndex]); - } - return url; - } - - return "&" + encodeURIComponent(key) + "=" + encodeURIComponent(val); -}; - -GHRequest.prototype.doRequest = function (url, callback) { - var that = this; - $.ajax({ - timeout: 30000, - url: url, - beforeSend: function(request) { - request.setRequestHeader("GH-Client", "web-ui 3.0") - }, - success: function (json) { - if (json.paths) { - for (var i = 0; i < json.paths.length; i++) { - var path = json.paths[i]; - // convert encoded polyline to geo json - if (path.points_encoded) { - var tmpArray = graphhopperTools.decodePath(path.points, that.hasElevation()); - path.points = { - "type": "LineString", - "coordinates": tmpArray - }; - - var tmpSnappedArray = graphhopperTools.decodePath(path.snapped_waypoints, that.hasElevation()); - path.snapped_waypoints = { - "type": "MultiPoint", - "coordinates": tmpSnappedArray - }; - } - } - } - callback(json); - }, - error: function (err) { - var msg = "API did not respond! "; - var json; - - if (err && err.responseText && err.responseText.indexOf('{') >= 0) { - json = JSON.parse(err.responseText); - } else if (err && err.statusText && err.statusText !== "OK") { - msg += err.statusText; - var details = "Error for " + url; - json = { - message: msg, - hints: [{"message": msg, "details": details}] - }; - } - console.log(msg + " " + JSON.stringify(err)); - - callback(json); - }, - type: "GET", - dataType: this.dataType, - crossDomain: true - }); -}; - -GHRequest.prototype.getInfo = function () { - var url = this.host + "/info?type=" + this.dataType + "&key=" + this.getKey(); - // console.log(url); - return $.ajax({ - url: url, - timeout: 3000, - type: "GET", - dataType: this.dataType, - crossDomain: true - }); -}; - -GHRequest.prototype.setLocale = function (locale) { - if (locale) - this.api_params.locale = locale; -}; - -GHRequest.prototype.getKey = function() { - return this.api_params.key; -}; - -GHRequest.prototype.fetchTranslationMap = function (urlLocaleParam) { - if (!urlLocaleParam) - // let servlet figure out the locale from the Accept-Language header - urlLocaleParam = ""; - var url = this.host + "/i18n/" + urlLocaleParam + "?type=" + this.dataType + "&key=" + this.getKey(); - // console.log(url); - return $.ajax({ - url: url, - timeout: 3000, - type: "GET", - dataType: this.dataType, - crossDomain: true - }); -}; - -module.exports = GHRequest; diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHRoute.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHRoute.js deleted file mode 100644 index 1bff018c56e..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/GHRoute.js +++ /dev/null @@ -1,223 +0,0 @@ -var GHInput = require('./GHInput.js'); - -var GHroute = function () { - var route = Object.create(Array.prototype); - route = (Array.apply(route, arguments) || route); - GHroute.injectClassMethods(route); - route._listeners = {}; - return (route); -}; - -GHroute.injectClassMethods = function (route) { - for (var method in GHroute.prototype) { - if (GHroute.prototype.hasOwnProperty(method)) { - route[method] = GHroute.prototype[method]; - } - } - return (route); -}; - -GHroute.fromArray = function (array) { - var route = GHroute.apply(null, array); - return (route); -}; - -GHroute.isArray = function (value) { - var stringValue = Object.prototype.toString.call(value); - return (stringValue.toLowerCase() === "[object array]"); -}; - -GHroute.isObject = function (value) { - var stringValue = Object.prototype.toString.call(value); - return (stringValue.toLowerCase() === "[object object]"); -}; - -GHroute.prototype = { - first: function () { - return this.getIndex(0); - }, - last: function () { - return this.getIndex((this.length - 1)); - }, - getIndex: function (index) { - index = (isNaN(index)) ? 0 : index; - if (this[index] instanceof GHInput) { - return this[index]; - } else - return false; - }, - getIndexByCoord: function (value) { - var point, - index = false, - coord = new GHInput(value), - i, - l; - - for (i = 0, l = this.length; i < l; i++) { - point = this[i]; - if (point.toString() === coord.toString()) { - index = i; - break; - } - } - return index; - }, - getIndexFromCoord: function (value) { - return this.getIndex(this.getIndexByCoord(value)); - }, - size: function () { - return this.length; - }, - add: function (value, to) { - if (GHroute.isArray(value)) { - for (var i = 0; i < value.length; i++) { - Array.prototype.push.call(this, (value[i] instanceof GHInput) ? value[i] : new GHInput(value[i])); - if (to !== undefined) { - this.move(-1, to, true); - to++; - } else - to = this.length - 1; - this.fire('route.add', { - point: this[to], - to: to - }); - } - return (this); - } else { - Array.prototype.push.call(this, (value instanceof GHInput) ? value : new GHInput(value)); - if (to !== undefined) - this.move(-1, to, true); - else - to = this.length - 1; - this.fire('route.add', { - point: this[to], - to: to - }); - } - return (this[to]); - }, - removeSingle: function (value) { - var index = false; - if (!(isNaN(value) || value >= this.length) && this[value] !== undefined) { - index = value; - } else { - if (value instanceof GHInput) { - value = value.toString(); - } - index = this.getIndexByCoord(value); - } - if (index !== false) { - this.remove(index); - } - return (this); - }, - remove: function (from, to) { - var tmpTo = to || 1; - Array.prototype.splice.call(this, from, tmpTo); - if (this.length === 1) - Array.prototype.push.call(this, new GHInput()); - this.fire('route.remove', { - from: from, - to: tmpTo - }); - return (this); - }, - addAll: function () { - for (var i = 0; i < arguments.length; i++) { - this.add(arguments[i]); - } - return (this); - }, - set: function (value, to, create) { - if (value instanceof GHInput) - this[to] = value; - else if (this[to] instanceof GHInput) { - this[to].set(value); - } else if (create) - return this.add(value, to); - else - return false; - this.fire('route.set', { - point: this[to], - to: to - }); - return (this[to]); - }, - move: function (old_index, new_index, supress_event) { - while (old_index < 0) { - old_index += this.length; - } - while (new_index < 0) { - new_index += this.length; - } - if (new_index >= this.length) { - var k = new_index - this.length; - while ((k--) + 1) { - Array.prototype.push.call(this, undefined); - } - } - Array.prototype.splice.call(this, new_index, 0, Array.prototype.splice.call(this, old_index, 1)[0]); - if (!supress_event) - this.fire('route.move', { - old_index: old_index, - new_index: new_index - }); - return (this); - }, - reverse: function () { - Array.prototype.reverse.call(this); - this.fire('route.reverse', {}); - return (this); - }, - isResolved: function () { - for (var i = 0, l = this.length; i < l; i++) { - var point = this[i]; - if (!point.isResolved()) { - return false; - } - } - return true; - }, - addListener: function (type, listener) { - if (typeof this._listeners[type] === "undefined") { - this._listeners[type] = []; - } - this._listeners[type].push(listener); - return this; - }, - fire: function (event, options) { - if (typeof event === "string") { - event = {type: event}; - } - if (typeof options === "object") { - for (var attrname in options) { - event[attrname] = options[attrname]; - } - } - if (!event.route) { - event.route = this; - } - if (!event.type) { //falsy - throw new Error("Event object missing 'type' property."); - } - if (this._listeners[event.type] instanceof Array) { - var listeners = this._listeners[event.type]; - for (var i = 0, len = listeners.length; i < len; i++) { - listeners[i].call(this, event); - } - } - }, - removeListener: function (type, listener) { - if (this._listeners[type] instanceof Array) { - var listeners = this._listeners[type]; - for (var i = 0, len = listeners.length; i < len; i++) { - if (listeners[i] === listener) { - listeners.splice(i, 1); - break; - } - } - } - } -}; - -module.exports = GHroute; diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/tools.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/tools.js deleted file mode 100644 index b37b16faeeb..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/graphhopper/tools.js +++ /dev/null @@ -1,53 +0,0 @@ -var decodePath = function (encoded, is3D) { - // var start = new Date().getTime(); - var len = encoded.length; - var index = 0; - var array = []; - var lat = 0; - var lng = 0; - var ele = 0; - - while (index < len) { - var b; - var shift = 0; - var result = 0; - do { - b = encoded.charCodeAt(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - var deltaLat = ((result & 1) ? ~(result >> 1) : (result >> 1)); - lat += deltaLat; - - shift = 0; - result = 0; - do { - b = encoded.charCodeAt(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - var deltaLon = ((result & 1) ? ~(result >> 1) : (result >> 1)); - lng += deltaLon; - - if (is3D) { - // elevation - shift = 0; - result = 0; - do - { - b = encoded.charCodeAt(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - var deltaEle = ((result & 1) ? ~(result >> 1) : (result >> 1)); - ele += deltaEle; - array.push([lng * 1e-5, lat * 1e-5, ele / 100]); - } else - array.push([lng * 1e-5, lat * 1e-5]); - } - // var end = new Date().getTime(); - // console.log("decoded " + len + " coordinates in " + ((end - start) / 1000) + "s"); - return array; -}; - -module.exports.decodePath = decodePath; diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/instructions.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/instructions.js deleted file mode 100644 index 839e16d632d..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/instructions.js +++ /dev/null @@ -1,136 +0,0 @@ -var translate = require('./translate.js'); -var messages = require('./messages.js'); - -var routeSegmentPopup = null; - -function addInstruction(mapLayer, main, instr, instrIndex, lngLat, useMiles, debug) { - var sign = instr.sign; - if (instrIndex === 0) - sign = "marker-icon-green"; - else - sign = messages.getSignName(sign); - var title = instr.text; - - var pathname = window.location.pathname; - var dirname = pathname.substring(0, pathname.lastIndexOf('/')); - - var instructionDiv = $(""); - if (sign !== "continue") { - var indiPic = ""; - instructionDiv.append("" + indiPic + ""); - } else { - instructionDiv.append(""); - } - - var tdVar = $(""); - tdVar.text(title); - - instructionDiv.append(tdVar); - var distance = instr.distance; - if (distance > 0) { - instructionDiv.append("" + translate.createDistanceString(distance, useMiles) + "
    " + translate.createTimeString(instr.time) + "
    "); - } - - if (lngLat) { - instructionDiv.click(function () { - if (routeSegmentPopup) - mapLayer.removeLayerFromMap(routeSegmentPopup); - - routeSegmentPopup = L.popup().setLatLng([lngLat[1], lngLat[0]]).setContent(title).openOn(mapLayer.getMap()); - - }); - - if (debug) { - // Debug Turn Instructions more easily - L.marker([lngLat[1], lngLat[0]], { - icon: L.icon({ - iconUrl: './img/marker-small-red.png', - // Made the instructions icon a bit bigger, as they are placed before the path details - iconSize: [16, 16] - }), - draggable: true, - autoPan: true - }).addTo(mapLayer.getRoutingLayer()).bindPopup(title); - } - } - main.append(instructionDiv); -} - -module.exports.create = function (mapLayer, path, urlForHistory, request) { - var instructionsElement = $(""); - var debug = request.api_params.debug; - - var partialInstr = path.instructions.length > 100; - var len = Math.min(path.instructions.length, 100); - for (var m = 0; m < len; m++) { - var instr = path.instructions[m]; - var lngLat = path.points.coordinates[instr.interval[0]]; - addInstruction(mapLayer, instructionsElement, instr, m, lngLat, request.useMiles, debug); - } - var infoDiv = $("
    "); - infoDiv.append(instructionsElement); - - if (partialInstr) { - var moreDiv = $(""); - moreDiv.click(function () { - moreDiv.remove(); - for (var m = len; m < path.instructions.length; m++) { - var instr = path.instructions[m]; - var lngLat = path.points.coordinates[instr.interval[0]]; - addInstruction(mapLayer, instructionsElement, instr, m, lngLat, request.useMiles); - } - }); - instructionsElement.append(moreDiv); - } - - var hiddenDiv = $("
    "); - hiddenDiv.hide(); - - var toggly = $(""); - toggly.click(function () { - hiddenDiv.toggle(); - }); - infoDiv.append(toggly); - var infoStr = "points: " + path.points.coordinates.length; - - hiddenDiv.append("" + infoStr + ""); - - var osmRouteLink = $("
    view on OSM"); - - var osmVehicle = request.getVehicle(); - if (osmVehicle === "bicycle") { - osmVehicle = "bike"; - } else if (osmVehicle.indexOf("truck") >= 0) { - osmVehicle = "car"; - } - - osmRouteLink.attr("href", "http://www.openstreetmap.org/directions?engine=graphhopper_" + osmVehicle + "&route=" + encodeURIComponent(request.from.lat + "," + request.from.lng + ";" + request.to.lat + "," + request.to.lng)); - hiddenDiv.append(osmRouteLink); - - var osrmLink = $("OSRM"); - osrmLink.attr("href", "http://map.project-osrm.org/?z=13&loc=" + request.from + "&loc=" + request.to); - hiddenDiv.append("
    Compare with: "); - hiddenDiv.append(osrmLink); - var googleLink = $("Google "); - var addToGoogle = ""; - var addToBing = ""; - if (request.getVehicle().toUpperCase() === "FOOT") { - addToGoogle = "&dirflg=w"; - addToBing = "&mode=W"; - } else if ((request.getVehicle().toUpperCase().indexOf("BIKE") >= 0) || - (request.getVehicle().toUpperCase() === "MTB")) { - addToGoogle = "&dirflg=b"; - } - - googleLink.attr("href", "https://maps.google.com/?saddr=" + request.from + "&daddr=" + request.to + addToGoogle); - hiddenDiv.append(googleLink); - var bingLink = $("Bing "); - bingLink.attr("href", "https://www.bing.com/maps/default.aspx?rtp=adr." + request.from + "~adr." + request.to + addToBing); - hiddenDiv.append(bingLink); - if (metaVersionInfo) - hiddenDiv.append(metaVersionInfo); - - infoDiv.append(hiddenDiv); - return infoDiv; -}; diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery-ui-custom-1.12.0.min.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery-ui-custom-1.12.0.min.js deleted file mode 100644 index 5db2c6d4e9b..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery-ui-custom-1.12.0.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/*! jQuery UI - v1.12.0 - 2016-07-26 -* http://jqueryui.com -* Custom: select dialog, sortable and jquery 1.7 support -* Copyright jQuery Foundation and other contributors; Licensed MIT */ - -(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){function e(t){for(var e=t.css("visibility");"inherit"===e;)t=t.parent(),e=t.css("visibility");return"hidden"!==e}t.ui=t.ui||{},t.ui.version="1.12.0";var i=0,s=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var i,n,o=s.call(arguments,1),a=0,r=o.length;r>a;a++)for(i in o[a])n=o[a][i],o[a].hasOwnProperty(i)&&void 0!==n&&(e[i]=t.isPlainObject(n)?t.isPlainObject(e[i])?t.widget.extend({},e[i],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,i){var n=i.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=s.call(arguments,1),l=this;return a?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new i(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
    ",options:{classes:{},disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o,a=Math.max,r=Math.abs,l=Math.round,h=/left|center|right/,c=/top|center|bottom/,u=/[\+\-]\d+(\.[\d]+)?%?/,d=/^\w+/,p=/%$/,f=t.fn.position;o=function(){var e=t("
    ").css("position","absolute").appendTo("body").offset({top:1.5,left:1.5}),i=1.5===e.offset().top;return e.remove(),o=function(){return i},i},t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
    "),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>o?"top":s>0?"bottom":"middle"};u>g&&g>r(e+i)&&(l.horizontal="center"),d>m&&m>r(s+o)&&(l.vertical="middle"),l.important=a(r(e),r(i))>a(r(s),r(o))?"horizontal":"vertical",n.using.call(this,t,l)}),c.offset(t.extend(I,{using:h}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,o=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-o-n;e.collisionWidth>o?l>0&&0>=h?(i=t.left+l+e.collisionWidth-o-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+o-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=a(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,o=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-o-n;e.collisionHeight>o?l>0&&0>=h?(i=t.top+l+e.collisionHeight-o-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+o-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=a(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,a=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-a-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-a-o,(0>i||r(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,a=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-a-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-a-o,(0>s||r(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>r(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.ui.focusable=function(i,s){var n,o,a,r,l,h=i.nodeName.toLowerCase();return"area"===h?(n=i.parentNode,o=n.name,i.href&&o&&"map"===n.nodeName.toLowerCase()?(a=t("img[usemap='#"+o+"']"),a.length>0&&a.is(":visible")):!1):(/^(input|select|textarea|button|object)$/.test(h)?(r=!i.disabled,r&&(l=t(i).closest("fieldset")[0],l&&(r=!l.disabled))):r="a"===h?i.href||s:s,r&&t(i).is(":visible")&&e(t(i)))},t.extend(t.expr[":"],{focusable:function(e){return t.ui.focusable(e,null!=t.attr(e,"tabindex"))}}),t.ui.focusable,t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},"1.7"===t.fn.jquery.substring(0,3)&&(t.each(["Width","Height"],function(e,i){function s(e,i,s,o){return t.each(n,function(){i-=parseFloat(t.css(e,"padding"+this))||0,s&&(i-=parseFloat(t.css(e,"border"+this+"Width"))||0),o&&(i-=parseFloat(t.css(e,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],o=i.toLowerCase(),a={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn["inner"+i]=function(e){return void 0===e?a["inner"+i].call(this):this.each(function(){t(this).css(o,s(this,e)+"px")})},t.fn["outer"+i]=function(e,n){return"number"!=typeof e?a["outer"+i].call(this,e):this.each(function(){t(this).css(o,s(this,e,!0,n)+"px")})}}),t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.ui.escapeSelector=function(){var t=/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;return function(e){return e.replace(t,"\\$1")}}(),t.fn.labels=function(){var e,i,s,n,o;return this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(n=this.eq(0).parents("label"),s=this.attr("id"),s&&(e=this.eq(0).parents().last(),o=e.add(e.length?e.siblings():this.siblings()),i="label[for='"+t.ui.escapeSelector(s)+"']",n=n.add(o.find(i).addBack(i))),this.pushStack(n))},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.extend(t.expr[":"],{tabbable:function(e){var i=t.attr(e,"tabindex"),s=null!=i;return(!s||i>=0)&&t.ui.focusable(e,s)}}),t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var n=!1;t(document).on("mouseup",function(){n=!1}),t.widget("ui.mouse",{version:"1.12.0",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!n){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,s=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return s&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),n=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,n=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var n,o=t.ui[e].prototype;for(n in s)o.plugins[n]=o.plugins[n]||[],o.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;o.length>n;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}},t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.ui.safeBlur=function(e){e&&"body"!==e.nodeName.toLowerCase()&&t(e).trigger("blur")},t.widget("ui.draggable",t.ui.mouse,{version:"1.12.0",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this._addClass("ui-draggable"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),"handle"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(e){var i=this.options;return this._blurActiveElement(e),this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=t(this);return t("
    ").css("position","absolute").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(e){var i=t.ui.safeActiveElement(this.document[0]),s=t(e.target);this._getHandle(e)&&s.closest(i).length||t.ui.safeBlur(i)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this._addClass(this.helper,"ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===t(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(e),this.originalPosition=this.position=this._generatePosition(e,!1),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(e,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp(new t.Event("mouseup",e)),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1},_mouseUp:function(e){return this._unblockFrames(),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),this.handleElement.is(e.target)&&this.element.trigger("focus"),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp(new t.Event("mouseup",{target:this.element[0]})):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this._addClass(this.handleElement,"ui-draggable-handle")},_removeHandleClassName:function(){this._removeClass(this.handleElement,"ui-draggable-handle")},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper),n=s?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css("left"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options,o=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,t(o).width()-this.helperProportions.width-this.margins.left,(t(o).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0)},_convertPositionTo:function(t,e){e||(e=this.position);var i="absolute"===t?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s,n,o,a=this.options,r=this._isRootNode(this.scrollParent[0]),l=t.pageX,h=t.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.lefti[2]&&(l=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(h=i[3]+this.offset.click.top)),a.grid&&(n=a.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/a.grid[1])*a.grid[1]:this.originalPageY,h=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-a.grid[1]:n+a.grid[1]:n,o=a.grid[0]?this.originalPageX+Math.round((l-this.originalPageX)/a.grid[0])*a.grid[0]:this.originalPageX,l=i?o-this.offset.click.left>=i[0]||o-this.offset.click.left>i[2]?o:o-this.offset.click.left>=i[0]?o-a.grid[0]:o+a.grid[0]:o),"y"===a.axis&&(l=this.originalPageX),"x"===a.axis&&(h=this.originalPageY)),{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:l-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)} -},_clear:function(){this._removeClass(this.helper,"ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s,this],!0),/^(drag|start|stop)/.test(e)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i,s){var n=t.extend({},i,{item:s.element});s.sortables=[],t(s.options.connectToSortable).each(function(){var i=t(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",e,n))})},stop:function(e,i,s){var n=t.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,t.each(s.sortables,function(){var t=this;t.isOver?(t.isOver=0,s.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css("position"),top:t.placeholder.css("top"),left:t.placeholder.css("left")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger("deactivate",e,n))})},drag:function(e,i,s){t.each(s.sortables,function(){var n=!1,o=this;o.positionAbs=s.positionAbs,o.helperProportions=s.helperProportions,o.offset.click=s.offset.click,o._intersectsWith(o.containerCache)&&(n=!0,t.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==o&&this._intersectsWith(this.containerCache)&&t.contains(o.element[0],this.element[0])&&(n=!1),n})),n?(o.isOver||(o.isOver=1,s._parent=i.helper.parent(),o.currentItem=i.helper.appendTo(o.element).data("ui-sortable-item",!0),o.options._helper=o.options.helper,o.options.helper=function(){return i.helper[0]},e.target=o.currentItem[0],o._mouseCapture(e,!0),o._mouseStart(e,!0,!0),o.offset.click.top=s.offset.click.top,o.offset.click.left=s.offset.click.left,o.offset.parent.left-=s.offset.parent.left-o.offset.parent.left,o.offset.parent.top-=s.offset.parent.top-o.offset.parent.top,s._trigger("toSortable",e),s.dropped=o.element,t.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,o.fromOutside=s),o.currentItem&&(o._mouseDrag(e),i.position=o.position)):o.isOver&&(o.isOver=0,o.cancelHelperRemoval=!0,o.options._revert=o.options.revert,o.options.revert=!1,o._trigger("out",e,o._uiHash(o)),o._mouseStop(e,!0),o.options.revert=o.options._revert,o.options.helper=o.options._helper,o.placeholder&&o.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(e),i.position=s._generatePosition(e,!0),s._trigger("fromSortable",e),s.dropped=!1,t.each(s.sortables,function(){this.refreshPositions()}))})}}),t.ui.plugin.add("draggable","cursor",{start:function(e,i,s){var n=t("body"),o=s.options;n.css("cursor")&&(o._cursor=n.css("cursor")),n.css("cursor",o.cursor)},stop:function(e,i,s){var n=s.options;n._cursor&&t("body").css("cursor",n._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css("opacity")&&(o._opacity=n.css("opacity")),n.css("opacity",o.opacity)},stop:function(e,i,s){var n=s.options;n._opacity&&t(i.helper).css("opacity",n._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(t,e,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(e,i,s){var n=s.options,o=!1,a=s.scrollParentNotHidden[0],r=s.document[0];a!==r&&"HTML"!==a.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+a.offsetHeight-e.pageY=0;d--)l=s.snapElements[d].left-s.margins.left,h=l+s.snapElements[d].width,c=s.snapElements[d].top-s.margins.top,u=c+s.snapElements[d].height,l-g>_||m>h+g||c-g>b||v>u+g||!t.contains(s.snapElements[d].item.ownerDocument,s.snapElements[d].item)?(s.snapElements[d].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=!1):("inner"!==f.snapMode&&(n=g>=Math.abs(c-b),o=g>=Math.abs(u-v),a=g>=Math.abs(l-_),r=g>=Math.abs(h-m),n&&(i.position.top=s._convertPositionTo("relative",{top:c-s.helperProportions.height,left:0}).top),o&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left)),p=n||o||a||r,"outer"!==f.snapMode&&(n=g>=Math.abs(c-v),o=g>=Math.abs(u-b),a=g>=Math.abs(l-m),r=g>=Math.abs(h-_),n&&(i.position.top=s._convertPositionTo("relative",{top:c,left:0}).top),o&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left)),!s.snapElements[d].snapping&&(n||o||a||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=n||o||a||r||p)}}),t.ui.plugin.add("draggable","stack",{start:function(e,i,s){var n,o=s.options,a=t.makeArray(t(o.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});a.length&&(n=parseInt(t(a[0]).css("zIndex"),10)||0,t(a).each(function(e){t(this).css("zIndex",n+e)}),this.css("zIndex",n+a.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css("zIndex")&&(o._zIndex=n.css("zIndex")),n.css("zIndex",o.zIndex)},stop:function(e,i,s){var n=s.options;n._zIndex&&t(i.helper).css("zIndex",n._zIndex)}}),t.ui.draggable,t.widget("ui.resizable",t.ui.mouse,{version:"1.12.0",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(e,i){if("hidden"===t(e).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i=this.options,s=this;this._addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!i.aspectRatio,aspectRatio:i.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:i.helper||i.ghost||i.animate?i.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t("
    ").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,e={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(e),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(e),this._proportionallyResize()),this._setupHandles(),i.autoHide&&t(this.element).on("mouseenter",function(){i.disabled||(s._removeClass("ui-resizable-autohide"),s._handles.show())}).on("mouseleave",function(){i.disabled||s.resizing||(s._addClass("ui-resizable-autohide"),s._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeData("resizable").removeData("ui-resizable").off(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;default:}},_setupHandles:function(){var e,i,s,n,o,a=this.options,r=this;if(this.handles=a.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=t(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),s=this.handles.split(","),this.handles={},i=0;s.length>i;i++)e=t.trim(s[i]),n="ui-resizable-"+e,o=t("
    "),this._addClass(o,"ui-resizable-handle "+n),o.css({zIndex:a.zIndex}),this.handles[e]=".ui-resizable-"+e,this.element.append(o);this._renderAxis=function(e){var i,s,n,o;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:r._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),o=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,o),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){r.resizing||(this.className&&(o=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),r.axis=o&&o[1]?o[1]:"se")}),a.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._handles.remove()},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,o=this.options,a=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),o.containment&&(i+=t(o.containment).scrollLeft()||0,s+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:a.width(),height:a.height()},this.originalSize=this._helper?{width:a.outerWidth(),height:a.outerHeight()}:{width:a.width(),height:a.height()},this.sizeDiff={width:a.outerWidth()-a.width(),height:a.outerHeight()-a.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===n?this.axis+"-resize":n),this._addClass("ui-resizable-resizing"),this._propagate("start",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,o=this.axis,a=e.pageX-n.left||0,r=e.pageY-n.top||0,l=this._change[o];return this._updatePrevProperties(),l?(i=l.apply(this,[e,a,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,o,a,r,l,h=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:c.sizeDiff.height,o=s?0:c.sizeDiff.width,a={width:c.helper.width()-o,height:c.helper.height()-n},r=parseFloat(c.element.css("left"))+(c.position.left-c.originalPosition.left)||null,l=parseFloat(c.element.css("top"))+(c.position.top-c.originalPosition.top)||null,h.animate||this.element.css(t.extend(a,{top:l,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!h.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,o,a=this.options;o={minWidth:this._isNumber(a.minWidth)?a.minWidth:0,maxWidth:this._isNumber(a.maxWidth)?a.maxWidth:1/0,minHeight:this._isNumber(a.minHeight)?a.minHeight:0,maxHeight:this._isNumber(a.maxHeight)?a.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,s=o.minWidth/this.aspectRatio,i=o.maxHeight*this.aspectRatio,n=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),s>o.minHeight&&(o.minHeight=s),o.maxWidth>i&&(o.maxWidth=i),o.maxHeight>n&&(o.maxHeight=n)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidtht.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,l=this.originalPosition.top+this.originalSize.height,h=/sw|nw|w/.test(i),c=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&h&&(t.left=r-e.minWidth),s&&h&&(t.left=r-e.maxWidth),a&&c&&(t.top=l-e.minHeight),n&&c&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];4>e;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("
    "),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),a=o&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,l={width:i.size.width-r,height:i.size.height-a},h=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,c=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(l,c&&h?{top:c,left:h}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var e,i,s,n,o,a,r,l=t(this).resizable("instance"),h=l.options,c=l.element,u=h.containment,d=u instanceof t?u.get(0):/parent/.test(u)?c.parent().get(0):u;d&&(l.containerElement=t(d),/document/.test(u)||u===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t(["Top","Right","Left","Bottom"]).each(function(t,s){i[t]=l._num(e.css("padding"+s))}),l.containerOffset=e.offset(),l.containerPosition=e.position(),l.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=l.containerOffset,n=l.containerSize.height,o=l.containerSize.width,a=l._hasScroll(d,"left")?d.scrollWidth:o,r=l._hasScroll(d)?d.scrollHeight:n,l.parentData={element:d,left:s.left,top:s.top,width:a,height:r}))},resize:function(e){var i,s,n,o,a=t(this).resizable("instance"),r=a.options,l=a.containerOffset,h=a.position,c=a._aspectRatio||e.shiftKey,u={top:0,left:0},d=a.containerElement,p=!0;d[0]!==document&&/static/.test(d.css("position"))&&(u=l),h.left<(a._helper?l.left:0)&&(a.size.width=a.size.width+(a._helper?a.position.left-l.left:a.position.left-u.left),c&&(a.size.height=a.size.width/a.aspectRatio,p=!1),a.position.left=r.helper?l.left:0),h.top<(a._helper?l.top:0)&&(a.size.height=a.size.height+(a._helper?a.position.top-l.top:a.position.top),c&&(a.size.width=a.size.height*a.aspectRatio,p=!1),a.position.top=a._helper?l.top:0),n=a.containerElement.get(0)===a.element.parent().get(0),o=/relative|absolute/.test(a.containerElement.css("position")),n&&o?(a.offset.left=a.parentData.left+a.position.left,a.offset.top=a.parentData.top+a.position.top):(a.offset.left=a.element.offset().left,a.offset.top=a.element.offset().top),i=Math.abs(a.sizeDiff.width+(a._helper?a.offset.left-u.left:a.offset.left-l.left)),s=Math.abs(a.sizeDiff.height+(a._helper?a.offset.top-u.top:a.offset.top-l.top)),i+a.size.width>=a.parentData.width&&(a.size.width=a.parentData.width-i,c&&(a.size.height=a.size.width/a.aspectRatio,p=!1)),s+a.size.height>=a.parentData.height&&(a.size.height=a.parentData.height-s,c&&(a.size.width=a.size.height*a.aspectRatio,p=!1)),p||(a.position.left=a.prevPosition.left,a.position.top=a.prevPosition.top,a.size.width=a.prevSize.width,a.size.height=a.prevSize.height)},stop:function(){var e=t(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.containerPosition,o=e.containerElement,a=t(e.helper),r=a.offset(),l=a.outerWidth()-e.sizeDiff.width,h=a.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:l,height:h}),e._helper&&!i.animate&&/static/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:l,height:h})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).resizable("instance"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseFloat(e.width()),height:parseFloat(e.height()),left:parseFloat(e.css("left")),top:parseFloat(e.css("top"))})})},resize:function(e,i){var s=t(this).resizable("instance"),n=s.options,o=s.originalSize,a=s.originalPosition,r={height:s.size.height-o.height||0,width:s.size.width-o.width||0,top:s.position.top-a.top||0,left:s.position.left-a.left||0};t(n.alsoResize).each(function(){var e=t(this),s=t(this).data("ui-resizable-alsoresize"),n={},o=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData("ui-resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).resizable("instance"),i=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}),e._addClass(e.ghost,"ui-resizable-ghost"),t.uiBackCompat!==!1&&"string"==typeof e.options.ghost&&e.ghost.addClass(this.options.ghost),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable("instance");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable("instance");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e,i=t(this).resizable("instance"),s=i.options,n=i.size,o=i.originalSize,a=i.originalPosition,r=i.axis,l="number"==typeof s.grid?[s.grid,s.grid]:s.grid,h=l[0]||1,c=l[1]||1,u=Math.round((n.width-o.width)/h)*h,d=Math.round((n.height-o.height)/c)*c,p=o.width+u,f=o.height+d,g=s.maxWidth&&p>s.maxWidth,m=s.maxHeight&&f>s.maxHeight,_=s.minWidth&&s.minWidth>p,v=s.minHeight&&s.minHeight>f;s.grid=l,_&&(p+=h),v&&(f+=c),g&&(p-=h),m&&(f-=c),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=a.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=a.left-u):((0>=f-c||0>=p-h)&&(e=i._getPaddingPlusBorderDimensions(this)),f-c>0?(i.size.height=f,i.position.top=a.top-d):(f=c-e.height,i.size.height=f,i.position.top=a.top+o.height-f),p-h>0?(i.size.width=p,i.position.left=a.left-u):(p=h-e.width,i.size.width=p,i.position.left=a.left+o.width-p))}}),t.ui.resizable,t.widget("ui.sortable",t.ui.mouse,{version:"1.12.0",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return t>=e&&e+i>t},_isFloating:function(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))},_create:function(){this.containerCache={},this._addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),"handle"===t&&this._setHandleClassName()},_setHandleClassName:function(){var e=this;this._removeClass(this.element.find(".ui-sortable-handle"),"ui-sortable-handle"),t.each(this.items,function(){e._addClass(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item,"ui-sortable-handle")})},_destroy:function(){this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):void 0}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1 -},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?(this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,l=r+t.height,h=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+h>r&&l>s+h,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&l>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var e,i,s="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),n="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),o=s&&n;return o?(e=this._getDragVerticalDirection(),i=this._getDragHorizontalDirection(),this.floating?"right"===i||"down"===e?2:1:e&&("down"===e?2:1)):!1},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&e||"up"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],l=[],h=this._connectWith();if(h&&e)for(s=h.length-1;s>=0;s--)for(o=t(h[s],this.document[0]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&l.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(l.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=l.length-1;s>=0;s--)l[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,l,h,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i],this.document[0]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,h=r.length;h>s;s++)l=t(r[s]),l.data(this.widgetName+"-item",a),c.push({item:l,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.floating=this.items.length?"x"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]);return e._addClass(n,"ui-sortable-placeholder",i||e.currentItem[0].className)._removeClass(n,"ui-sortable-helper"),"tbody"===s?e._createTrPlaceholder(e.currentItem.find("tr").eq(0),t("
    ",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,l,h,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(l=this.items[s].item.offset()[a],h=!1,e[u]-l>this.items[s][r]/2&&(h=!0),n>Math.abs(e[u]-l)&&(n=Math.abs(e[u]-l),o=this.items[s],this.direction=h?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,l=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():l?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():l?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}});var o=/ui-corner-([a-z]){2,6}/g;t.widget("ui.controlgroup",{version:"1.12.0",defaultElement:"
    ",options:{direction:"horizontal",disabled:null,onlyVisible:!0,items:{button:"input[type=button], input[type=submit], input[type=reset], button, a",controlgroupLabel:".ui-controlgroup-label",checkboxradio:"input[type='checkbox'], input[type='radio']",selectmenu:"select",spinner:".ui-spinner-input"}},_create:function(){this._enhance()},_enhance:function(){this.element.attr("role","toolbar"),this.refresh()},_destroy:function(){this._callChildMethod("destroy"),this.childWidgets.removeData("ui-controlgroup-data"),this.element.removeAttr("role"),this.options.items.controlgroupLabel&&this.element.find(this.options.items.controlgroupLabel).find(".ui-controlgroup-label-contents").contents().unwrap()},_initWidgets:function(){var e=this,i=[];t.each(this.options.items,function(s,n){var o,a={};return n?"controlgroupLabel"===s?(o=e.element.find(n),o.each(function(){var e=t(this);e.children(".ui-controlgroup-label-contents").length||e.contents().wrapAll("")}),e._addClass(o,null,"ui-widget ui-widget-content ui-state-default"),i=i.concat(o.get()),void 0):(t.fn[s]&&(e["_"+s+"Options"]&&(a=e["_"+s+"Options"]("middle")),e.element.find(n).each(function(){var n=t(this),o=n[s]("instance"),r=t.widget.extend({},a);if("button"!==s||!n.parent(".ui-spinner").length){o||(o=n[s]()[s]("instance")),o&&(r.classes=e._resolveClassesValues(r.classes,o)),n[s](r);var l=n[s]("widget");t.data(l[0],"ui-controlgroup-data",o?o:n[s]("instance")),i.push(l[0])}})),void 0):void 0}),this.childWidgets=t(t.unique(i)),this._addClass(this.childWidgets,"ui-controlgroup-item")},_callChildMethod:function(e){this.childWidgets.each(function(){var i=t(this),s=i.data("ui-controlgroup-data");s&&s[e]&&s[e]()})},_updateCornerClass:function(t,e){var i="ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all",s=this._buildSimpleOptions(e,"label").classes.label;this._removeClass(t,null,i),this._addClass(t,null,s)},_buildSimpleOptions:function(t,e){var i="vertical"===this.options.direction,s={classes:{}};return s.classes[e]={middle:"",first:"ui-corner-"+(i?"top":"left"),last:"ui-corner-"+(i?"bottom":"right"),only:"ui-corner-all"}[t],s},_spinnerOptions:function(t){var e=this._buildSimpleOptions(t,"ui-spinner");return e.classes["ui-spinner-up"]="",e.classes["ui-spinner-down"]="",e},_buttonOptions:function(t){return this._buildSimpleOptions(t,"ui-button")},_checkboxradioOptions:function(t){return this._buildSimpleOptions(t,"ui-checkboxradio-label")},_selectmenuOptions:function(t){var e="vertical"===this.options.direction;return{width:e?"auto":!1,classes:{middle:{"ui-selectmenu-button-open":"","ui-selectmenu-button-closed":""},first:{"ui-selectmenu-button-open":"ui-corner-"+(e?"top":"tl"),"ui-selectmenu-button-closed":"ui-corner-"+(e?"top":"left")},last:{"ui-selectmenu-button-open":e?"":"ui-corner-tr","ui-selectmenu-button-closed":"ui-corner-"+(e?"bottom":"right")},only:{"ui-selectmenu-button-open":"ui-corner-top","ui-selectmenu-button-closed":"ui-corner-all"}}[t]}},_resolveClassesValues:function(e,i){var s={};return t.each(e,function(t){var n=i.options.classes[t]||"";n=n.replace(o,"").trim(),s[t]=(n+" "+e[t]).replace(/\s+/g," ")}),s},_setOption:function(t,e){return"direction"===t&&this._removeClass("ui-controlgroup-"+this.options.direction),this._super(t,e),"disabled"===t?(this._callChildMethod(e?"disable":"enable"),void 0):(this.refresh(),void 0)},refresh:function(){var e,i=this;this._addClass("ui-controlgroup ui-controlgroup-"+this.options.direction),"horizontal"===this.options.direction&&this._addClass(null,"ui-helper-clearfix"),this._initWidgets(),e=this.childWidgets,this.options.onlyVisible&&(e=e.filter(":visible")),e.length&&(t.each(["first","last"],function(t,s){var n=e[s]().data("ui-controlgroup-data");if(n&&i["_"+n.widgetName+"Options"]){var o=i["_"+n.widgetName+"Options"](1===e.length?"only":s);o.classes=i._resolveClassesValues(o.classes,n),n.element[n.widgetName](o)}else i._updateCornerClass(e[s](),s)}),this._callChildMethod("refresh"))}}),t.widget("ui.checkboxradio",[t.ui.formResetMixin,{version:"1.12.0",options:{disabled:null,label:null,icon:!0,classes:{"ui-checkboxradio-label":"ui-corner-all","ui-checkboxradio-icon":"ui-corner-all"}},_getCreateOptions:function(){var e,i,s=this,n=this._super()||{};return this._readType(),i=this.element.labels(),this.label=t(i[i.length-1]),this.label.length||t.error("No label found for checkboxradio widget"),this.originalLabel="",this.label.contents().not(this.element).each(function(){s.originalLabel+=3===this.nodeType?t(this).text():this.outerHTML}),this.originalLabel&&(n.label=this.originalLabel),e=this.element[0].disabled,null!=e&&(n.disabled=e),n},_create:function(){var t=this.element[0].checked;this._bindFormResetHandler(),null==this.options.disabled&&(this.options.disabled=this.element[0].disabled),this._setOption("disabled",this.options.disabled),this._addClass("ui-checkboxradio","ui-helper-hidden-accessible"),this._addClass(this.label,"ui-checkboxradio-label","ui-button ui-widget"),"radio"===this.type&&this._addClass(this.label,"ui-checkboxradio-radio-label"),this.options.label&&this.options.label!==this.originalLabel?this._updateLabel():this.originalLabel&&(this.options.label=this.originalLabel),this._enhance(),t&&(this._addClass(this.label,"ui-checkboxradio-checked","ui-state-active"),this.icon&&this._addClass(this.icon,null,"ui-state-hover")),this._on({change:"_toggleClasses",focus:function(){this._addClass(this.label,null,"ui-state-focus ui-visual-focus")},blur:function(){this._removeClass(this.label,null,"ui-state-focus ui-visual-focus")}})},_readType:function(){var e=this.element[0].nodeName.toLowerCase();this.type=this.element[0].type,"input"===e&&/radio|checkbox/.test(this.type)||t.error("Can't create checkboxradio on element.nodeName="+e+" and element.type="+this.type)},_enhance:function(){this._updateIcon(this.element[0].checked)},widget:function(){return this.label},_getRadioGroup:function(){var e,i=this.element[0].name,s="input[name='"+t.ui.escapeSelector(i)+"']";return i?(e=this.form.length?t(this.form[0].elements).filter(s):t(s).filter(function(){return 0===t(this).form().length}),e.not(this.element)):t([])},_toggleClasses:function(){var e=this.element[0].checked;this._toggleClass(this.label,"ui-checkboxradio-checked","ui-state-active",e),this.options.icon&&"checkbox"===this.type&&this._toggleClass(this.icon,null,"ui-icon-check ui-state-checked",e)._toggleClass(this.icon,null,"ui-icon-blank",!e),"radio"===this.type&&this._getRadioGroup().each(function(){var e=t(this).checkboxradio("instance");e&&e._removeClass(e.label,"ui-checkboxradio-checked","ui-state-active")})},_destroy:function(){this._unbindFormResetHandler(),this.icon&&(this.icon.remove(),this.iconSpace.remove())},_setOption:function(t,e){return"label"!==t||e?(this._super(t,e),"disabled"===t?(this._toggleClass(this.label,null,"ui-state-disabled",e),this.element[0].disabled=e,void 0):(this.refresh(),void 0)):void 0},_updateIcon:function(e){var i="ui-icon ui-icon-background ";this.options.icon?(this.icon||(this.icon=t(""),this.iconSpace=t(" "),this._addClass(this.iconSpace,"ui-checkboxradio-icon-space")),"checkbox"===this.type?(i+=e?"ui-icon-check ui-state-checked":"ui-icon-blank",this._removeClass(this.icon,null,e?"ui-icon-blank":"ui-icon-check")):i+="ui-icon-blank",this._addClass(this.icon,"ui-checkboxradio-icon",i),e||this._removeClass(this.icon,null,"ui-icon-check ui-state-checked"),this.icon.prependTo(this.label).after(this.iconSpace)):void 0!==this.icon&&(this.icon.remove(),this.iconSpace.remove(),delete this.icon)},_updateLabel:function(){this.label.contents().not(this.element.add(this.icon).add(this.iconSpace)).remove(),this.label.append(this.options.label)},refresh:function(){var t=this.element[0].checked,e=this.element[0].disabled;this._updateIcon(t),this._toggleClass(this.label,"ui-checkboxradio-checked","ui-state-active",t),null!==this.options.label&&this._updateLabel(),e!==this.options.disabled&&this._setOptions({disabled:e})}}]),t.ui.checkboxradio,t.widget("ui.button",{version:"1.12.0",defaultElement:"").button({label:t("").text(this.options.closeText).html(),icon:"ui-icon-closethick",showLabel:!1}).appendTo(this.uiDialogTitlebar),this._addClass(this.uiDialogTitlebarClose,"ui-dialog-titlebar-close"),this._on(this.uiDialogTitlebarClose,{click:function(t){t.preventDefault(),this.close(t)}}),e=t("").uniqueId().prependTo(this.uiDialogTitlebar),this._addClass(e,"ui-dialog-title"),this._title(e),this.uiDialogTitlebar.prependTo(this.uiDialog),this.uiDialog.attr({"aria-labelledby":e.attr("id")})},_title:function(t){this.options.title?t.text(this.options.title):t.html(" ")},_createButtonPane:function(){this.uiDialogButtonPane=t("
    "),this._addClass(this.uiDialogButtonPane,"ui-dialog-buttonpane","ui-widget-content ui-helper-clearfix"),this.uiButtonSet=t("
    ").appendTo(this.uiDialogButtonPane),this._addClass(this.uiButtonSet,"ui-dialog-buttonset"),this._createButtons()},_createButtons:function(){var e=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),t.isEmptyObject(i)||t.isArray(i)&&!i.length?(this._removeClass(this.uiDialog,"ui-dialog-buttons"),void 0):(t.each(i,function(i,s){var n,o;s=t.isFunction(s)?{click:s,text:i}:s,s=t.extend({type:"button"},s),n=s.click,o={icon:s.icon,iconPosition:s.iconPosition,showLabel:s.showLabel},delete s.click,delete s.icon,delete s.iconPosition,delete s.showLabel,t("",s).button(o).appendTo(e.uiButtonSet).on("click",function(){n.apply(e.element[0],arguments)})}),this._addClass(this.uiDialog,"ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function e(t){return{position:t.position,offset:t.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){i._addClass(t(this),"ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,e(n))},drag:function(t,s){i._trigger("drag",t,e(s))},stop:function(n,o){var a=o.offset.left-i.document.scrollLeft(),r=o.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(a>=0?"+":"")+a+" "+"top"+(r>=0?"+":"")+r,of:i.window},i._removeClass(t(this),"ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,e(o))}})},_makeResizable:function(){function e(t){return{originalPosition:t.originalPosition,originalSize:t.originalSize,position:t.position,size:t.size}}var i=this,s=this.options,n=s.resizable,o=this.uiDialog.css("position"),a="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:a,start:function(s,n){i._addClass(t(this),"ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,e(n))},resize:function(t,s){i._trigger("resize",t,e(s))},stop:function(n,o){var a=i.uiDialog.offset(),r=a.left-i.document.scrollLeft(),l=a.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(l>=0?"+":"")+l,of:i.window},i._removeClass(t(this),"ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,e(o))}}).css("position",o)},_trackFocus:function(){this._on(this.widget(),{focusin:function(e){this._makeFocusTarget(),this._focusedElement=t(e.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var e=this._trackingInstances(),i=t.inArray(this,e);-1!==i&&e.splice(i,1)},_trackingInstances:function(){var t=this.document.data("ui-dialog-instances");return t||(t=[],this.document.data("ui-dialog-instances",t)),t},_minHeight:function(){var t=this.options;return"auto"===t.height?t.minHeight:Math.min(t.minHeight,t.height)},_position:function(){var t=this.uiDialog.is(":visible");t||this.uiDialog.show(),this.uiDialog.position(this.options.position),t||this.uiDialog.hide()},_setOptions:function(e){var i=this,s=!1,n={};t.each(e,function(t,e){i._setOption(t,e),t in i.sizeRelatedOptions&&(s=!0),t in i.resizableRelatedOptions&&(n[t]=e)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(e,i){var s,n,o=this.uiDialog;"disabled"!==e&&(this._super(e,i),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:t("").text(""+this.options.closeText).html()}),"draggable"===e&&(s=o.is(":data(ui-draggable)"),s&&!i&&o.draggable("destroy"),!s&&i&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(n=o.is(":data(ui-resizable)"),n&&!i&&o.resizable("destroy"),n&&"string"==typeof i&&o.resizable("option","handles",i),n||i===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var t,e,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),t=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),e=Math.max(0,s.minHeight-t),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-t):"none","auto"===s.height?this.element.css({minHeight:e,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-t)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var e=t(this);return t("
    ").css({position:"absolute",width:e.outerWidth(),height:e.outerHeight()}).appendTo(e.parent()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(e){return t(e.target).closest(".ui-dialog").length?!0:!!t(e.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var e=!0;this._delay(function(){e=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(t){e||this._allowInteraction(t)||(t.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=t("
    ").appendTo(this._appendTo()),this._addClass(this.overlay,null,"ui-widget-overlay ui-front"),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var t=this.document.data("ui-dialog-overlays")-1;t?this.document.data("ui-dialog-overlays",t):(this._off(this.document,"focusin"),this.document.removeData("ui-dialog-overlays")),this.overlay.remove(),this.overlay=null}}}),t.uiBackCompat!==!1&&t.widget("ui.dialog",t.ui.dialog,{options:{dialogClass:""},_createWrapper:function(){this._super(),this.uiDialog.addClass(this.options.dialogClass)},_setOption:function(t,e){"dialogClass"===t&&this.uiDialog.removeClass(this.options.dialogClass).addClass(e),this._superApply(arguments)}}),t.ui.dialog}); \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery.autocomplete.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery.autocomplete.js deleted file mode 100644 index bbacf00c116..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery.autocomplete.js +++ /dev/null @@ -1,831 +0,0 @@ -/** -* Ajax Autocomplete for jQuery, version 1.2.9 -* (c) 2013 Tomas Kirda -* -* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. -* For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete -* -*/ - -/*jslint browser: true, white: true, plusplus: true */ -/*global define, window, document, jQuery */ - -// Expose plugin as an AMD module if AMD loader is present: -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else { - // Browser globals - factory(jQuery); - } -}(function ($) { - 'use strict'; - - var - utils = (function () { - return { - escapeRegExChars: function (value) { - return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - }, - createNode: function (containerClass) { - var div = document.createElement('div'); - div.className = containerClass; - div.style.position = 'absolute'; - div.style.display = 'none'; - div.style.cursor = 'pointer'; - return div; - } - }; - }()), - - keys = { - ESC: 27, - TAB: 9, - RETURN: 13, - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40 - }; - - function Autocomplete(el, options) { - var noop = function () { }, - that = this, - defaults = { - autoSelectFirst: false, - appendTo: 'body', - serviceUrl: null, - lookup: null, - onSelect: null, - width: 'auto', - minChars: 1, - maxHeight: 300, - deferRequestBy: 0, - params: {}, - formatResult: Autocomplete.formatResult, - onPreSelect: noop, - delimiter: null, - zIndex: 9999, - type: 'GET', - noCache: false, - onSearchStart: noop, - onSearchComplete: noop, - onSearchError: noop, - containerClass: 'autocomplete-suggestions', - tabDisabled: false, - dataType: 'text', - currentRequest: null, - triggerSelectOnValidInput: true, - lookupFilter: function (suggestion, originalQuery, queryLowerCase) { - return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1; - }, - paramName: 'query', - transformResult: function (response) { - return typeof response === 'string' ? $.parseJSON(response) : response; - } - }; - - // Shared variables: - that.element = el; - that.el = $(el); - that.suggestions = []; - that.badQueries = []; - that.selectedIndex = -1; - that.currentValue = that.element.value; - that.intervalId = 0; - that.cachedResponse = {}; - that.onChangeInterval = null; - that.onChange = null; - that.isLocal = false; - that.suggestionsContainer = null; - that.options = $.extend({}, defaults, options); - that.classes = { - selected: 'autocomplete-selected', - suggestion: 'autocomplete-suggestion' - }; - that.hint = null; - that.hintValue = ''; - that.selection = null; - - // Initialize and set options: - that.initialize(); - that.setOptions(options); - } - - Autocomplete.utils = utils; - - $.Autocomplete = Autocomplete; - - Autocomplete.formatResult = function (suggestion, currentValue) { - var pattern = '(' + utils.escapeRegExChars(currentValue) + ')'; - - return suggestion.value.replace(new RegExp(pattern, 'gi'), '$1<\/strong>'); - }; - - Autocomplete.prototype = { - - killerFn: null, - - initialize: function () { - var that = this, - suggestionSelector = '.' + that.classes.suggestion, - selected = that.classes.selected, - options = that.options, - container; - - // Remove autocomplete attribute to prevent native suggestions: - that.element.setAttribute('autocomplete', 'off'); - - that.killerFn = function (e) { - if ($(e.target).closest('.' + that.options.containerClass).length === 0) { - that.killSuggestions(); - that.disableKillerFn(); - } - }; - - that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass); - - container = $(that.suggestionsContainer); - - container.appendTo(options.appendTo); - - // Only set width if it was provided: - if (options.width !== 'auto') { - container.width(options.width); - } - - // special on() plugin code for 'autocomplete' - // http://api.jquery.com/on/#on-events-selector-data - // Listen for mouse over event on suggestions list: -// container.on('mouseenter.autocomplete', suggestionSelector, function () { -// that.activate($(this).data('index')); -// }); -// -// // Deselect active element when mouse leaves suggestions container: -// container.on('mouseleave.autocomplete', suggestionSelector, function () { -// that.selectedIndex = -1; -// container.children('.' + selected).removeClass(selected); -// }); - - // Listen for click event on suggestions list: - container.on('click.autocomplete', suggestionSelector, function () { - that.select($(this).data('index')); - }); - - that.fixPosition(); - - that.fixPositionCapture = function () { - if (that.visible) { - that.fixPosition(); - } - }; - - $(window).on('resize.autocomplete', that.fixPositionCapture); - - that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); }); - that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); }); - that.el.on('blur.autocomplete', function () { that.onBlur(); }); - that.el.on('focus.autocomplete', function () { that.onFocus(); }); - that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); }); - }, - - onFocus: function () { - var that = this; - that.fixPosition(); - if (that.options.minChars <= that.el.val().length) { - // that.onValueChange(); - } - }, - - onBlur: function () { - this.enableKillerFn(); - }, - - setOptions: function (suppliedOptions) { - var that = this, - options = that.options; - - $.extend(options, suppliedOptions); - - that.isLocal = $.isArray(options.lookup); - - if (that.isLocal) { - options.lookup = that.verifySuggestionsFormat(options.lookup); - } - - // Adjust height, width and z-index: - $(that.suggestionsContainer).css({ - 'max-height': options.maxHeight + 'px', - 'width': options.width + 'px', - 'z-index': options.zIndex - }); - }, - - clearCache: function () { - this.cachedResponse = {}; - this.badQueries = []; - }, - - clear: function () { - this.clearCache(); - this.currentValue = ''; - this.suggestions = []; - }, - - disable: function () { - var that = this; - that.disabled = true; - if (that.currentRequest) { - that.currentRequest.abort(); - } - }, - - enable: function () { - this.disabled = false; - }, - - fixPosition: function () { - var that = this, - offset, - styles; - - // Don't adjust position if custom container has been specified: - if (that.options.appendTo !== 'body') { - return; - } - - offset = that.el.offset(); - - styles = { - top: (offset.top + that.el.outerHeight()) + 'px', - left: offset.left + 'px' - }; - - if (that.options.width === 'auto') { - styles.width = (that.el.outerWidth() - 2) + 'px'; - } - - $(that.suggestionsContainer).css(styles); - }, - - enableKillerFn: function () { - var that = this; - $(document).on('click.autocomplete', that.killerFn); - }, - - disableKillerFn: function () { - var that = this; - $(document).off('click.autocomplete', that.killerFn); - }, - - killSuggestions: function () { - var that = this; - that.stopKillSuggestions(); - that.intervalId = window.setInterval(function () { - that.hide(); - that.stopKillSuggestions(); - }, 50); - }, - - stopKillSuggestions: function () { - window.clearInterval(this.intervalId); - }, - - isCursorAtEnd: function () { - var that = this, - valLength = that.el.val().length, - selectionStart = that.element.selectionStart, - range; - - if (typeof selectionStart === 'number') { - return selectionStart === valLength; - } - if (document.selection) { - range = document.selection.createRange(); - range.moveStart('character', -valLength); - return valLength === range.text.length; - } - return true; - }, - - onKeyPress: function (e) { - var that = this; - - // If suggestions are hidden and user presses arrow down, display suggestions: - if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) { - that.suggest(); - return; - } - - if (that.disabled || !that.visible) { - return; - } - - switch (e.which) { - case keys.ESC: - that.el.val(that.currentValue); - that.hide(); - break; - case keys.RIGHT: - if (that.hint && that.options.onHint && that.isCursorAtEnd()) { - that.selectHint(); - break; - } - return; - case keys.TAB: - if (that.hint && that.options.onHint) { - that.selectHint(); - return; - } - // Fall through to RETURN - case keys.RETURN: - if (that.selectedIndex === -1) { - that.hide(); - return; - } - that.select(that.selectedIndex); - if (e.which === keys.TAB && that.options.tabDisabled === false) { - return; - } - break; - case keys.UP: - that.moveUp(); - break; - case keys.DOWN: - that.moveDown(); - break; - default: - return; - } - - // Cancel event if function did not return: - e.stopImmediatePropagation(); - e.preventDefault(); - }, - - onKeyUp: function (e) { - var that = this; - - if (that.disabled) { - return; - } - - switch (e.which) { - case keys.UP: - case keys.DOWN: - return; - } - - clearInterval(that.onChangeInterval); - - if (that.currentValue !== that.el.val()) { - that.findBestHint(); - if (that.options.deferRequestBy > 0) { - // Defer lookup in case when value changes very quickly: - that.onChangeInterval = setInterval(function () { - that.onValueChange(); - }, that.options.deferRequestBy); - } else { - that.onValueChange(); - } - } - }, - - onValueChange: function () { - var that = this, - options = that.options, - value = that.el.val(), - query = that.getQuery(value), - index; - - if (that.selection) { - that.selection = null; - (options.onInvalidateSelection || $.noop).call(that.element); - } - - clearInterval(that.onChangeInterval); - that.currentValue = value; - that.selectedIndex = -1; - - // Check existing suggestion for the match before proceeding: - if (options.triggerSelectOnValidInput) { - index = that.findSuggestionIndex(query); - if (index !== -1) { - that.select(index); - return; - } - } - - if (query.length < options.minChars) { - that.hide(); - } else { - that.getSuggestions(query); - } - }, - - findSuggestionIndex: function (query) { - var that = this, - index = -1, - queryLowerCase = query.toLowerCase(); - - $.each(that.suggestions, function (i, suggestion) { - if (suggestion.value.toLowerCase() === queryLowerCase) { - index = i; - return false; - } - }); - - return index; - }, - - getQuery: function (value) { - var delimiter = this.options.delimiter, - parts; - - if (!delimiter) { - return value; - } - parts = value.split(delimiter); - return $.trim(parts[parts.length - 1]); - }, - - getSuggestionsLocal: function (query) { - var that = this, - options = that.options, - queryLowerCase = query.toLowerCase(), - filter = options.lookupFilter, - limit = parseInt(options.lookupLimit, 10), - data; - - data = { - suggestions: $.grep(options.lookup, function (suggestion) { - return filter(suggestion, query, queryLowerCase); - }) - }; - - if (limit && data.suggestions.length > limit) { - data.suggestions = data.suggestions.slice(0, limit); - } - - return data; - }, - - getSuggestions: function (q) { - var response, - that = this, - options = that.options, - serviceUrl = options.serviceUrl, - data, - cacheKey; - - options.params[options.paramName] = q; - data = options.ignoreParams ? null : options.params; - - if (that.isLocal) { - response = that.getSuggestionsLocal(q); - } else { - if ($.isFunction(serviceUrl)) { - serviceUrl = serviceUrl.call(that.element, q); - } - cacheKey = serviceUrl + '?' + $.param(data || {}); - response = that.cachedResponse[cacheKey]; - } - - if (response && $.isArray(response.suggestions)) { - that.suggestions = response.suggestions; - that.suggest(); - } else if (!that.isBadQuery(q)) { - if (options.onSearchStart.call(that.element, options.params) === false) { - return; - } - if (that.currentRequest) { - that.currentRequest.abort(); - } - that.currentRequest = $.ajax({ - url: serviceUrl, - data: data, - type: options.type, - dataType: options.dataType - }).done(function (data) { - that.currentRequest = null; - that.processResponse(data, q, cacheKey); - options.onSearchComplete.call(that.element, q); - }).fail(function (jqXHR, textStatus, errorThrown) { - options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown); - }); - } - }, - - isBadQuery: function (q) { - var badQueries = this.badQueries, - i = badQueries.length; - - while (i--) { - if (q.indexOf(badQueries[i]) === 0) { - return true; - } - } - - return false; - }, - - hide: function () { - var that = this; - that.visible = false; - that.selectedIndex = -1; - $(that.suggestionsContainer).hide(); - that.signalHint(null); - }, - - suggest: function () { - if (this.suggestions.length === 0) { - this.hide(); - return; - } - - var that = this, - options = that.options, - formatResult = options.formatResult, - value = that.getQuery(that.currentValue), - className = that.classes.suggestion, - classSelected = that.classes.selected, - container = $(that.suggestionsContainer), - beforeRender = options.beforeRender, - html = '', - index, - width; - - if (options.triggerSelectOnValidInput) { - index = that.findSuggestionIndex(value); - if (index !== -1) { - that.select(index); - return; - } - } - - // Build suggestions inner HTML: - $.each(that.suggestions, function (i, suggestion) { - html += '
    ' + formatResult(suggestion, value) + '
    '; - }); - - // If width is auto, adjust width before displaying suggestions, - // because if instance was created before input had width, it will be zero. - // Also it adjusts if input width has changed. - // -2px to account for suggestions border. - if (options.width === 'auto') { - width = that.el.outerWidth() - 2; - container.width(width > 0 ? width : 300); - } - - container.html(html); - - // Select first value by default: - if (options.autoSelectFirst) { - that.selectedIndex = 0; - container.children().first().addClass(classSelected); - } - - if ($.isFunction(beforeRender)) { - beforeRender.call(that.element, container); - } - - container.show(); - that.visible = true; - - that.findBestHint(); - }, - - findBestHint: function () { - var that = this, - value = that.el.val().toLowerCase(), - bestMatch = null; - - if (!value) { - return; - } - - $.each(that.suggestions, function (i, suggestion) { - var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0; - if (foundMatch) { - bestMatch = suggestion; - } - return !foundMatch; - }); - - that.signalHint(bestMatch); - }, - - signalHint: function (suggestion) { - var hintValue = '', - that = this; - if (suggestion) { - hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length); - } - if (that.hintValue !== hintValue) { - that.hintValue = hintValue; - that.hint = suggestion; - (this.options.onHint || $.noop)(hintValue); - } - }, - - verifySuggestionsFormat: function (suggestions) { - // If suggestions is string array, convert them to supported format: - if (suggestions.length && typeof suggestions[0] === 'string') { - return $.map(suggestions, function (value) { - return { value: value, data: null }; - }); - } - - return suggestions; - }, - - processResponse: function (response, originalQuery, cacheKey) { - var that = this, - options = that.options, - result = options.transformResult(response, originalQuery); - - result.suggestions = that.verifySuggestionsFormat(result.suggestions); - - // Cache results if cache is not disabled: - if (!options.noCache) { - that.cachedResponse[cacheKey] = result; - if (result.suggestions.length === 0) { - that.badQueries.push(cacheKey); - } - } - - // Return if originalQuery is not matching current query: - if (originalQuery !== that.getQuery(that.currentValue)) { - return; - } - - that.suggestions = result.suggestions; - that.suggest(); - }, - - activate: function (index) { - var that = this, - activeItem, - selected = that.classes.selected, - container = $(that.suggestionsContainer), - children = container.children(); - - if(that.selectedIndex === index) - return null; - - container.children('.' + selected).removeClass(selected); - - that.selectedIndex = index; - - if (that.selectedIndex !== -1 && children.length > that.selectedIndex) { - activeItem = children.get(that.selectedIndex); - $(activeItem).addClass(selected); - that.options.onPreSelect(that.suggestions[index], activeItem); - return activeItem; - } - - return null; - }, - - selectHint: function () { - var that = this, - i = $.inArray(that.hint, that.suggestions); - - that.select(i); - }, - - select: function (i) { - var that = this; - that.hide(); - that.onSelect(i); - }, - - moveUp: function () { - var that = this; - - if (that.selectedIndex === -1) { - return; - } - - if (that.selectedIndex === 0) { - $(that.suggestionsContainer).children().first().removeClass(that.classes.selected); - that.selectedIndex = -1; - that.el.val(that.currentValue); - that.findBestHint(); - return; - } - - that.adjustScroll(that.selectedIndex - 1); - }, - - moveDown: function () { - var that = this; - - if (that.selectedIndex === (that.suggestions.length - 1)) { - return; - } - - that.adjustScroll(that.selectedIndex + 1); - }, - - adjustScroll: function (index) { - var that = this, - activeItem = that.activate(index), - offsetTop, - upperBound, - lowerBound, - heightDelta = 25; - - if (!activeItem) { - return; - } - - offsetTop = activeItem.offsetTop; - upperBound = $(that.suggestionsContainer).scrollTop(); - lowerBound = upperBound + that.options.maxHeight - heightDelta; - - if (offsetTop < upperBound) { - $(that.suggestionsContainer).scrollTop(offsetTop); - } else if (offsetTop > lowerBound) { - $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta); - } - - that.el.val(that.getValue(that.suggestions[index].value)); - that.signalHint(null); - }, - - onSelect: function (index) { - var that = this, - onSelectCallback = that.options.onSelect, - suggestion = that.suggestions[index]; - - that.currentValue = that.getValue(suggestion.value); - that.el.val(that.currentValue); - that.signalHint(null); - that.suggestions = []; - that.selection = suggestion; - - if ($.isFunction(onSelectCallback)) { - onSelectCallback.call(that.element, suggestion); - } - }, - - getValue: function (value) { - var that = this, - delimiter = that.options.delimiter, - currentValue, - parts; - - if (!delimiter) { - return value; - } - - currentValue = that.currentValue; - parts = currentValue.split(delimiter); - - if (parts.length === 1) { - return value; - } - - return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value; - }, - - dispose: function () { - var that = this; - that.el.off('.autocomplete').removeData('autocomplete'); - that.disableKillerFn(); - $(window).off('resize.autocomplete', that.fixPositionCapture); - $(that.suggestionsContainer).remove(); - } - }; - - // Create chainable jQuery plugin: - $.fn.autocomplete = function (options, args) { - var dataKey = 'autocomplete'; - // If function invoked without argument return - // instance of the first matched element: - if (arguments.length === 0) { - return this.first().data(dataKey); - } - - return this.each(function () { - var inputElement = $(this), - instance = inputElement.data(dataKey); - - if (typeof options === 'string') { - if (instance && typeof instance[options] === 'function') { - instance[options](args); - } - } else { - // If instance already exists, destroy it: - if (instance && instance.dispose) { - instance.dispose(); - } - instance = new Autocomplete(this, options); - inputElement.data(dataKey, instance); - } - }); - }; -})); diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery.history.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery.history.js deleted file mode 100644 index caeb7aa4b53..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/jquery.history.js +++ /dev/null @@ -1 +0,0 @@ -(function(e,t){"use strict";var n=e.History=e.History||{},r=e.jQuery;if(typeof n.Adapter!="undefined")throw new Error("History.js Adapter has already been loaded...");n.Adapter={bind:function(e,t,n){r(e).bind(t,n)},trigger:function(e,t,n){r(e).trigger(t,n)},extractEventData:function(e,n,r){var i=n&&n.originalEvent&&n.originalEvent[e]||r&&r[e]||t;return i},onDomLoad:function(e){r(e)}},typeof n.init!="undefined"&&n.init()})(window),function(e,t){"use strict";var n=e.console||t,r=e.document,i=e.navigator,s=e.sessionStorage||!1,o=e.setTimeout,u=e.clearTimeout,a=e.setInterval,f=e.clearInterval,l=e.JSON,c=e.alert,h=e.History=e.History||{},p=e.history;try{s.setItem("TEST","1"),s.removeItem("TEST")}catch(d){s=!1}l.stringify=l.stringify||l.encode,l.parse=l.parse||l.decode;if(typeof h.init!="undefined")throw new Error("History.js Core has already been loaded...");h.init=function(e){return typeof h.Adapter=="undefined"?!1:(typeof h.initCore!="undefined"&&h.initCore(),typeof h.initHtml4!="undefined"&&h.initHtml4(),!0)},h.initCore=function(d){if(typeof h.initCore.initialized!="undefined")return!1;h.initCore.initialized=!0,h.options=h.options||{},h.options.hashChangeInterval=h.options.hashChangeInterval||100,h.options.safariPollInterval=h.options.safariPollInterval||500,h.options.doubleCheckInterval=h.options.doubleCheckInterval||500,h.options.disableSuid=h.options.disableSuid||!1,h.options.storeInterval=h.options.storeInterval||1e3,h.options.busyDelay=h.options.busyDelay||250,h.options.debug=h.options.debug||!1,h.options.initialTitle=h.options.initialTitle||r.title,h.options.html4Mode=h.options.html4Mode||!1,h.options.delayInit=h.options.delayInit||!1,h.intervalList=[],h.clearAllIntervals=function(){var e,t=h.intervalList;if(typeof t!="undefined"&&t!==null){for(e=0;e")&&n[0]);return e>4?e:!1}();return e},h.isInternetExplorer=function(){var e=h.isInternetExplorer.cached=typeof h.isInternetExplorer.cached!="undefined"?h.isInternetExplorer.cached:Boolean(h.getInternetExplorerMajorVersion());return e},h.options.html4Mode?h.emulated={pushState:!0,hashChange:!0}:h.emulated={pushState:!Boolean(e.history&&e.history.pushState&&e.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(i.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(i.userAgent)),hashChange:Boolean(!("onhashchange"in e||"onhashchange"in r)||h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<8)},h.enabled=!h.emulated.pushState,h.bugs={setHash:Boolean(!h.emulated.pushState&&i.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(i.userAgent)),safariPoll:Boolean(!h.emulated.pushState&&i.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(i.userAgent)),ieDoubleCheck:Boolean(h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<7)},h.isEmptyObject=function(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0},h.cloneObject=function(e){var t,n;return e?(t=l.stringify(e),n=l.parse(t)):n={},n},h.getRootUrl=function(){var e=r.location.protocol+"//"+(r.location.hostname||r.location.host);if(r.location.port||!1)e+=":"+r.location.port;return e+="/",e},h.getBaseHref=function(){var e=r.getElementsByTagName("base"),t=null,n="";return e.length===1&&(t=e[0],n=t.href.replace(/[^\/]+$/,"")),n=n.replace(/\/+$/,""),n&&(n+="/"),n},h.getBaseUrl=function(){var e=h.getBaseHref()||h.getBasePageUrl()||h.getRootUrl();return e},h.getPageUrl=function(){var e=h.getState(!1,!1),t=(e||{}).url||h.getLocationHref(),n;return n=t.replace(/\/+$/,"").replace(/[^\/]+$/,function(e,t,n){return/\./.test(e)?e:e+"/"}),n},h.getBasePageUrl=function(){var e=h.getLocationHref().replace(/[#\?].*/,"").replace(/[^\/]+$/,function(e,t,n){return/[^\/]$/.test(e)?"":e}).replace(/\/+$/,"")+"/";return e},h.getFullUrl=function(e,t){var n=e,r=e.substring(0,1);return t=typeof t=="undefined"?!0:t,/[a-z]+\:\/\//.test(e)||(r==="/"?n=h.getRootUrl()+e.replace(/^\/+/,""):r==="#"?n=h.getPageUrl().replace(/#.*/,"")+e:r==="?"?n=h.getPageUrl().replace(/[\?#].*/,"")+e:t?n=h.getBaseUrl()+e.replace(/^(\.\/)+/,""):n=h.getBasePageUrl()+e.replace(/^(\.\/)+/,"")),n.replace(/\#$/,"")},h.getShortUrl=function(e){var t=e,n=h.getBaseUrl(),r=h.getRootUrl();return h.emulated.pushState&&(t=t.replace(n,"")),t=t.replace(r,"/"),h.isTraditionalAnchor(t)&&(t="./"+t),t=t.replace(/^(\.\/)+/g,"./").replace(/\#$/,""),t},h.getLocationHref=function(e){return e=e||r,e.URL===e.location.href?e.location.href:e.location.href===decodeURIComponent(e.URL)?e.URL:e.location.hash&&decodeURIComponent(e.location.href.replace(/^[^#]+/,""))===e.location.hash?e.location.href:e.URL.indexOf("#")==-1&&e.location.href.indexOf("#")!=-1?e.location.href:e.URL||e.location.href},h.store={},h.idToState=h.idToState||{},h.stateToId=h.stateToId||{},h.urlToId=h.urlToId||{},h.storedStates=h.storedStates||[],h.savedStates=h.savedStates||[],h.normalizeStore=function(){h.store.idToState=h.store.idToState||{},h.store.urlToId=h.store.urlToId||{},h.store.stateToId=h.store.stateToId||{}},h.getState=function(e,t){typeof e=="undefined"&&(e=!0),typeof t=="undefined"&&(t=!0);var n=h.getLastSavedState();return!n&&t&&(n=h.createStateObject()),e&&(n=h.cloneObject(n),n.url=n.cleanUrl||n.url),n},h.getIdByState=function(e){var t=h.extractId(e.url),n;if(!t){n=h.getStateString(e);if(typeof h.stateToId[n]!="undefined")t=h.stateToId[n];else if(typeof h.store.stateToId[n]!="undefined")t=h.store.stateToId[n];else{for(;;){t=(new Date).getTime()+String(Math.random()).replace(/\D/g,"");if(typeof h.idToState[t]=="undefined"&&typeof h.store.idToState[t]=="undefined")break}h.stateToId[n]=t,h.idToState[t]=e}}return t},h.normalizeState=function(e){var t,n;if(!e||typeof e!="object")e={};if(typeof e.normalized!="undefined")return e;if(!e.data||typeof e.data!="object")e.data={};return t={},t.normalized=!0,t.title=e.title||"",t.url=h.getFullUrl(e.url?e.url:h.getLocationHref()),t.hash=h.getShortUrl(t.url),t.data=h.cloneObject(e.data),t.id=h.getIdByState(t),t.cleanUrl=t.url.replace(/\??\&_suid.*/,""),t.url=t.cleanUrl,n=!h.isEmptyObject(t.data),(t.title||n)&&h.options.disableSuid!==!0&&(t.hash=h.getShortUrl(t.url).replace(/\??\&_suid.*/,""),/\?/.test(t.hash)||(t.hash+="?"),t.hash+="&_suid="+t.id),t.hashedUrl=h.getFullUrl(t.hash),(h.emulated.pushState||h.bugs.safariPoll)&&h.hasUrlDuplicate(t)&&(t.url=t.hashedUrl),t},h.createStateObject=function(e,t,n){var r={data:e,title:t,url:n};return r=h.normalizeState(r),r},h.getStateById=function(e){e=String(e);var n=h.idToState[e]||h.store.idToState[e]||t;return n},h.getStateString=function(e){var t,n,r;return t=h.normalizeState(e),n={data:t.data,title:e.title,url:e.url},r=l.stringify(n),r},h.getStateId=function(e){var t,n;return t=h.normalizeState(e),n=t.id,n},h.getHashByState=function(e){var t,n;return t=h.normalizeState(e),n=t.hash,n},h.extractId=function(e){var t,n,r,i;return e.indexOf("#")!=-1?i=e.split("#")[0]:i=e,n=/(.*)\&_suid=([0-9]+)$/.exec(i),r=n?n[1]||e:e,t=n?String(n[2]||""):"",t||!1},h.isTraditionalAnchor=function(e){var t=!/[\/\?\.]/.test(e);return t},h.extractState=function(e,t){var n=null,r,i;return t=t||!1,r=h.extractId(e),r&&(n=h.getStateById(r)),n||(i=h.getFullUrl(e),r=h.getIdByUrl(i)||!1,r&&(n=h.getStateById(r)),!n&&t&&!h.isTraditionalAnchor(e)&&(n=h.createStateObject(null,null,i))),n},h.getIdByUrl=function(e){var n=h.urlToId[e]||h.store.urlToId[e]||t;return n},h.getLastSavedState=function(){return h.savedStates[h.savedStates.length-1]||t},h.getLastStoredState=function(){return h.storedStates[h.storedStates.length-1]||t},h.hasUrlDuplicate=function(e){var t=!1,n;return n=h.extractState(e.url),t=n&&n.id!==e.id,t},h.storeState=function(e){return h.urlToId[e.url]=e.id,h.storedStates.push(h.cloneObject(e)),e},h.isLastSavedState=function(e){var t=!1,n,r,i;return h.savedStates.length&&(n=e.id,r=h.getLastSavedState(),i=r.id,t=n===i),t},h.saveState=function(e){return h.isLastSavedState(e)?!1:(h.savedStates.push(h.cloneObject(e)),!0)},h.getStateByIndex=function(e){var t=null;return typeof e=="undefined"?t=h.savedStates[h.savedStates.length-1]:e<0?t=h.savedStates[h.savedStates.length+e]:t=h.savedStates[e],t},h.getCurrentIndex=function(){var e=null;return h.savedStates.length<1?e=0:e=h.savedStates.length-1,e},h.getHash=function(e){var t=h.getLocationHref(e),n;return n=h.getHashByUrl(t),n},h.unescapeHash=function(e){var t=h.normalizeHash(e);return t=decodeURIComponent(t),t},h.normalizeHash=function(e){var t=e.replace(/[^#]*#/,"").replace(/#.*/,"");return t},h.setHash=function(e,t){var n,i;return t!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.setHash,args:arguments,queue:t}),!1):(h.busy(!0),n=h.extractState(e,!0),n&&!h.emulated.pushState?h.pushState(n.data,n.title,n.url,!1):h.getHash()!==e&&(h.bugs.setHash?(i=h.getPageUrl(),h.pushState(null,null,i+"#"+e,!1)):r.location.hash=e),h)},h.escapeHash=function(t){var n=h.normalizeHash(t);return n=e.encodeURIComponent(n),h.bugs.hashEscape||(n=n.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),n},h.getHashByUrl=function(e){var t=String(e).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return t=h.unescapeHash(t),t},h.setTitle=function(e){var t=e.title,n;t||(n=h.getStateByIndex(0),n&&n.url===e.url&&(t=n.title||h.options.initialTitle));try{r.getElementsByTagName("title")[0].innerHTML=t.replace("<","<").replace(">",">").replace(" & "," & ")}catch(i){}return r.title=t,h},h.queues=[],h.busy=function(e){typeof e!="undefined"?h.busy.flag=e:typeof h.busy.flag=="undefined"&&(h.busy.flag=!1);if(!h.busy.flag){u(h.busy.timeout);var t=function(){var e,n,r;if(h.busy.flag)return;for(e=h.queues.length-1;e>=0;--e){n=h.queues[e];if(n.length===0)continue;r=n.shift(),h.fireQueueItem(r),h.busy.timeout=o(t,h.options.busyDelay)}};h.busy.timeout=o(t,h.options.busyDelay)}return h.busy.flag},h.busy.flag=!1,h.fireQueueItem=function(e){return e.callback.apply(e.scope||h,e.args||[])},h.pushQueue=function(e){return h.queues[e.queue||0]=h.queues[e.queue||0]||[],h.queues[e.queue||0].push(e),h},h.queue=function(e,t){return typeof e=="function"&&(e={callback:e}),typeof t!="undefined"&&(e.queue=t),h.busy()?h.pushQueue(e):h.fireQueueItem(e),h},h.clearQueue=function(){return h.busy.flag=!1,h.queues=[],h},h.stateChanged=!1,h.doubleChecker=!1,h.doubleCheckComplete=function(){return h.stateChanged=!0,h.doubleCheckClear(),h},h.doubleCheckClear=function(){return h.doubleChecker&&(u(h.doubleChecker),h.doubleChecker=!1),h},h.doubleCheck=function(e){return h.stateChanged=!1,h.doubleCheckClear(),h.bugs.ieDoubleCheck&&(h.doubleChecker=o(function(){return h.doubleCheckClear(),h.stateChanged||e(),!0},h.options.doubleCheckInterval)),h},h.safariStatePoll=function(){var t=h.extractState(h.getLocationHref()),n;if(!h.isLastSavedState(t))return n=t,n||(n=h.createStateObject()),h.Adapter.trigger(e,"popstate"),h;return},h.back=function(e){return e!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.back,args:arguments,queue:e}),!1):(h.busy(!0),h.doubleCheck(function(){h.back(!1)}),p.go(-1),!0)},h.forward=function(e){return e!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.forward,args:arguments,queue:e}),!1):(h.busy(!0),h.doubleCheck(function(){h.forward(!1)}),p.go(1),!0)},h.go=function(e,t){var n;if(e>0)for(n=1;n<=e;++n)h.forward(t);else{if(!(e<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(n=-1;n>=e;--n)h.back(t)}return h};if(h.emulated.pushState){var v=function(){};h.pushState=h.pushState||v,h.replaceState=h.replaceState||v}else h.onPopState=function(t,n){var r=!1,i=!1,s,o;return h.doubleCheckComplete(),s=h.getHash(),s?(o=h.extractState(s||h.getLocationHref(),!0),o?h.replaceState(o.data,o.title,o.url,!1):(h.Adapter.trigger(e,"anchorchange"),h.busy(!1)),h.expectedStateId=!1,!1):(r=h.Adapter.extractEventData("state",t,n)||!1,r?i=h.getStateById(r):h.expectedStateId?i=h.getStateById(h.expectedStateId):i=h.extractState(h.getLocationHref()),i||(i=h.createStateObject(null,null,h.getLocationHref())),h.expectedStateId=!1,h.isLastSavedState(i)?(h.busy(!1),!1):(h.storeState(i),h.saveState(i),h.setTitle(i),h.Adapter.trigger(e,"statechange"),h.busy(!1),!0))},h.Adapter.bind(e,"popstate",h.onPopState),h.pushState=function(t,n,r,i){if(h.getHashByUrl(r)&&h.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(i!==!1&&h.busy())return h.pushQueue({scope:h,callback:h.pushState,args:arguments,queue:i}),!1;h.busy(!0);var s=h.createStateObject(t,n,r);return h.isLastSavedState(s)?h.busy(!1):(h.storeState(s),h.expectedStateId=s.id,p.pushState(s.id,s.title,s.url),h.Adapter.trigger(e,"popstate")),!0},h.replaceState=function(t,n,r,i){if(h.getHashByUrl(r)&&h.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(i!==!1&&h.busy())return h.pushQueue({scope:h,callback:h.replaceState,args:arguments,queue:i}),!1;h.busy(!0);var s=h.createStateObject(t,n,r);return h.isLastSavedState(s)?h.busy(!1):(h.storeState(s),h.expectedStateId=s.id,p.replaceState(s.id,s.title,s.url),h.Adapter.trigger(e,"popstate")),!0};if(s){try{h.store=l.parse(s.getItem("History.store"))||{}}catch(m){h.store={}}h.normalizeStore()}else h.store={},h.normalizeStore();h.Adapter.bind(e,"unload",h.clearAllIntervals),h.saveState(h.storeState(h.extractState(h.getLocationHref(),!0))),s&&(h.onUnload=function(){var e,t,n;try{e=l.parse(s.getItem("History.store"))||{}}catch(r){e={}}e.idToState=e.idToState||{},e.urlToId=e.urlToId||{},e.stateToId=e.stateToId||{};for(t in h.idToState){if(!h.idToState.hasOwnProperty(t))continue;e.idToState[t]=h.idToState[t]}for(t in h.urlToId){if(!h.urlToId.hasOwnProperty(t))continue;e.urlToId[t]=h.urlToId[t]}for(t in h.stateToId){if(!h.stateToId.hasOwnProperty(t))continue;e.stateToId[t]=h.stateToId[t]}h.store=e,h.normalizeStore(),n=l.stringify(e);try{s.setItem("History.store",n)}catch(i){if(i.code!==DOMException.QUOTA_EXCEEDED_ERR)throw i;s.length&&(s.removeItem("History.store"),s.setItem("History.store",n))}},h.intervalList.push(a(h.onUnload,h.options.storeInterval)),h.Adapter.bind(e,"beforeunload",h.onUnload),h.Adapter.bind(e,"unload",h.onUnload));if(!h.emulated.pushState){h.bugs.safariPoll&&h.intervalList.push(a(h.safariStatePoll,h.options.safariPollInterval));if(i.vendor==="Apple Computer, Inc."||(i.appCodeName||"")==="Mozilla")h.Adapter.bind(e,"hashchange",function(){h.Adapter.trigger(e,"popstate")}),h.getHash()&&h.Adapter.onDomLoad(function(){h.Adapter.trigger(e,"hashchange")})}},(!h.options||!h.options.delayInit)&&h.init()}(window) \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/leaflet_numbered_markers.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/leaflet_numbered_markers.js deleted file mode 100644 index 2ab1983b9a9..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/lib/leaflet_numbered_markers.js +++ /dev/null @@ -1,30 +0,0 @@ -L.NumberedDivIcon = L.Icon.extend({ - options: { - iconUrl: './img/marker_hole.png', - number: '', - shadowUrl: null, - iconSize: new L.Point(25, 41), - iconAnchor: new L.Point(12, 40), - popupAnchor: new L.Point(0, -33), - shadowSize: new L.Point(50, -64), - shadowAnchor: new L.Point(4, -62), - className: 'leaflet-div-icon' - }, - - createIcon: function () { - var div = document.createElement('div'); - var img = this._createImg(this.options['iconUrl']); - var numdiv = document.createElement('div'); - numdiv.setAttribute ( "class", "number" ); - numdiv.innerHTML = this.options['number'] || ''; - div.appendChild ( img ); - div.appendChild ( numdiv ); - this._setIconStyles(div, 'icon'); - return div; - }, - - //you could change this to add a shadow like in the normal marker if you really wanted - createShadow: function () { - return null; - } -}); \ No newline at end of file diff --git a/web-bundle/src/main/resources/com/graphhopper/maps/js/main-template.js b/web-bundle/src/main/resources/com/graphhopper/maps/js/main-template.js deleted file mode 100644 index 6622bb9fad9..00000000000 --- a/web-bundle/src/main/resources/com/graphhopper/maps/js/main-template.js +++ /dev/null @@ -1,999 +0,0 @@ -var Flatpickr = require('flatpickr'); -require('flatpickr/dist/l10n'); - -var L = require('leaflet'); -require('leaflet-contextmenu'); -require('leaflet-loading'); -require('leaflet.heightgraph'); -var moment = require('moment'); -require('./lib/leaflet_numbered_markers.js'); - -global.jQuery = require('jquery'); -global.$ = global.jQuery; -require('./lib/jquery-ui-custom-1.12.0.min.js'); -require('./lib/jquery.history.js'); -require('./lib/jquery.autocomplete.js'); - -var ghenv = require("./config/options.js").options; -console.log(ghenv.environment); - -var GHInput = require('./graphhopper/GHInput.js'); -var GHRequest = require('./graphhopper/GHRequest.js'); -var host = ghenv.routing.host; -if (!host) { - if (location.port === '') { - host = location.protocol + '//' + location.hostname; - } else { - host = location.protocol + '//' + location.hostname + ":" + location.port; - } -} - -var AutoComplete = require('./autocomplete.js'); -if (ghenv.environment === 'development') { - var autocomplete = AutoComplete.prototype.createStub(); -} else { - var autocomplete = new AutoComplete(ghenv.geocoding.host, ghenv.geocoding.api_key); -} - -var mapLayer = require('./map.js'); -var nominatim = require('./nominatim.js'); -var routeManipulation = require('./routeManipulation.js'); -var gpxExport = require('./gpxexport.js'); -var messages = require('./messages.js'); -var translate = require('./translate.js'); -var customModelEditor = require('custom-model-editor/dist/index.js'); - -var format = require('./tools/format.js'); -var urlTools = require('./tools/url.js'); -var tileLayers = require('./config/tileLayers.js'); -if(ghenv.with_tiles) - tileLayers.enableVectorTiles(); - -var debug = false; -var ghRequest = new GHRequest(host, ghenv.routing.api_key); -var bounds = {}; -var metaVersionInfo; - -// usage: log('inside coolFunc',this,arguments); -// http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ -if (global.window) { - window.log = function () { - log.history = log.history || []; // store logs to an array for reference - log.history.push(arguments); - if (this.console && debug) { - console.log(Array.prototype.slice.call(arguments)); - } - }; -} - -$(document).ready(function (e) { - // fixing cross domain support e.g in Opera - jQuery.support.cors = true; - - gpxExport.addGpxExport(ghRequest); - // we start without encoded values, they will be loaded later - var cmEditor = customModelEditor.create({}, function (element) { - $("#custom-model-editor").append(element); - }); - - cmEditor.validListener = function(valid) { - $("#custom-model-search-button").prop('disabled', !valid); - }; - ghRequest.cmEditor = cmEditor; - ghRequest.cmEditorActive = false; - var toggleCustomModelBox = function(sendRoute) { - $("#custom-model-box").toggle(); - ghRequest.cmEditorActive = !ghRequest.cmEditorActive; - // avoid default action, so use a different search button - $("#searchButton").toggle(); - mapLayer.adjustMapSize(); - cmEditor.cm.refresh(); - cmEditor.cm.focus(); - cmEditor.cm.setCursor(cmEditor.cm.lineCount()); - if (sendRoute) - sendCustomData(); - }; - $("#custom-model-button").click(function() { - toggleCustomModelBox(true); - }); - function showCustomModelExample() { - cmEditor.value = - "{" - + "\n \"speed\": [" - + "\n {" - + "\n \"if\": \"road_class == MOTORWAY\"," - + "\n \"multiply_by\": 0.8" - + "\n }" - + "\n ]," - + "\n \"priority\": [" - + "\n {" - + "\n \"if\": \"road_environment == TUNNEL\"," - + "\n \"multiply_by\": 0.5" - + "\n }," - + "\n {" - + "\n \"if\": \"max_weight < 3\"," - + "\n \"multiply_by\": 0.0" - + "\n }" - + "\n ]" - + "\n}"; - cmEditor.cm.focus(); - cmEditor.cm.setCursor(0); - cmEditor.cm.execCommand('selectAll'); - cmEditor.cm.refresh(); - } - $("#custom-model-example").click(function () { - showCustomModelExample(); - return false; - }); - - $("#export-link").click(function (e) { - try { - e.preventDefault(); - var url = location.href; - if(url.indexOf("?") > 0) - url = url.substring(0, url.indexOf("?")) + ghRequest.createHistoryURL() + "&layer=" + encodeURIComponent(tileLayers.activeLayerName); - if(ghRequest.cmEditorActive) { - var text = cmEditor.value.replaceAll("&","%26"); - url += "&custom_model=" + new URLSearchParams(JSON.stringify(JSON.parse(text))).toString(); - } - navigator.clipboard.writeText(url).then(() => { alert('Link copied to clipboard'); }); - } catch(e) { console.warn(e); } - }); - - var sendCustomData = function () { - ghRequest.ignoreCustomErrors = false; - mySubmit(); - }; - - var sendCustomDataIgnoreErrors = function () { - ghRequest.ignoreCustomErrors = true; - mySubmit(); - } - - cmEditor.setExtraKey('Ctrl-Enter', sendCustomDataIgnoreErrors); - $("#custom-model-search-button").click(sendCustomData); - - if (isProduction()) - $('#hosting').show(); - - var History = window.History; - if (History.enabled) { - History.Adapter.bind(window, 'statechange', function () { - // No need for workaround? - // Chrome and Safari always emit a popstate event on page load, but Firefox doesn’t - // https://github.com/defunkt/jquery-pjax/issues/143#issuecomment-6194330 - - var state = History.getState(); - initFromParams(state.data, true); - }); - } - - $('#locationform').submit(function (e) { - // no page reload - e.preventDefault(); - mySubmit(); - }); - - var urlParams = urlTools.parseUrlWithHisto(); - - var customModelJSON = urlParams.custom_model; - if(customModelJSON) { - toggleCustomModelBox(false); - cmEditor.value = customModelJSON; // if json parsing fails we still have the partial custom model in the box - try { - var tmpObj = JSON.parse(customModelJSON); - cmEditor.value = JSON.stringify(tmpObj, null, 2); - } catch(e) { - console.warn('cannot pretty print custom model: ' + e); - } - - } else { - // todo: the idea was to highlight everything so if we start typing the example is overwritten. But unfortunately - // this does not work. And not even sure this is so useful? - showCustomModelExample(); - } - - $.when(ghRequest.fetchTranslationMap(urlParams.locale), ghRequest.getInfo()) - .then(function (arg1, arg2) { - // init translation retrieved from first call (fetchTranslationMap) - var translations = arg1[0]; - autocomplete.setLocale(translations.locale); - ghRequest.setLocale(translations.locale); - translate.init(translations); - - // init bounding box from getInfo result - var json = arg2[0]; - var tmp = json.bbox; - bounds.initialized = true; - bounds.minLon = tmp[0]; - bounds.minLat = tmp[1]; - bounds.maxLon = tmp[2]; - bounds.maxLat = tmp[3]; - nominatim.setBounds(bounds); - var profilesDiv = $("#profiles"); - - function createButton(profile, hide) { - var vehicle = profile.vehicle; - var profileName = profile.name; - var button = $("